Pythonスクリプトが実行されているかどうかを確認します


100

Webアプリの一部としてPythonデーモンを実行しています/デーモンが実行されているかどうかを(Pythonを使用して)すばやく確認し、実行されていない場合は起動するにはどうすればよいですか?

デーモンのクラッシュを修正するためにそのようにしたいので、スクリプトを手動で実行する必要はありません。呼び出されるとすぐに自動的に実行され、実行を続けます。

スクリプトが実行されているかどうかを(Pythonを使用して)確認するにはどうすればよいですか?


あなたはあなたのプロセスもPythonで書かれた他のプロセスを維持し続けていませんか?
ojblass 2009

Tendoを試して、スクリプトのシングルトンインスタンスを作成します。そのため、スクリプトがすでに実行されている場合、スクリプトは実行されません。github.com/pycontribs/tendo
JasTonAChair

これはデーモンの仕事ではなく、デーモンを起動する「上位」アプリケーションの仕事です。systemdまたは監視のような他のツールを使用します。ファイルに書き込まれたPIDに依存しないでください。systemd / supervisordを使用できない場合は、ロックを使用して、2回実行されないことを確認してください。
guettli

回答:


92

pidfileをどこかにドロップします(例:/ tmp)。次に、ファイル内のPIDが存在するかどうかを確認することで、プロセスが実行されているかどうかを確認できます。完全にシャットダウンしたらファイルを削除し、起動時に確認してください。

#/usr/bin/env python

import os
import sys

pid = str(os.getpid())
pidfile = "/tmp/mydaemon.pid"

if os.path.isfile(pidfile):
    print "%s already exists, exiting" % pidfile
    sys.exit()
file(pidfile, 'w').write(pid)
try:
    # Do some actual work here
finally:
    os.unlink(pidfile)

次に、/ tmp / mydaemon.pidの内容が既存のプロセスであるかどうかを確認することで、プロセスが実行されているかどうかを確認できます。Monit(上記)がこれを行うことができます。または、psからの戻りコードを使用してチェックする簡単なシェルスクリプトを書くこともできます。

ps up `cat /tmp/mydaemon.pid ` >/dev/null && echo "Running" || echo "Not running"

追加のクレジットとして、atexitモジュールを使用して、プログラムが任意の状況(強制終了、例外の発生など)でpidfileを確実にクリーンアップできるようにすることができます。


6
プログラムが壊れた場合、ファイルが存在するため、os.unlink()は実行されず、プログラムは再度実行されません。正しい ?
Yuda Prawira、

2
正解ですが、これは予想される動作です。pidfileは存在するが、内部のPIDが実行されていない場合は、正常にシャットダウンされていないことを示しています。これは、アプリがクラッシュしたことを意味します。これにより、問題があることがわかり、ログを確認できます。前述のように、バグがPythonインタープリター自体にない場合は、atexitモジュールもこれを処理できます。
Dan Udey、2011年

7
簡単な解決策ですが、これは競合状態の影響を受けやすくなっています。スクリプトの2つのインスタンスがほぼ同時に実行されるとif os.path.isfile(pidfile)、両方がfalseと評価され、両方がロックファイルを書き込んで実行を継続する可能性があります。
セリン2013年

6
pidもオペレーティングシステムによって再利用されます。したがって、誤検知が発生する可能性があります。
被告人2013年

12
今これを見つけた人のために、Python 3 ではfile()削除され、open()代わりに使用する必要があることに注意してください。さらに、2.7を使用open()file()ている場合でも、docs.python.org / 2 / library / functions.html#fileで説明されているように使用する必要があります(そうです、Pythonを2.2で使用していた場合、公式のアドバイスは反対でした。どうやら彼らは気が変わった。)
jpk

154

Linuxシステムで便利な手法は、ドメインソケットを使用しています。

import socket
import sys
import time

def get_lock(process_name):
    # Without holding a reference to our socket somewhere it gets garbage
    # collected when the function exits
    get_lock._lock_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)

    try:
        # The null byte (\0) means the the socket is created 
        # in the abstract namespace instead of being created 
        # on the file system itself.
        # Works only in Linux
        get_lock._lock_socket.bind('\0' + process_name)
        print 'I got the lock'
    except socket.error:
        print 'lock exists'
        sys.exit()


get_lock('running_test')
while True:
    time.sleep(3)

これはアトミックであり、プロセスにSIGKILLが送信された場合にロックファイルが存在するという問題を回避します

あなたはできるのドキュメントを読み込むsocket.closeガベージコレクション時にソケットが自動的に閉じていること。


20
将来のGoogle社員への注意:このコードは、Linux固有の「抽象ソケット」を使用します(一般的にposixではありません)。もっとこのことについて:blog.eduardofleury.com/archives/2007/09/13
ゲオルク・

6
これは素晴らしく、愚かな残留ファイルを残しません。これをもっと賛成できるといいのですが。
Hiro2k 2013年

4
驚くばかり。しかし、なぜlock_socketはグローバルに定義されているのでしょうか。私がテストしたところ、lock_socketがグローバルに定義されていない場合、複数のプロセスを実行しているときにロックシステムが機能しません。どうして?lock_socketが定義され、get_lock関数でのみ使用されます。なぜグローバルに定義する必要があるのですか?
Alptugay 2014年

7
私がこれを書いてから久しぶりです...そして、私の記憶はかすんでいます。しかし、それはガベージコレクションが行われ、それ以外の場合はソケットが閉じられたためだと思います。そんな感じ。
被告人2014年

8
nullバイト(\0)は、ソケットがファイルシステム自体ではなく、抽象的な名前空間に作成されることを意味します。
被告人

22

pidのライブラリは、まさにこれを行うことができます。

from pid import PidFile

with PidFile():
  do_something()

また、pidfileは存在するがプロセスが実行されていない場合も自動的に処理します。


これは美しく機能します。Ubuntuで実行するには、ルートとして実行する必要があります。+1
ジミー

11
@Jimmyたとえばwith PidFile(piddir='/home/user/run/')、別のディレクトリを使用して、pidファイルをアクセス許可のある場所に置くことができます。その後、rootで実行する必要はありません
Decko

ここで説明さている一時ディレクトリを使用することは、piddirの良いオプションになると思います。
Rishi Latchmepersad

@RishiLatchmepersad gettempdirを使用すると、すべての呼び出しで一意のディレクトリが提供されるため、pidチェックが失敗するため、良い考えではありません。ディレクトリは、スクリプトが実行されるたびに同じである必要があります。
Decko

11

もちろん、Danの例は、本来あるべきようには機能しません。

実際、スクリプトがクラッシュしたり、例外が発生したり、pidファイルがクリーンアップされなかったりすると、スクリプトは複数回実行されます。

私は別のウェブサイトに基づいて以下を提案します:

これはすでにロックファイルが存在するかどうかを確認することです

\#/usr/bin/env python
import os
import sys
if os.access(os.path.expanduser("~/.lockfile.vestibular.lock"), os.F_OK):
        #if the lockfile is already there then check the PID number
        #in the lock file
        pidfile = open(os.path.expanduser("~/.lockfile.vestibular.lock"), "r")
        pidfile.seek(0)
        old_pid = pidfile.readline()
        # Now we check the PID from lock file matches to the current
        # process PID
        if os.path.exists("/proc/%s" % old_pid):
                print "You already have an instance of the program running"
                print "It is running as process %s," % old_pid
                sys.exit(1)
        else:
                print "File is there but the program is not running"
                print "Removing lock file for the: %s as it can be there because of the program last time it was run" % old_pid
                os.remove(os.path.expanduser("~/.lockfile.vestibular.lock"))

これは、PIDファイルをロックファイルに配置するコードの一部です。

pidfile = open(os.path.expanduser("~/.lockfile.vestibular.lock"), "w")
pidfile.write("%s" % os.getpid())
pidfile.close()

このコードは、既存の実行中のプロセスと比較してpidの値をチェックし、二重実行を回避します。

お役に立てば幸いです。


3
を使用する必要がありますos.kill(old_pid, 0)。これは、UNIX間で移植性が高くなります。そのOSErrorようなPIDがないか、別のユーザーに属している場合に発生します。
drdaeman 2011

1
/ proc / <pid>を使用してプロセスをチェックすることは非常に移植性がなく、Linuxでのみ確実に機能することに注意してください。
Dan Udey、2015年

10

UNIXでプロセスを再起動するための非常に優れたパッケージがあります。ビルドと構成についての素晴らしいチュートリアルがあるのはmonitです。少し調整することで、デーモンを維持する確かな実績のあるテクノロジーを使用できます。


私は、などなど、実行していない場合は起動し、それが死んだ場合、それを再起動するなど、あなたのアプリケーションをデーモン化する方法のトンがあり、同意、車輪の再発明をしていない
davr

9

私の解決策は、Windowsとubuntu linuxでテストされたプロセスとコマンドライン引数を確認することです

import psutil
import os

def is_running(script):
    for q in psutil.process_iter():
        if q.name().startswith('python'):
            if len(q.cmdline())>1 and script in q.cmdline()[1] and q.pid !=os.getpid():
                print("'{}' Process is already running".format(script))
                return True

    return False


if not is_running("test.py"):
    n = input("What is Your Name? ")
    print ("Hello " + n)

@nstの回答のほかに、これがより良い回答です。
shgnInc

これが最善の方法です!thx
Hadi hashemi

5

無数のオプションがあります。1つの方法は、そのような呼び出しを実行するシステムコールまたはPythonライブラリを使用することです。もう1つは、次のようなプロセスを生成することです。

ps ax | grep processName

出力を解析します。多くの人がこのアプローチを選択します。それは、私の見解では必ずしも悪いアプローチではありません。


processNameにはスクリプトのファイル名が含まれますか?
Josh Hunt、

thetはプロセスの開始方法によって異なります
ojblass 2009

例:ps ax | grep python
ユーザー

3

自分で解決策を探しているこの古い質問に出くわしました。

psutilを使用します

import psutil
import sys
from subprocess import Popen

for process in psutil.process_iter():
    if process.cmdline() == ['python', 'your_script.py']:
        sys.exit('Process found: exiting.')

print('Process not found: starting it.')
Popen(['python', 'your_script.py'])

このスクリプトはsudoとして実行する必要があります。そうしないと、アクセス拒否エラーが発生します。
DoesData

また、リストのようなコマンドからスクリプトに引数を渡すと、それらの引数がすべて含まれます。
DoesData


2

この他のバージョンをお試しください

def checkPidRunning(pid):        
    '''Check For the existence of a unix pid.
    '''
    try:
        os.kill(pid, 0)
    except OSError:
        return False
    else:
        return True

# Entry point
if __name__ == '__main__':
    pid = str(os.getpid())
    pidfile = os.path.join("/", "tmp", __program__+".pid")

    if os.path.isfile(pidfile) and checkPidRunning(int(file(pidfile,'r').readlines()[0])):
            print "%s already exists, exiting" % pidfile
            sys.exit()
    else:
        file(pidfile, 'w').write(pid)

    # Do some actual work here
    main()

    os.unlink(pidfile)

1

独自のPIDファイルソリューションを開発するのではなく(考えているよりも多くの微妙な問題とコーナーケースがあります)、監視を確認してください-これは、既存のPythonの周りにジョブ制御とデーモンの動作を簡単にラップできるプロセス制御システムです脚本。


0

他の答えはcronジョブなどに最適ですが、デーモンを実行している場合は、daemontoolsなどで監視する必要があります。


0
ps ax | grep processName

pycharmのyorデバッグスクリプトが常に終了する場合

pydevd.py --multiproc --client 127.0.0.1 --port 33882 --file processName

0

これを試して:

#/usr/bin/env python
import os, sys, atexit

try:
    # Set PID file
    def set_pid_file():
        pid = str(os.getpid())
        f = open('myCode.pid', 'w')
        f.write(pid)
        f.close()

    def goodby():
        pid = str('myCode.pid')
        os.remove(pid)

    atexit.register(goodby)
    set_pid_file()
    # Place your code here

except KeyboardInterrupt:
    sys.exit(0)

0

以下はより便利なコードです(正確にpythonがスクリプトを実行するかどうかをチェックしています):

#! /usr/bin/env python

import os
from sys import exit


def checkPidRunning(pid):
    global script_name
    if pid<1:
        print "Incorrect pid number!"
        exit()
    try:
        os.kill(pid, 0)
    except OSError:
        print "Abnormal termination of previous process."
        return False
    else:
        ps_command = "ps -o command= %s | grep -Eq 'python .*/%s'" % (pid,script_name)
        process_exist = os.system(ps_command)
        if process_exist == 0:
            return True
        else:
            print "Process with pid %s is not a Python process. Continue..." % pid
            return False


if __name__ == '__main__':
    script_name = os.path.basename(__file__)
    pid = str(os.getpid())
    pidfile = os.path.join("/", "tmp/", script_name+".pid")
    if os.path.isfile(pidfile):
        print "Warning! Pid file %s existing. Checking for process..." % pidfile
        r_pid = int(file(pidfile,'r').readlines()[0])
        if checkPidRunning(r_pid):
            print "Python process with pid = %s is already running. Exit!" % r_pid
            exit()
        else:
            file(pidfile, 'w').write(pid)
    else:
        file(pidfile, 'w').write(pid)

# main programm
....
....

os.unlink(pidfile)

ここに文字列があります:

ps_command = "ps -o command= %s | grep -Eq 'python .*/%s'" % (pid,script_name)

「grep」が成功し、プロセス「python」が現在スクリプトの名前をパラメーターとして実行している場合、0を返します。


0

プロセス名のみが存在するかどうかを探している場合の簡単な例:

import os

def pname_exists(inp):
    os.system('ps -ef > /tmp/psef')
    lines=open('/tmp/psef', 'r').read().split('\n')
    res=[i for i in lines if inp in i]
    return True if res else False

Result:
In [21]: pname_exists('syslog')
Out[21]: True

In [22]: pname_exists('syslog_')
Out[22]: False

-1

問題を解決するには、次の例を検討してください。

#!/usr/bin/python
# -*- coding: latin-1 -*-

import os, sys, time, signal

def termination_handler (signum,frame):
    global running
    global pidfile
    print 'You have requested to terminate the application...'
    sys.stdout.flush()
    running = 0
    os.unlink(pidfile)

running = 1
signal.signal(signal.SIGINT,termination_handler)

pid = str(os.getpid())
pidfile = '/tmp/'+os.path.basename(__file__).split('.')[0]+'.pid'

if os.path.isfile(pidfile):
    print "%s already exists, exiting" % pidfile
    sys.exit()
else:
    file(pidfile, 'w').write(pid)

# Do some actual work here

while running:
  time.sleep(10)

このスクリプトは1回しか実行できないため、このスクリプトをお勧めします。


-1

bashを使用して、現在のスクリプト名でプロセスを検索します。余分なファイルはありません。

import commands
import os
import time
import sys

def stop_if_already_running():
    script_name = os.path.basename(__file__)
    l = commands.getstatusoutput("ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'" % script_name)
    if l[1]:
        sys.exit(0);

テストするには、

stop_if_already_running()
print "running normally"
while True:
    time.sleep(3)

追加のファイルはありませんが、6つの追加のプロセスですか?
Alois Mahdal 2013

2
そして、私ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'がそのことを実行するとどうなりますか?;)
アロイス・マダル2013

ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'をしているのか分かりません。名前でプロセスを検索する必要がある場合は、なぜ使用しないのpgrepですか?の目的はawk '{print $2}'| awk '{print $2}'何ですか?一般に、区切り文字を変更しない限り、そのようにawkを2回続けて実行することはできません。最初のawkはPID列になります... 2番目のawkは何も起こりません。

-1

これは、すでに実行されている場合にスクリプトを開始しないようにするためにLinuxで使用するものです。

import os
import sys


script_name = os.path.basename(__file__)
pidfile = os.path.join("/tmp", os.path.splitext(script_name)[0]) + ".pid"


def create_pidfile():
    if os.path.exists(pidfile):
        with open(pidfile, "r") as _file:
            last_pid = int(_file.read())

        # Checking if process is still running
        last_process_cmdline = "/proc/%d/cmdline" % last_pid
        if os.path.exists(last_process_cmdline):
            with open(last_process_cmdline, "r") as _file:
                cmdline = _file.read()
            if script_name in cmdline:
                raise Exception("Script already running...")

    with open(pidfile, "w") as _file:
        pid = str(os.getpid())
        _file.write(pid)


def main():
    """Your application logic goes here"""


if __name__ == "__main__":
    create_pidfile()
    main()

このアプローチは、外部モジュールに依存することなくうまく機能します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.