本文目录一览:
python 守护进程
一、守护进程的特性
守护进程是一个在后台运行并且不受任何终端控制的进程(守护进程独立于所有终端,之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。)
二、守护进程的作用
守护进程是一类在后台执行,生命周期较长的进程,它一般随系统启动运行,在系统关闭的时候停止。所以守护进程一般用作系统后台服务。
三、如何编写一个守护进程
编写守护进程实际上是把一个普通进程按照守护进程的特性进行改造。
守护进程的开发涉及到子进程、进程组、会晤期、信号量、文件权限、目录和控制终端等多个概念。
由于守护进程是脱离控制终端的,因此首先创建子进程,终止父进程,使得程序在shell终端里造成一个已经运行完毕的假象。之后所有的工作都在子进程中完成,而用户在shell终端里则可以执行其他的命令,从而使得程序以僵尸进程形式运行,在形式上做到了与控制终端的脱离。
四、python 编写守护进程
参考:
如何给脚本写一个守护进程
在我们日常运维中,写脚本监控一个进程是比较常见的操作,比如我要监控mysql进程是否消失,如果消失就重启mysql,用下面这段代码就可以实现:
#!/bin/sh
Date=` date '+%c'`
while :
do
if ! psaux | grep -w mysqld | grep -v grep /dev/null 21
then
/etc/init.d/mysqldstart
echo $Datemysqldwasreboot /var/log/reboot_mysql.log
fi
done
本篇这是以mysql为例子,但实际中如果是监控的脚本出了问题,报警没发出来,那就比较尴尬了,所以为保证我们的检查脚本能实时运行,我们需要一个进程来守护这个脚本,这就是我们今天要说的主题,如何给脚本写一个daemon,我们先上代码:
#!/usr/bin/python
import subprocess
from daemonimport runner
cmd = "/root/demo_script/restart_mysql.sh"
class App():
def __init__(self):
self.stdin_path = '/dev/null'
self.stdout_path = '/dev/tty'
self.stderr_path = '/dev/tty'
self.pidfile_path = '/tmp/hello.pid'
self.pidfile_timeout = 5
def start_subprocess(self):
return subprocess.Popen(cmd, shell=True)
def run(self):
p = self.start_subprocess()
while True:
res = p.poll()
if resis not None:
p = self.start_subprocess()
if __name__ == '__main__':
app = App()
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()
脚本比较简单,没什么特别的逻辑,关于daemon这个模块如何使用,我这里给出官方的解释,注意哟,是英文的,我就不翻译了,如果有生词就查查字典,就当多学几个了单词吧。
__init__(self, app)
| Setuptheparametersof a new runner.
|
| The `app` argumentmusthavethefollowingattributes:
|
| * `stdin_path`, `stdout_path`, `stderr_path`: Filesystem
| pathsto openand replacetheexisting `sys.stdin`,
| `sys.stdout`, `sys.stderr`.
|
| * `pidfile_path`: Absolutefilesystempathto a filethat
| willbeusedas thePIDfilefor thedaemon. If
| ``None``, noPIDfilewillbeused.
|
| * `pidfile_timeout`: Usedas thedefault acquisition
| timeoutvaluesuppliedto therunner's PIDlockfile.
|
| * `run`: Callablethatwillbeinvokedwhenthedaemonis
| started.
|
| do_action(self)
| Performtherequestedaction.
|
| parse_args(self, argv=None)
| Parsecommand-linearguments.
这样就完成了,守护进程的启动比较高大上,输入以上代码后,可以直接在终端输入:
#python monitor.py start
当然还有stop,restart等参数。
这里我介绍的是其中一个应用场景,实际中可以灵活运用,比如1台服务器上启动的程序过多,环境配置比较复杂,就可以先启动daemon进程,然后通过daemon来启动其它所有应用程序,就不用一个一个应用程序启动了,这篇就到这里,有问题可以给我留言。
如何在python脚本中新建一个守护子进程
函数实现
[html] view plaincopy
#!/usr/bin/env python
#coding: utf-8
import sys, os
'''将当前进程fork为一个守护进程
注意:如果你的守护进程是由inetd启动的,不要这样做!inetd完成了
所有需要做的事情,包括重定向标准文件描述符,需要做的事情只有chdir()和umask()了
'''
def daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
#重定向标准文件描述符(默认情况下定向到/dev/null)
try:
pid = os.fork()
#父进程(会话组头领进程)退出,这意味着一个非会话组头领进程永远不能重新获得控制终端。
if pid 0:
sys.exit(0) #父进程退出
except OSError, e:
sys.stderr.write ("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror) )
sys.exit(1)
#从母体环境脱离
os.chdir("/") #chdir确认进程不保持任何目录于使用状态,否则不能umount一个文件系统。也可以改变到对于守护程序运行重要的文件所在目录
os.umask(0) #调用umask(0)以便拥有对于写的任何东西的完全控制,因为有时不知道继承了什么样的umask。
os.setsid() #setsid调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。
#执行第二次fork
try:
pid = os.fork()
if pid 0:
sys.exit(0) #第二个父进程退出
except OSError, e:
sys.stderr.write ("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror) )
sys.exit(1)
#进程已经是守护进程了,重定向标准文件描述符
for f in sys.stdout, sys.stderr: f.flush()
si = open(stdin, 'r')
so = open(stdout, 'a+')
se = open(stderr, 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno()) #dup2函数原子化关闭和复制文件描述符
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
#示例函数:每秒打印一个数字和时间戳
def main():
import time
sys.stdout.write('Daemon started with pid %d\n' % os.getpid())
sys.stdout.write('Daemon stdout output\n')
sys.stderr.write('Daemon stderr output\n')
c = 0
while True:
sys.stdout.write('%d: %s\n' %(c, time.ctime()))
sys.stdout.flush()
c = c+1
time.sleep(1)
if __name__ == "__main__":
daemonize('/dev/null','/tmp/daemon_stdout.log','/tmp/daemon_error.log')
main()
可以通过命令ps -ef | grep daemon.py查看后台运行的继承,在/tmp/daemon_error.log会记录错误运行日志,在/tmp/daemon_stdout.log会记录标准输出日志。