Python 스크립트가 실행 중인지 확인하십시오.


100

웹 앱의 일부로 파이썬 데몬을 실행하고 있습니다 / 데몬이 실행 중인지 (파이썬 사용) 빠르게 확인하고 그렇지 않은 경우 시작하려면 어떻게해야합니까?

데몬의 크래시를 수정하기 위해 그렇게하고 싶습니다. 그래서 스크립트를 수동으로 실행할 필요가 없으며, 호출되는 즉시 자동으로 실행 된 다음 계속 실행됩니다.

내 스크립트가 실행 중인지 어떻게 확인할 수 있습니까 (python 사용)?


당신의 프로세스도 파이썬으로 작성된 다른 프로세스를 유지하고 싶지 않습니까?
ojblass

Tendo에 가서 스크립트의 싱글 톤 인스턴스를 생성하므로 스크립트가 이미 실행 중이면 실행되지 않습니다. github.com/pycontribs/tendo
JasTonAChair

이것은 데몬의 작업이 아니라 데몬을 시작하는 "상위"응용 프로그램의 작업입니다. systemd 또는 supervisord와 같은 다른 도구를 사용하십시오. 파일에 기록 된 pid에 의존하지 마십시오. systemd / supervisord를 사용할 수없는 경우 잠금을 사용하여 두 번 실행되지 않는지 확인하십시오.
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

7
간단한 솔루션이지만 경쟁 조건에 취약합니다. 스크립트의 두 인스턴스가 거의 동시에 실행되는 경우 두 인스턴스 if os.path.isfile(pidfile)모두에 대해 false로 평가되어 둘 다 잠금 파일을 작성하고 계속 실행되도록 할 수 있습니다.
Cerin 2013 년

6
pid는 운영 체제에서도 재사용됩니다. 따라서 오탐이 가능합니다.
aychedee

12
지금 이것을 찾은 사람들을 위해, 파이썬 3 file()에서 제거되었으며 open()대신 사용해야 합니다. 또한, 2.7에있어 경우에도 사용해야 open()이상 file(): 여기에 설명 된대로 docs.python.org/2/library/functions.html#file을 (그리고 다시 약 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
georg

6
이것은 굉장하며 어리석은 느린 파일을 남기지 않습니다. 더 많이 찬성 할 수 있기를 바랍니다.
Hiro2k 2013 년

4
대박. 하지만 lock_socket이 전역으로 정의 된 이유가 궁금합니다. 테스트 한 결과 lock_socket이 전역으로 정의되지 않은 경우 여러 프로세스를 실행할 때 잠금 시스템이 작동하지 않습니다. 왜? lock_socket이 정의되어 있으며 get_lock 함수에서만 사용됩니다. 글로벌로 정의해야하는 이유는 무엇입니까?
Alptugay

7
이 글을 쓴 지 오랜만인데 ... 기억이 흐릿합니다. 그러나 나는 그것이 가비지 수집되고 소켓이 닫히기 때문이라고 생각합니다. 그런 것.
aychedee 2014 년

8
널 바이트 ( \0)는 소켓이 파일 시스템 자체에서 생성되는 대신 추상 네임 스페이스에서 생성됨을 의미합니다.
aychedee

22

PID 라이브러리는 정확히이 작업을 수행 할 수 있습니다.

from pid import PidFile

with PidFile():
  do_something()

또한 pidfile이 존재하지만 프로세스가 실행되지 않는 경우를 자동으로 처리합니다.


이것은 아름답게 작동합니다. Ubuntu에서 실행하려면 루트로 실행해야합니다. +1
Jimmy

11
@Jimmy 예 with PidFile(piddir='/home/user/run/')를 들어 다른 디렉토리를 사용하여 권한이있는 곳에 pid 파일을 넣을 수 있습니다. 그러면 루트로 실행할 필요가 없습니다
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)유닉스의에서 이식성해야한다. 그것은 올릴 것이다 OSError그런 PID 존재하지 않는 경우 또는 다른 사용자에 속한다.
drdaeman

1
/ proc / <pid>를 사용하여 프로세스를 확인하는 것은 매우 이식성이 없으며 Linux에서만 안정적으로 작동합니다.
Dan Udey

10

UNIX에서 프로세스를 다시 시작하기위한 아주 좋은 패키지가 있습니다. 빌드 및 구성에 대한 훌륭한 튜토리얼이있는 것은 monit 입니다. 약간의 조정으로 데몬을 유지하는 견고한 입증 된 기술을 가질 수 있습니다.


동의합니다, 바퀴를 재발 명하지 마세요. 앱이 죽으면 다시 시작하고, 실행되지 않으면 시작하는 등 앱을 데몬 화하는 수많은 방법이 있습니다
davr

9

내 해결책은 Windows 및 우분투 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

무수히 많은 옵션이 있습니다. 한 가지 방법은 이러한 호출을 수행하는 시스템 호출 또는 Python 라이브러리를 사용하는 것입니다. 다른 하나는 다음과 같은 프로세스를 생성하는 것입니다.

ps ax | grep processName

출력을 구문 분석하십시오. 많은 사람들이이 접근 방식을 선택하지만 제 생각에는 반드시 나쁜 접근 방식은 아닙니다.


processName에 내 스크립트의 파일 이름이 포함됩니까?
Josh Hunt

프로세스를 시작하는 방법에 따라 다릅니다.
ojblass

예 : 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

나는 데몬을 관리하는 Supervisor 의 열렬한 팬입니다 . Python으로 작성되었으므로 Python과 상호 작용하거나 Python에서 확장하는 방법에 대한 많은 예가 있습니다. 귀하의 목적을 위해 XML-RPC 프로세스 제어 API 는 잘 작동해야합니다.


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 파일 솔루션 (생각할 수있는 것보다 더 미묘한 경우가 많음)을 개발하는 대신 supervisord를 살펴보십시오. 이것은 기존 Python 주위에 작업 제어 및 데몬 동작을 쉽게 래핑 할 수있는 프로세스 제어 시스템입니다. 스크립트.



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

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'실행하면 어떨까요? ;)
Alois Mahdal 2013

나는 무엇 ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'을하고 있는지 이해하지 못한다 . 이름으로 프로세스를 검색해야하는 경우 왜 사용하지 pgrep않습니까? 의 목적은 awk '{print $2}'| awk '{print $2}'무엇입니까? 일반적으로 구분 기호를 변경하지 않는 한 awk를 두 번 연속해서 실행할 수 없습니다. 첫 번째 awk는 PID 열이됩니다. 두 번째 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.