检查python脚本是否正在运行


100

我有一个作为后台程序运行的python守护程序/如何快速查看(使用python)我的守护程序是否正在运行,如果没有运行,请启动它?

我想通过这种方式来修复守护程序的任何崩溃,因此该脚本不必手动运行,它将在调用后立即自动运行,然后保持运行状态。

如何检查(使用python)脚本是否正在运行?


您确定您的进程也不需要用python编写其他进程吗?
ojblass

尝试一下Tendo,创建脚本的单例实例,因此,如果脚本已经在运行,它将不会运行。github.com/pycontribs/tendo
JasTonAChair

这不是您的守护程序的工作,这是启动您的守护程序的“上层”应用程序的工作。使用systemd或其他诸如supervisord的工具。不要依赖于写入文件的pid。如果您不能使用systemd / supervisord,请使用锁定来确保它不会被执行两次。
guettli

Answers:


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(如上所述)可以为您完成此操作,或者您可以编写一个简单的Shell脚本使用ps的返回代码为您检查它。

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

为了获得更多的荣誉,您可以使用atexit模块来确保您的程序在任何情况下(被杀死,引发异常等)都清理其pidfile。


6
如果程序已中断,则os.unlink()将不会执行,并且该程序将不会再次运行,因为该文件已存在。对 ?
尤达·普拉维拉

2
正确,但是这可能是预期的行为。如果pidfile存在,但内部的PID未运行,则表示非正常关机,这意味着应用程序崩溃了。这样一来,您就可以知道存在问题并检查日志。如前所述,假设错误不在Python解释器本身中,则atexit模块也可以解决此问题。
丹·乌迪

7
尽管是一种简单的解决方案,但它容易受到竞争条件的影响。如果脚本的两个实例大约在同一时间执行,则这两个实例的if os.path.isfile(pidfile)评估结果都可能为false,从而导致它们都写入锁定文件并继续运行。
塞林

6
pid也可被操作系统重用。因此,误报是可能的。
aychedee 2013年

12
对于那些现在发现此问题的人,请注意,在python 3 file()中已将其删除,而应open()改为使用。此外,即使你是在2.7,你应该使用open()file():作为解释这里docs.python.org/2/library/functions.html#file(是的,如果你使用的蟒蛇绕回2.2的官方意见是相反的。显然他们改变了主意。)
jpk,2015年

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

4
太棒了 但是我不知道为什么lock_socket定义为全局。我进行了测试,如果未全局定义lock_socket,则在运行多个进程时锁定系统将无法正常工作。为什么?定义了lock_socket,仅在get_lock函数中使用。为什么必须全局定义?
Alptugay 2014年

7
自从我写这篇文章已经有一段时间了...我的记忆模糊。但是我认为这是因为它会收集垃圾,否则会关闭套接字。这样的事情。
2014年

8
空字节(\0)表示套接字是在抽象名称空间中创建的,而不是在文件系统本身上创建的。
Aychedee'7

22

PID库可以做的正是这一点。

from pid import PidFile

with PidFile():
  do_something()

它还会自动处理pidfile存在但进程未运行的情况。


这工作美丽。它只需要以root用户身份运行才能在Ubuntu上运行。+1
吉米

11
@Jimmy您可以执行例如with PidFile(piddir='/home/user/run/')使用其他目录将pid文件放在您具有权限的位置。然后,您无需以root用户身份运行它
Decko 2015年

我在想,使用的临时目录中描述这里将是该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上可靠地工作。
丹·乌迪

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

有很多选择。一种方法是使用系统调用或为您执行此类调用的python库。另一种方法是产生一个类似以下的过程:

ps ax | grep processName

并解析输出。许多人选择这种方法,在我看来,这不一定是不好的方法。


processName是否包含脚本的文件名?
乔什·亨特

这取决于您如何开始过程
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身份运行,否则您将获得拒绝访问错误。
DidData

同样,如果您从命令中将参数传递给脚本,例如列表,列表中也将包含所有这些参数。
DidsData


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中的调试脚本始终退出

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个额外的进程?
2013年

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不会有任何结果。
2015年

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

这种方法效果很好,无需依赖任何外部模块。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.