ตรวจสอบดูว่าสคริปต์ python กำลังทำงานอยู่หรือไม่


104

ฉันมี python daemon ที่ทำงานเป็นส่วนหนึ่งของเว็บแอปของฉัน / ฉันจะตรวจสอบอย่างรวดเร็ว (โดยใช้ python) ได้อย่างไรว่า daemon ของฉันกำลังทำงานอยู่และถ้าไม่เปิดใช้งาน

ฉันต้องการทำวิธีนั้นเพื่อแก้ไขข้อขัดข้องของ daemon ดังนั้นจึงไม่จำเป็นต้องเรียกใช้สคริปต์ด้วยตนเองสคริปต์จะทำงานโดยอัตโนมัติทันทีที่มีการเรียกใช้จากนั้นจึงทำงานต่อไป

ฉันจะตรวจสอบ (โดยใช้ python) ได้อย่างไรว่าสคริปต์ของฉันกำลังทำงานอยู่


คุณแน่ใจหรือไม่ว่าคุณต้องการให้กระบวนการอื่น ๆ ของคุณเขียนเป็น python เกินไป
ojblass

ไปที่ Tendo สร้างอินสแตนซ์ซิงเกิลของสคริปต์ของคุณดังนั้นสคริปต์จะไม่ทำงานหากทำงานอยู่แล้ว github.com/pycontribs/tendo
JasTonAChair

นี่ไม่ใช่งาน daemon ของคุณนี่คืองานของแอปพลิเคชัน "ด้านบน" ที่เปิดใช้งาน daemon ของคุณ ใช้ systemd หรือเครื่องมืออื่น ๆ เช่นผู้บังคับบัญชา อย่าพึ่งพา pid ที่เขียนลงในไฟล์ หากคุณไม่สามารถใช้ systemd / Supervisor ได้ให้ใช้การล็อกเพื่อไม่แน่ใจว่าจะไม่มีการดำเนินการสองครั้ง
guettli

คำตอบ:


94

วางพิดไฟล์ไว้ที่ไหนสักแห่ง (เช่น / 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 ไม่ว่าในสถานการณ์ใด ๆ (เมื่อถูกฆ่า, มีการเพิ่มข้อยกเว้น ฯลฯ )


7
หากโปรแกรมเสีย os.unlink () จะไม่ทำงานและโปรแกรมจะไม่ทำงานอีกเนื่องจากมีไฟล์อยู่ ใช่ไหม
ยุดาปวีรา

2
ถูกต้องอย่างไรก็ตามอาจเป็นพฤติกรรมที่คาดหวัง หาก pidfile มีอยู่ แต่ PID ภายในไม่ทำงานนั่นแสดงว่ามีการปิดระบบที่ไม่สง่างามซึ่งหมายความว่าแอปหยุดทำงาน ซึ่งช่วยให้คุณทราบว่ามีปัญหาและตรวจสอบบันทึกได้ ดังที่ได้กล่าวไปแล้วโมดูล atexit ยังสามารถดูแลสิ่งนี้ได้โดยสมมติว่าจุดบกพร่องไม่ได้อยู่ในตัวแปล Python
Dan Udey

7
แม้ว่าจะเป็นวิธีแก้ปัญหาง่ายๆ แต่ก็มีความอ่อนไหวต่อสภาพการแข่งขัน หากสคริปต์สองอินสแตนซ์ถูกเรียกใช้ในเวลาเดียวกันif os.path.isfile(pidfile)อาจเป็นไปได้ว่าอาจประเมินเป็นเท็จสำหรับทั้งสองอย่างทำให้ทั้งคู่เขียนไฟล์ล็อกและทำงานต่อไป
Cerin

6
pids ยังถูกนำมาใช้ใหม่โดยระบบปฏิบัติการ ผลบวกลวงจึงเป็นไปได้
aychedee

12
สำหรับผู้ที่พบสิ่งนี้ในขณะนี้โปรดทราบว่าใน python 3 file()ถูกลบออกไปแล้วและคุณควรใช้open()แทน นอกจากนี้แม้ว่าคุณจะอยู่บน 2.7 คุณควรใช้open()มากกว่าfile()ตามที่อธิบายไว้ที่นี่: docs.python.org/2/library/functions.html#file (และใช่ถ้าคุณใช้หลามกลับรอบ 2.2 คำแนะนำอย่างเป็นทางการตรงข้าม เห็นได้ชัดว่าพวกเขาเปลี่ยนใจ)
jpk

157

เทคนิคที่มีประโยชน์ในระบบ 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 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ว่าซ็อกเก็ตจะปิดโดยอัตโนมัติเมื่อเก็บขยะ


21
หมายเหตุสำหรับ Googler ในอนาคต: โค้ดนี้ใช้ "ซ็อกเก็ตนามธรรม" ซึ่งเป็นข้อมูลเฉพาะของ Linux (ไม่ใช่ posix โดยทั่วไป) เพิ่มเติมเกี่ยวกับเรื่องนี้: blog.eduardofleury.com/archives/2007/09/13
georg

6
สิ่งนี้ยอดเยี่ยมมากและไม่ทิ้งไฟล์ที่น่าเบื่อหน่าย หวังว่าฉันจะโหวตให้มากกว่านี้
Hiro2k

4
น่ากลัว แต่ฉันสงสัยว่าทำไม lock_socket จึงกำหนดเป็น global ฉันทดสอบแล้วและหากไม่ได้กำหนด lock_socket ส่วนกลางระบบล็อกจะไม่ทำงานเมื่อเรียกใช้หลายกระบวนการ ทำไม? lock_socket ถูกกำหนดและใช้ในฟังก์ชัน get_lock เท่านั้น ทำไมต้องมีการกำหนด global?
Alptugay

7
เป็นเวลานานแล้วที่ฉันเขียนสิ่งนี้ ... และความทรงจำของฉันก็มืดมน แต่ฉันคิดว่ามันเป็นเพราะมันถูกเก็บขยะและเต้ารับก็ปิดลง อะไรแบบนั้น.
aychedee

8
null byte ( \0) หมายถึงซ็อกเก็ตถูกสร้างขึ้นในเนมสเปซนามธรรมแทนที่จะสร้างบนระบบไฟล์เอง
aychedee

23

pidห้องสมุดสามารถทำตรงนี้

from pid import PidFile

with PidFile():
  do_something()

นอกจากนี้ยังจัดการกรณีที่มี pidfile โดยอัตโนมัติ แต่กระบวนการไม่ทำงาน


งานนี้สวยงาม มันต้องถูกเรียกใช้ในฐานะรูทเพื่อที่จะทำงานบน Ubuntu +1
Jimmy

11
@ Jimmy คุณสามารถทำได้เช่นwith PidFile(piddir='/home/user/run/')ใช้ไดเร็กทอรีอื่นเพื่อใส่ไฟล์ pid ในที่ที่คุณมีสิทธิ์ จากนั้นคุณไม่จำเป็นต้องเรียกใช้เป็นรูท
Decko

ฉันคิดว่าการใช้ temp directory ตามที่อธิบายไว้ที่นี่จะเป็นตัวเลือกที่ดีสำหรับ piddir
Rishi Latchmepersad

@RishiLatchmepersad การใช้ gettempdir คงไม่ใช่ความคิดที่ดีเนื่องจากจะให้ไดเร็กทอรีที่ไม่ซ้ำกันในทุกการโทรซึ่งจะทำลายการตรวจสอบ pid ไดเร็กทอรีต้องเหมือนกันทุกครั้งที่เรียกใช้สคริปต์
Decko

ขอบคุณ! ทำงานได้ดีแม้ว่าจะมีข้อยกเว้นเพิ่มขึ้นก็ตาม
Zhou Hongbo

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

1
โปรดทราบว่าการใช้ / proc / <pid> เพื่อตรวจสอบกระบวนการนั้นไม่สามารถพกพาได้อย่างมากและจะทำงานบน Linux ได้อย่างน่าเชื่อถือเท่านั้น
Dan Udey

10

มีแพ็คเกจที่ดีมากสำหรับการรีสตาร์ทกระบวนการบน UNIX หนึ่งที่มีการกวดวิชาที่ดีเกี่ยวกับการสร้างและการกำหนดค่ามันเป็นmonit ด้วยการปรับแต่งบางอย่างคุณสามารถมีเทคโนโลยีที่พิสูจน์แล้วว่าแข็งแกร่งเพื่อรักษาภูตของคุณ


ฉันเห็นด้วยไม่ต้องสร้างวงล้อใหม่มีหลายวิธีในการทำให้แอปของคุณเริ่มต้นใหม่รวมถึงการรีสตาร์ทถ้ามันตายเปิดตัวถ้าไม่ทำงาน ฯลฯ
davr

8

วิธีแก้ปัญหาของฉันคือตรวจสอบกระบวนการและอาร์กิวเมนต์บรรทัดคำสั่งที่ทดสอบบน 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

5

มีตัวเลือกมากมาย วิธีหนึ่งคือการใช้การเรียกระบบหรือไลบรารี python ที่ดำเนินการเรียกดังกล่าวให้คุณ อีกอย่างคือการสร้างกระบวนการเช่น:

ps ax | grep processName

และแยกวิเคราะห์ผลลัพธ์ หลายคนเลือกแนวทางนี้มันไม่จำเป็นต้องเป็นแนวทางที่ไม่ดีในมุมมองของฉัน


processName จะรวมชื่อไฟล์ของสคริปต์ของฉันหรือไม่
Josh Hunt

มันขึ้นอยู่กับว่าคุณเริ่มกระบวนการของคุณ
อย่างไร

ตัวอย่างเช่น: 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 สำหรับวัตถุประสงค์ของคุณAPI การควบคุมกระบวนการ XML-RPCควรทำงานได้ดี


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

ถ้า yor debug script ใน pycharm จะออกเสมอ

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)

คืนค่า 0 ถ้า "grep" สำเร็จและกระบวนการ "python" กำลังทำงานโดยมีชื่อสคริปต์ของคุณเป็นพารามิเตอร์


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

2
แล้วถ้าฉันln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'เรียกใช้สิ่งนั้นล่ะ? ;)
Alois Mahdal

ฉันไม่เข้าใจว่า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.