检查python脚本是不是正在运行
Posted
技术标签:
【中文标题】检查python脚本是不是正在运行【英文标题】:Check to see if python script is running检查python脚本是否正在运行 【发布时间】:2010-10-21 18:21:44 【问题描述】:我有一个 python 守护程序作为我的网络应用程序的一部分运行/我如何快速检查(使用 python)我的守护程序是否正在运行,如果没有,启动它?
我想这样做来修复守护程序的任何崩溃,因此脚本不必手动运行,它会在调用时自动运行,然后继续运行。
如果我的脚本正在运行,我如何检查(使用 python)?
【问题讨论】:
你确定你的进程不想让你的其他进程也用python编写吗? 试一试 Tendo,创建脚本的单例实例,因此如果脚本已经在运行,它将不会运行。 github.com/pycontribs/tendo 这不是你的守护进程的工作,这是启动你的守护进程的“上层”应用程序的工作。使用 systemd 或其他工具,如 supervisord。不要依赖写入文件的 pid。如果您不能使用 systemd/supervisord,则使用锁定来确定它不会被执行两次。 【参考方案1】:在 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 时锁定文件到处存在的问题
您可以read in the documentation for socket.close
垃圾回收时自动关闭套接字。
【讨论】:
未来 googlers 的注意事项:此代码使用“抽象套接字”,它是 Linux 特定的(一般不是 posix)。更多信息:blog.eduardofleury.com/archives/2007/09/13 这太棒了,它不会留下愚蠢的挥之不去的文件。希望我能多点赞。 太棒了。但我想知道为什么 lock_socket 是全局定义的。我测试过,如果 lock_socket 未全局定义,则在运行多个进程时锁定系统不起作用。为什么? lock_socket 已定义且仅在 get_lock 函数中使用。为什么必须将其定义为全局? 好久没有写这个了……我的记忆很模糊。但我认为这是因为它会收集垃圾,否则套接字会关闭。类似的东西。 空字节(\0
)表示套接字是在抽象命名空间中创建的,而不是在文件系统本身上创建的。【参考方案2】:
在某处放置一个 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。
【讨论】:
如果程序中断,os.unlink() 将不会执行,程序也不会再次运行,因为文件存在。对吗? 正确,但这可能是预期的行为。如果 pidfile 存在但里面的 PID 没有运行,则表示非正常关闭,这意味着应用程序崩溃了。这让您知道有问题,并检查日志。如前所述,atexit 模块也可以解决这个问题,假设错误不在 Python 解释器本身中。 虽然是一个简单的解决方案,但这很容易受到竞争条件的影响。如果脚本的两个实例几乎同时执行,if os.path.isfile(pidfile)
可能对两者都评估为 false,从而导致它们都写入锁定文件并继续运行。
pids 也被操作系统重用。所以误报是可能的。
对于那些现在发现它的人,请注意在 python 3 中 file()
已被删除,您应该使用 open()
代替。此外,即使您使用的是 2.7,您也应该使用 open()
而不是 file()
,如下所述:docs.python.org/2/library/functions.html#file(是的,如果您在 2.2 左右使用 python,则官方建议相反。显然他们改变了主意。 )【参考方案3】:
pid 库可以做到这一点。
from pid import PidFile
with PidFile():
do_something()
它还会自动处理pidfile存在但进程未运行的情况。
【讨论】:
这很好用。它只需要以 root 身份运行才能在 Ubuntu 上运行。 +1 @Jimmy 你可以做例如with PidFile(piddir='/home/user/run/')
使用不同的目录将 pid 文件放在您有权限的位置。然后你不需要以root身份运行它
我认为使用 here 描述的临时目录对于 piddir 来说是一个不错的选择。
@RishiLatchmepersad 使用 gettempdir 不是一个好主意,因为这会在每次调用时提供一个唯一目录,这会破坏 pid 检查。每次脚本运行时目录都需要相同。
在某些情况下,您可能需要手动强制删除 pidfile:pidfile.close(fh=pidfile.fh, cleanup=True)【参考方案4】:
当然,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 与现有正在运行的进程相比的值。避免重复执行。
我希望它会有所帮助。
【讨论】:
应该使用os.kill(old_pid, 0)
,它应该在 UNIX 上更便携。如果没有这样的 PID 或者它属于不同的用户,它将引发 OSError
。
请注意,使用 /proc/在 UNIX 上有一些非常好的用于重启进程的软件包。 monit 有一个关于构建和配置它的很棒的教程。通过一些调整,您可以获得可靠的成熟技术来维持您的守护进程。
【讨论】:
我同意,不要重新发明***,有很多方法可以守护你的应用程序,包括如果它死了就重新启动它,如果它没有运行就启动它等等【参考方案6】:我的解决方案是检查进程和命令行参数 在 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 的答案,这是更好的答案。 您需要确保脚本以python ..
启动,而不是直接通过调用./<script name>
启动,否则它将无法工作,因为它会检查进程是否以python
开头。【参考方案7】:
有无数种选择。一种方法是使用系统调用或 python 库为您执行此类调用。另一种是简单地产生一个过程,如:
ps ax | grep processName
并解析输出。很多人选择这种方法,在我看来这不一定是一个坏方法。
【讨论】:
processName 会包含我的脚本的文件名吗? 这取决于你如何开始你的过程 例如:ps ax | grep python【参考方案8】:自己遇到了这个老问题,正在寻找解决方案。
使用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 运行,否则您将收到拒绝访问错误。 此外,如果您从命令中将参数传递给脚本,例如列表也将包含所有这些参数。【参考方案9】:我是 Supervisor 管理守护程序的忠实粉丝。它是用 Python 编写的,因此有很多关于如何与 Python 交互或从 Python 扩展它的示例。出于您的目的,XML-RPC process control API 应该可以很好地工作。
【讨论】:
【参考方案10】:试试这个其他版本
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)
【讨论】:
【参考方案11】:与其开发你自己的 PID 文件解决方案(它比你想象的更微妙和极端情况),不如看看supervisord——这是一个过程控制系统,可以很容易地包装作业控制和守护进程围绕现有 Python 脚本的行为。
【讨论】:
【参考方案12】:其他答案非常适合 cron 作业,但如果您正在运行守护程序,则应使用 daemontools 之类的内容对其进行监控。
【讨论】:
【参考方案13】:ps ax | grep processName
如果你在 pycharm 中的调试脚本总是退出
pydevd.py --multiproc --client 127.0.0.1 --port 33882 --file processName
【讨论】:
【参考方案14】:试试这个:
#/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)
【讨论】:
【参考方案15】:这里是更有用的代码(检查是否正是 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”成功,则返回 0,并且进程“python”当前正在以脚本名称作为参数运行。
【讨论】:
【参考方案16】:一个简单的例子,如果你只是在寻找一个进程名称是否存在:
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
【讨论】:
【参考方案17】:在我看来,我一直在寻找这个问题的答案,就我而言,我想到了一个非常简单且非常好的解决方案(因为不可能存在误报,我猜 - 时间戳怎么能如果程序不这样做,则在 TXT 上更新):
--> 根据您的需要,在某个时间间隔内继续在 TXT 上写入当前时间戳(这里每半小时是完美的)。
如果您检查时TXT上的时间戳相对于当前时间戳已过时,则程序存在问题,应该重新启动它或您喜欢做的事情。
【讨论】:
【参考方案18】:考虑以下示例来解决您的问题:
#!/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)
我推荐这个脚本,因为它只能执行一次。
【讨论】:
【参考方案19】:使用 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个额外的进程? 如果我ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'
运行那个东西会怎样? ;)
我不明白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 将不会产生任何结果。【参考方案20】:
这是我在 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()
这种方法在不依赖外部模块的情况下效果很好。
【讨论】:
以上是关于检查python脚本是不是正在运行的主要内容,如果未能解决你的问题,请参考以下文章