Kiểm tra xem tập lệnh python có đang chạy không


100

Tôi có một trình nền python đang chạy như một phần của ứng dụng web của tôi / Làm cách nào để kiểm tra nhanh (bằng cách sử dụng python) nếu daemon của tôi đang chạy và khởi chạy nó nếu không?

Tôi muốn làm theo cách đó để khắc phục mọi sự cố của daemon và do đó, tập lệnh không phải chạy theo cách thủ công, nó sẽ tự động chạy ngay sau khi nó được gọi và sau đó tiếp tục chạy.

Làm cách nào để kiểm tra (sử dụng python) nếu tập lệnh của tôi đang chạy?


Bạn có chắc chắn rằng bạn sẽ không giữ quy trình của bạn quá trình khác của bạn được viết bằng python?
ojblass

Hãy đến Tendo, tạo một phiên bản singleton của script của bạn, do đó script sẽ không chạy nếu nó đã chạy. github.com/pycontribs/tendo
JasTonAChair

Đây không phải là công việc mà daemon của bạn, đây là công việc của ứng dụng "phía trên" khởi chạy daemon của bạn. Sử dụng systemd hoặc một công cụ khác như supervisord. Đừng dựa vào một pid được ghi vào một tệp. Nếu bạn không thể sử dụng systemd / supervisord, hãy sử dụng khóa để đảm bảo rằng nó không được thực thi hai lần.
guettli

Câu trả lời:


92

Thả một pidfile ở đâu đó (ví dụ / tmp). Sau đó, bạn có thể kiểm tra xem quá trình có đang chạy hay không bằng cách kiểm tra xem PID trong tệp có tồn tại hay không. Đừng quên xóa tệp khi bạn tắt hoàn toàn và kiểm tra nó khi bạn khởi động.

#/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)

Sau đó, bạn có thể kiểm tra xem quy trình có đang chạy hay không bằng cách kiểm tra xem nội dung của /tmp/mydaemon.pid có phải là quy trình hiện có hay không. Monit (đã đề cập ở trên) có thể làm điều này cho bạn hoặc bạn có thể viết một tập lệnh shell đơn giản để kiểm tra nó cho bạn bằng cách sử dụng mã trả về từ ps.

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

Để có thêm tín dụng, bạn có thể sử dụng mô-đun atexit để đảm bảo rằng chương trình của bạn xóa sạch tệp pidfile của nó trong bất kỳ trường hợp nào (khi bị giết, nâng cấp ngoại lệ, v.v.).


6
nếu chương trình bị hỏng, os.unlink () sẽ không thực thi và chương trình sẽ không chạy lại vì tệp đã tồn tại. đúng ?
Yuda Prawira

2
Đúng, tuy nhiên đây có thể là hành vi được mong đợi. Nếu pidfile tồn tại nhưng PID bên trong không chạy, điều đó cho thấy việc tắt máy không liên tục, có nghĩa là ứng dụng bị lỗi. Điều đó cho bạn biết có sự cố và kiểm tra nhật ký. Như đã đề cập, mô-đun atexit cũng có thể xử lý vấn đề này, giả sử rằng lỗi không nằm trong chính trình thông dịch Python.
Dan Udey

7
Mặc dù một giải pháp đơn giản, điều này dễ bị ảnh hưởng bởi tình trạng chủng tộc. Nếu hai phiên bản của tập lệnh được thực thi cùng một lúc, có if os.path.isfile(pidfile)thể đánh giá là false cho cả hai, khiến cả hai đều ghi tệp khóa và tiếp tục chạy.
Cerin

6
pids cũng được sử dụng lại bởi hệ điều hành. Vì vậy, dương tính giả là có thể.
aychedee

12
Đối với những người tìm thấy điều này ngay bây giờ, hãy lưu ý rằng trong python 3 file()đã bị xóa và bạn nên sử dụng open()thay thế. Ngoài ra, ngay cả khi bạn đang ở trên 2.7, bạn nên sử dụng open()hơn file()như được giải thích ở đây: docs.python.org/2/library/functions.html#file (Và có, nếu bạn đã sử dụng python khoảng 2.2, lời khuyên chính thức là ngược lại. Rõ ràng họ đã thay đổi ý định.)
jpk

154

Một kỹ thuật hữu ích trên hệ thống Linux là sử dụng các ổ cắm miền:

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)

Nó là nguyên tử và tránh vấn đề có các tệp khóa nằm xung quanh nếu quy trình của bạn được gửi SIGKILL

Bạn có thể đọc trong tài liệu đểsocket.close biết rằng các ổ cắm sẽ tự động đóng khi thu gom rác.


20
Lưu ý cho những người dùng google trong tương lai: mã này sử dụng "các ổ cắm trừu tượng", dành riêng cho Linux (không phải posix nói chung). Thêm về điều này: blog.eduardofleury.com/archives/2007/09/13
Georg

6
Điều này thật tuyệt vời và nó không để lại các tệp ngu ngốc kéo dài. Ước gì tôi có thể ủng hộ điều này nhiều hơn.
Hiro2k

4
Tuyệt vời. Nhưng tôi tự hỏi tại sao lock_socket lại được định nghĩa toàn cầu. Tôi đã thử nghiệm và nếu lock_socket không được xác định toàn cục, hệ thống khóa không hoạt động khi chạy nhiều quy trình. Tại sao? lock_socket được định nghĩa và chỉ được sử dụng trong hàm get_lock. Tại sao nó phải được định nghĩa toàn cầu?
Alptugay

7
Đã lâu rồi tôi không viết cái này ... và trí nhớ của tôi mơ hồ. Nhưng tôi nghĩ đó là vì nó được thu gom rác và ổ cắm sẽ bị đóng lại. Đại loại vậy.
aychedee

8
Byte null ( \0) có nghĩa là ổ cắm được tạo trong không gian tên trừu tượng thay vì được tạo trên chính hệ thống tệp.
aychedee

22

Các pid thư viện có thể thực hiện chính xác này.

from pid import PidFile

with PidFile():
  do_something()

Nó cũng sẽ tự động xử lý trường hợp pidfile tồn tại nhưng tiến trình không chạy.


Điều này hoạt động rất ĐẸP. Nó chỉ phải được chạy dưới dạng root để chạy trên Ubuntu. +1
Jimmy

11
@Jimmy bạn có thể làm ví dụ: with PidFile(piddir='/home/user/run/')sử dụng một thư mục khác để đặt tệp pid vào nơi bạn có quyền. Sau đó, bạn không cần phải chạy nó như là người chủ
Decko

Tôi nghĩ rằng việc sử dụng thư mục tạm thời như được mô tả ở đây sẽ là một lựa chọn tốt cho piddir.
Rishi Latchmepersad

@RishiLatchmepersad Sử dụng gettempdir sẽ không phải là một ý tưởng hay vì điều đó sẽ cung cấp một thư mục duy nhất cho mỗi cuộc gọi, điều này sẽ phá vỡ kiểm tra pid. Thư mục cần phải giống nhau mỗi khi tập lệnh chạy.
Decko

11

Tất nhiên ví dụ từ Dan sẽ không hoạt động như nó phải.

Thật vậy, nếu tập lệnh gặp sự cố, tăng một ngoại lệ hoặc không làm sạch tệp pid, thì tập lệnh sẽ được chạy nhiều lần.

Tôi đề xuất những điều sau dựa trên một trang web khác:

Điều này để kiểm tra xem đã có tệp khóa tồn tại chưa

\#/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"))

Đây là một phần của mã nơi chúng tôi đặt tệp PID vào tệp khóa

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

Đoạn mã này sẽ kiểm tra giá trị của pid so với tiến trình đang chạy hiện có., Tránh thực thi kép.

Tôi mong nó sẽ có ích.


3
Một nên sử dụng os.kill(old_pid, 0), nên di động hơn trên các UNIX. Nó sẽ tăng lên OSErrornếu không có PID như vậy hoặc nó thuộc về người dùng khác.
drdaeman

1
Hãy lưu ý rằng việc sử dụng / proc / <pid> để kiểm tra một quy trình là cực kỳ không di động và sẽ chỉ hoạt động đáng tin cậy trên Linux.
Dan Udey

10

Có những gói rất tốt để khởi động lại quy trình trên UNIX. Một trong đó có một hướng dẫn tuyệt vời về xây dựng và cấu hình nó được monit . Với một số điều chỉnh, bạn có thể có một công nghệ đã được chứng minh vững chắc theo kịp daemon của bạn.


Tôi đồng ý, đừng phát minh lại bánh xe, có rất nhiều cách để tạo ứng dụng của bạn bao gồm khởi động lại ứng dụng nếu nó chết, khởi chạy nếu không chạy, v.v.
davr 25-04-09

9

Giải pháp của tôi là kiểm tra các đối số quy trình và dòng lệnh Đã kiểm tra trên windows và linux ubuntu

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)

Bên cạnh câu trả lời của @nst, đây là câu trả lời tốt hơn.
shgnInc

Đây la cach tôt nhât! thx
Hadi hashemi

5

Có vô số lựa chọn. Một phương pháp là sử dụng lời gọi hệ thống hoặc thư viện python thực hiện các lệnh gọi như vậy cho bạn. Cách khác chỉ đơn giản là tạo ra một quy trình như:

ps ax | grep processName

và phân tích cú pháp đầu ra. Nhiều người chọn cách tiếp cận này, nó không hẳn là một cách tiếp cận tồi theo quan điểm của tôi.


processName sẽ bao gồm tên tệp của tập lệnh của tôi?
Josh Hunt

điều này phụ thuộc vào cách bạn bắt đầu quy trình của mình
ojblass

ví dụ: ps ax | grep python
Người dùng

3

Đi qua câu hỏi cũ này và tìm kiếm giải pháp cho chính mình.

Sử dụng 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'])

Tập lệnh này phải được chạy dưới dạng sudo nếu không bạn sẽ gặp lỗi bị từ chối truy cập.
DoesData

Ngoài ra, nếu bạn truyền các đối số cho tập lệnh của mình từ lệnh như danh sách cũng sẽ có tất cả các đối số đó.
DoesData

2

Tôi là một fan hâm mộ lớn của Supervisor để quản lý daemon. Nó được viết bằng Python, vì vậy có rất nhiều ví dụ về cách tương tác với hoặc mở rộng nó từ Python. Đối với mục đích của bạn, API điều khiển quy trình XML-RPC sẽ hoạt động tốt.


2

Hãy thử phiên bản khác này

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

Thay vì phát triển giải pháp tệp PID của riêng bạn (có nhiều điểm phức tạp và các trường hợp góc cạnh hơn bạn có thể nghĩ), hãy xem xét giám sát viên - đây là một hệ thống kiểm soát quy trình giúp dễ dàng kết hợp các hành vi kiểm soát công việc và daemon xung quanh Python hiện có kịch bản.


0

Các câu trả lời khác là tuyệt vời cho những thứ như cron job, nhưng nếu bạn đang chạy một daemon, bạn nên theo dõi nó bằng một thứ gì đó như daemontools .


0
ps ax | grep processName

nếu bạn gỡ lỗi tập lệnh trong pycharm luôn thoát

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

0

thử cái này:

#/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

Đây là mã hữu ích hơn (với việc kiểm tra xem python có thực thi chính xác tập lệnh hay không):

#! /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)

Đây là chuỗi:

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

trả về 0 nếu "grep" thành công và quá trình "python" hiện đang chạy với tên tập lệnh của bạn làm tham số.


0

Một ví dụ đơn giản nếu bạn chỉ đang tìm kiếm một tên quy trình có tồn tại hay không:

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

Hãy xem xét ví dụ sau để giải quyết vấn đề của bạn:

#!/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)

Tôi đề xuất tập lệnh này vì nó có thể được thực thi một lần duy nhất.


-1

Sử dụng bash để tìm kiếm tiến trình có tên tập lệnh hiện tại. Không có tệp bổ sung.

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);

Để kiểm tra, hãy thêm

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

Không có tệp bổ sung nhưng có 6 quy trình bổ sung?
Alois Mahdal

2
Và nếu tôi ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'và chạy thứ đó thì sao? ;)
Alois Mahdal

Tôi không hiểu chuyện gì ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'đang làm. Nếu bạn cần tìm kiếm một tiến trình theo tên thì tại sao không sử dụng pgrep? Mục đích là awk '{print $2}'| awk '{print $2}'gì? Nói chung, bạn không thể chạy awk hai lần liên tiếp như vậy trừ khi bạn thay đổi dấu phân cách. Awk đầu tiên kết quả trong cột PID ... awk thứ hai sẽ không kết quả.
Sáu

-1

Đây là những gì tôi sử dụng trong Linux để tránh bắt đầu một tập lệnh nếu đã chạy:

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()

Cách tiếp cận này hoạt động tốt mà không phụ thuộc vào mô-đun bên ngoài.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.