python使用fork实现守护进程的方法
os模块中的fork方法可以创建一个子进程。相当于克隆了父进程
os.fork()
子进程运行时,os.fork方法会返回0;
而父进程运行时,os.fork方法会返回子进程的PID号。
所以可以使用PID来区分两个进程:
#!/usr/bin/envpython #coding=utf8 fromtimeimportsleep importos try: pid=os.fork() exceptOSError,e: pass sleep(30)
运行代码,查看进程:
[root@localhost~]#pythontest2.py& [1]2464 [root@localhost~]#ps-l FSUIDPIDPPIDCPRINIADDRSZWCHANTTYTIMECMD 4S0237923770800-28879waitpts/100:00:00bash 0S0246423790800-31318poll_spts/100:00:00python 1S0246524640800-31318poll_spts/100:00:00python 0R0246623790800-37227-pts/100:00:00ps
可以看出第二条python进程就是第一条的子进程。
如刚刚所说os.fork()方法区分子进程和父进程
#-*-coding:utf-8-*-
fromtimeimportsleep
importos
print('start+++++++++++++')
#创建子进程之前声明的变量
source=10
try:
pid=os.fork()
print('pid=',pid)
ifpid==0:#子进程
print("thisischildprocess.")
source=source-1#在子进程中source减1
else:#父进程
print("thisisparentprocess.")
print(source)
except(OSError,e):
pass
print('END---------------')
面代码中,在子进程创建前,声明了一个变量source,然后在子进程中减1,最后打印出source的值,显然父进程打印出来的值应该为10,子进程打印出来的值应该为9。
[root@localhost~]#pythontest3.py start+++++++++++++ pid=2550 thisisparentprocess. 10 END--------------- pid=0 thisischildprocess. 9 END---------------
简单守护进程例子:
defmain():
'''程序要执行的逻辑代码'''
pass
#创建守护进程函数
defcreateDaemon():
'''第一块(创建第一个子进程)'''
#fork第一个子进程(如果fork成功,父进程自杀,只留下第一个子进程继续向下运行)
try:
ifos.fork()>0:
sys.exit(0)
exceptOSError,error:
print'(fork第一个子进程失败)fork#1failed:%d(%s)'%(error.errno,error.strerror)
sys.exit(1)
'''第一块结束'''
######第一个进程创建成功后,它的ppid=1,已是一个守护里程了,但有些功能上还是有被限制。
######所以下面再来创建一个子进程。第二次创建的子进程限制就没那多了,有可能没有,所以最安全。
######下面来创建第二个子进程。
os.chdir('/')#把第一个子进程的工作目录切换到/(根目录)
os.setsid()#第一个子进程取得程序的权限
os.umask(0)#第一个子进程取得工作目录的所有操作(目录的rwx)
'''第二块(创建第二个子进程)'''
#fork第二个子进程(如果fork成功,第一个子进程自杀,只留下新创建的第二个子进程)
try:
pid=os.fork()
ifpid>0:
print'DaemonPID%d'%pid
sys.exit(0)
exceptOSError,error:
print'(fork第二个子进程失败)fork#2failed:%d(%s)'%(error.errno,error.strerror)
sys.exit(1)
'''第二块结束'''
#######通过上面两个try语句块,只留下了第二个子进程在运行了。这时第二个子进程的ppid=1。
#######创建的第二个子进程,可以说是一个不受限的守护进程了。
#重定向标准IO(因为只有第二个子进程在运行了,所以也就是指定整个程序的输入、输出、错误流)
#sys.stdout.flush()#清除程序运行空间的输出流
#sys.stderr.flush()#清除程序运行空间的错误流
#inputS=file("/dev/null",'r')#定义一个inputS文件对象
#outputS=file("/dev/null",'a+')#定义一个outputS文件对象
#errorS=file("/dev/null",'a+',0)#定义一个errorS文件对象
#os.dup2(inputS.fileno(),sys.stdin.fileno())#把程序的输入流重定向到上面定义的inputS文件对象上。
#os.dup2(so.fileno(),sys.stdout.fileno())#把程序的输出流重定向到上面定义的outputS文件对象上。
#os.dup2(se.fileno(),sys.stderr.fileno())#把程序的错误流重定向到上面定义的errorS文件对象上。
main()#main函数为真正程序逻辑代码
if__name__=="__main__":
ifplatform.system()=="Linux":
createDaemon()
else:
sys.exit()
带控制参数的例子:
编写守护进程的基类,用于继承:
#coding:utf-8
importos
importsys
importtime
importatexit
importsignal
classDaemon:
def__init__(self,pidfile='/tmp/daemon.pid',stdin='/dev/null',stdout='/dev/null',stderr='/dev/null'):
self.stdin=stdin
self.stdout=stdout
self.stderr=stderr
self.pidfile=pidfile
defdaemonize(self):
ifos.path.exists(self.pidfile):
raiseRuntimeError('Alreadyrunning.')
#Firstfork(detachesfromparent)
try:
ifos.fork()>0:
raiseSystemExit(0)
exceptOSErrorase:
raiseRuntimeError('fork#1faild:{0}({1})\n'.format(e.errno,e.strerror))
os.chdir('/')
os.setsid()
os.umask(0o22)
#Secondfork(relinquishsessionleadership)
try:
ifos.fork()>0:
raiseSystemExit(0)
exceptOSErrorase:
raiseRuntimeError('fork#2faild:{0}({1})\n'.format(e.errno,e.strerror))
#FlushI/Obuffers
sys.stdout.flush()
sys.stderr.flush()
#Replacefiledescriptorsforstdin,stdout,andstderr
withopen(self.stdin,'rb',0)asf:
os.dup2(f.fileno(),sys.stdin.fileno())
withopen(self.stdout,'ab',0)asf:
os.dup2(f.fileno(),sys.stdout.fileno())
withopen(self.stderr,'ab',0)asf:
os.dup2(f.fileno(),sys.stderr.fileno())
#WritethePIDfile
withopen(self.pidfile,'w')asf:
print(os.getpid(),file=f)
#ArrangetohavethePIDfileremovedonexit/signal
atexit.register(lambda:os.remove(self.pidfile))
signal.signal(signal.SIGTERM,self.__sigterm_handler)
#Signalhandlerfortermination(required)
@staticmethod
def__sigterm_handler(signo,frame):
raiseSystemExit(1)
defstart(self):
try:
self.daemonize()
exceptRuntimeErrorase:
print(e,file=sys.stderr)
raiseSystemExit(1)
self.run()
defstop(self):
try:
ifos.path.exists(self.pidfile):
withopen(self.pidfile)asf:
os.kill(int(f.read()),signal.SIGTERM)
else:
print('Notrunning.',file=sys.stderr)
raiseSystemExit(1)
exceptOSErrorase:
if'Nosuchprocess'instr(e)andos.path.exists(self.pidfile):
os.remove(self.pidfile)
defrestart(self):
self.stop()
self.start()
defrun(self):
#继承类重写该方法
pass
编写自己的类:
#导入刚刚编写的基类
fromdaemonimportDaemon
#继承
classMyTestDaemon(Daemon):
#重写run方法,就是你要后台运行的函数
defrun(self):
#后台运行的函数,比如shell输出到自己定义的文件
sys.stdout.write('Daemonstartedwithpid{}\n'.format(os.getpid()))
whileTrue:
sys.stdout.write('DaemonAlive!{}\n'.format(time.ctime()))
sys.stdout.flush()
time.sleep(5)
if__name__=='__main__':
PIDFILE='/tmp/daemon-example.pid'
LOG='/tmp/daemon-example.log'
daemon=MyTestDaemon(pidfile=PIDFILE,stdout=LOG,stderr=LOG)
iflen(sys.argv)!=2:
print('Usage:{}[start|stop]'.format(sys.argv[0]),file=sys.stderr)
raiseSystemExit(1)
if'start'==sys.argv[1]:
daemon.start()
elif'stop'==sys.argv[1]:
daemon.stop()
elif'restart'==sys.argv[1]:
daemon.restart()
else:
print('Unknowncommand{!r}'.format(sys.argv[1]),file=sys.stderr)
raiseSystemExit(1)
关于两次fork
第二个fork不是必须的,只是为了防止进程打开控制终端。
打开一个控制终端的条件是该进程必须是sessionleader。第一次fork,setsid之后,子进程成为sessionleader,进程可以打开终端;第二次fork产生的进程,不再是sessionleader,进程则无法打开终端。
也就是说,只要程序实现得好,控制程序不主动打开终端,无第二次fork亦可。
代码实现
#coding:utf-8
importos
importsys
importtime
importatexit
importsignal
classDaemon:
def__init__(self,pidfile='/tmp/daemon.pid',stdin='/dev/null',stdout='/dev/null',stderr='/dev/null'):
self.stdin=stdin
self.stdout=stdout
self.stderr=stderr
self.pidfile=pidfile
defdaemonize(self):
ifos.path.exists(self.pidfile):
raiseRuntimeError('Alreadyrunning.')
#Firstfork(detachesfromparent)
try:
ifos.fork()>0:
raiseSystemExit(0)
exceptOSErrorase:
raiseRuntimeError('fork#1faild:{0}({1})\n'.format(e.errno,e.strerror))
os.chdir('/')
os.setsid()
os.umask(0o22)
#Secondfork(relinquishsessionleadership)
try:
ifos.fork()>0:
raiseSystemExit(0)
exceptOSErrorase:
raiseRuntimeError('fork#2faild:{0}({1})\n'.format(e.errno,e.strerror))
#FlushI/Obuffers
sys.stdout.flush()
sys.stderr.flush()
#Replacefiledescriptorsforstdin,stdout,andstderr
withopen(self.stdin,'rb',0)asf:
os.dup2(f.fileno(),sys.stdin.fileno())
withopen(self.stdout,'ab',0)asf:
os.dup2(f.fileno(),sys.stdout.fileno())
withopen(self.stderr,'ab',0)asf:
os.dup2(f.fileno(),sys.stderr.fileno())
#WritethePIDfile
withopen(self.pidfile,'w')asf:
print(os.getpid(),file=f)
#ArrangetohavethePIDfileremovedonexit/signal
atexit.register(lambda:os.remove(self.pidfile))
signal.signal(signal.SIGTERM,self.__sigterm_handler)
#Signalhandlerfortermination(required)
@staticmethod
def__sigterm_handler(signo,frame):
raiseSystemExit(1)
defstart(self):
try:
self.daemonize()
exceptRuntimeErrorase:
print(e,file=sys.stderr)
raiseSystemExit(1)
self.run()
defstop(self):
try:
ifos.path.exists(self.pidfile):
withopen(self.pidfile)asf:
os.kill(int(f.read()),signal.SIGTERM)
else:
print('Notrunning.',file=sys.stderr)
raiseSystemExit(1)
exceptOSErrorase:
if'Nosuchprocess'instr(e)andos.path.exists(self.pidfile):
os.remove(self.pidfile)
defrestart(self):
self.stop()
self.start()
defrun(self):
pass
使用测试
importos
importsys
importtime
fromdaemonimportDaemon
classMyTestDaemon(Daemon):
defrun(self):
sys.stdout.write('Daemonstartedwithpid{}\n'.format(os.getpid()))
whileTrue:
sys.stdout.write('DaemonAlive!{}\n'.format(time.ctime()))
sys.stdout.flush()
time.sleep(5)
if__name__=='__main__':
PIDFILE='/tmp/daemon-example.pid'
LOG='/tmp/daemon-example.log'
daemon=MyTestDaemon(pidfile=PIDFILE,stdout=LOG,stderr=LOG)
iflen(sys.argv)!=2:
print('Usage:{}[start|stop]'.format(sys.argv[0]),file=sys.stderr)
raiseSystemExit(1)
if'start'==sys.argv[1]:
daemon.start()
elif'stop'==sys.argv[1]:
daemon.stop()
elif'restart'==sys.argv[1]:
daemon.restart()
else:
print('Unknowncommand{!r}'.format(sys.argv[1]),file=sys.stderr)
raiseSystemExit(1)
[daemon]pythontest.pystart23:45:42 [daemon]cat/tmp/daemon-example.pid23:45:49 8532 [daemon]ps-ef|grep8532|grep-vgrep23:46:07 50285321011:45下午??0:00.00pythontest.pystart [daemon]tail-f/tmp/daemon-example.log23:46:20 Daemonstartedwithpid8532 DaemonAlive!FriDec223:45:492016 DaemonAlive!FriDec223:45:542016 DaemonAlive!FriDec223:45:592016 DaemonAlive!FriDec223:46:042016 DaemonAlive!FriDec223:46:092016 DaemonAlive!FriDec223:46:142016 DaemonAlive!FriDec223:46:192016 DaemonAlive!FriDec223:46:242016 DaemonAlive!FriDec223:46:292016 DaemonAlive!FriDec223:46:342016 [daemon]pythontest.pystop23:46:36 [daemon]ps-ef|grep8532|grep-vgrep23:46:43
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。