python使用fork實(shí)現(xiàn)守護(hù)進(jìn)程的方法
os模塊中的fork方法可以創(chuàng)建一個(gè)子進(jìn)程。相當(dāng)于克隆了父進(jìn)程
os.fork()
子進(jìn)程運(yùn)行時(shí),os.fork方法會(huì)返回0;
而父進(jìn)程運(yùn)行時(shí),os.fork方法會(huì)返回子進(jìn)程的PID號(hào)。
所以可以使用PID來(lái)區(qū)分兩個(gè)進(jìn)程:
#!/usr/bin/env python #coding=utf8 from time import sleep import os try: pid = os.fork() except OSError, e: pass sleep(30)
運(yùn)行代碼,查看進(jìn)程:
[root@localhost ~]# python test2.py & [1] 2464 [root@localhost ~]# ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 2379 2377 0 80 0 - 28879 wait pts/1 00:00:00 bash 0 S 0 2464 2379 0 80 0 - 31318 poll_s pts/1 00:00:00 python 1 S 0 2465 2464 0 80 0 - 31318 poll_s pts/1 00:00:00 python 0 R 0 2466 2379 0 80 0 - 37227 - pts/1 00:00:00 ps
可以看出第二條python進(jìn)程就是第一條的子進(jìn)程。
如剛剛所說(shuō)os.fork()方法區(qū)分子進(jìn)程和父進(jìn)程
#-*- coding:utf-8 -*- from time import sleep import os print('start+++++++++++++') #創(chuàng)建子進(jìn)程之前聲明的變量 source = 10 try: pid = os.fork() print('pid=',pid) if pid == 0: #子進(jìn)程 print("this is child process.") source = source - 1 #在子進(jìn)程中source減1 else: #父進(jìn)程 print("this is parent process." ) print(source) except (OSError,e): pass print('END---------------')
面代碼中,在子進(jìn)程創(chuàng)建前,聲明了一個(gè)變量source,然后在子進(jìn)程中減1,最后打印出source的值,顯然父進(jìn)程打印出來(lái)的值應(yīng)該為10,子進(jìn)程打印出來(lái)的值應(yīng)該為9。
[root@localhost ~]# python test3.py start+++++++++++++ pid= 2550 this is parent process. 10 END--------------- pid= 0 this is child process. 9 END---------------
簡(jiǎn)單守護(hù)進(jìn)程例子:
def main(): ''' 程序要執(zhí)行的邏輯代碼 ''' pass # 創(chuàng)建守護(hù)進(jìn)程函數(shù) def createDaemon(): ''' 第一塊(創(chuàng)建第一個(gè)子進(jìn)程) ''' # fork 第一個(gè)子進(jìn)程(如果fork成功,父進(jìn)程自殺,只留下第一個(gè)子進(jìn)程繼續(xù)向下運(yùn)行) try: if os.fork() > 0: sys.exit(0) except OSError, error: print '(fork第一個(gè)子進(jìn)程失敗)fork #1 failed: %d (%s)' % (error.errno, error.strerror) sys.exit(1) ''' 第一塊結(jié)束 ''' ###### 第一個(gè)進(jìn)程創(chuàng)建成功后,它的ppid = 1,已是一個(gè)守護(hù)里程了,但有些功能上還是有被限制。 ###### 所以下面再來(lái)創(chuàng)建一個(gè)子進(jìn)程。第二次創(chuàng)建的子進(jìn)程限制就沒(méi)那多了,有可能沒(méi)有,所以最安全。 ###### 下面來(lái)創(chuàng)建第二個(gè)子進(jìn)程。 os.chdir('/') # 把第一個(gè)子進(jìn)程的工作目錄切換到 / (根目錄) os.setsid() # 第一個(gè)子進(jìn)程取得程序的權(quán)限 os.umask(0) # 第一個(gè)子進(jìn)程取得工作目錄的所有操作(目錄的rwx) ''' 第二塊(創(chuàng)建第二個(gè)子進(jìn)程) ''' # fork 第二個(gè)子進(jìn)程(如果fork成功,第一個(gè)子進(jìn)程自殺,只留下新創(chuàng)建的第二個(gè)子進(jìn)程) try: pid = os.fork() if pid > 0: print 'Daemon PID %d' % pid sys.exit(0) except OSError, error: print '(fork第二個(gè)子進(jìn)程失?。ゝork #2 failed: %d (%s)' % (error.errno, error.strerror) sys.exit(1) ''' 第二塊結(jié)束 ''' ####### 通過(guò)上面兩個(gè) try 語(yǔ)句塊,只留下了第二個(gè)子進(jìn)程在運(yùn)行了。這時(shí)第二個(gè)子進(jìn)程的ppid=1。 ####### 創(chuàng)建的第二個(gè)子進(jìn)程,可以說(shuō)是一個(gè)不受限的守護(hù)進(jìn)程了。 # 重定向標(biāo)準(zhǔn)IO(因?yàn)橹挥械诙€(gè)子進(jìn)程在運(yùn)行了,所以也就是指定整個(gè)程序的輸入、輸出、錯(cuò)誤流) # sys.stdout.flush() # 清除程序運(yùn)行空間的輸出流 # sys.stderr.flush() # 清除程序運(yùn)行空間的錯(cuò)誤流 # inputS = file("/dev/null", 'r') # 定義一個(gè) inputS 文件對(duì)象 # outputS = file("/dev/null", 'a+') # 定義一個(gè) outputS 文件對(duì)象 # errorS = file("/dev/null", 'a+', 0) # 定義一個(gè) errorS 文件對(duì)象 # os.dup2(inputS.fileno(), sys.stdin.fileno()) # 把程序的輸入流重定向到上面定義的 inputS 文件對(duì)象上。 # os.dup2(so.fileno(), sys.stdout.fileno()) # 把程序的 輸出流 重定向到上面定義的 outputS 文件對(duì)象上。 # os.dup2(se.fileno(), sys.stderr.fileno()) # 把程序的 錯(cuò)誤流 重定向到上面定義的 errorS 文件對(duì)象上。 main() # main函數(shù)為真正程序邏輯代碼 if __name__ == "__main__": if platform.system() == "Linux": createDaemon() else: sys.exit()
帶控制參數(shù)的例子:
編寫(xiě)守護(hù)進(jìn)程的基類(lèi),用于繼承:
# coding: utf-8 import os import sys import time import atexit import signal class Daemon: 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 def daemonize(self): if os.path.exists(self.pidfile): raise RuntimeError('Already running.') # First fork (detaches from parent) try: if os.fork() > 0: raise SystemExit(0) except OSError as e: raise RuntimeError('fork #1 faild: {0} ({1})\n'.format(e.errno, e.strerror)) os.chdir('/') os.setsid() os.umask(0o22) # Second fork (relinquish session leadership) try: if os.fork() > 0: raise SystemExit(0) except OSError as e: raise RuntimeError('fork #2 faild: {0} ({1})\n'.format(e.errno, e.strerror)) # Flush I/O buffers sys.stdout.flush() sys.stderr.flush() # Replace file descriptors for stdin, stdout, and stderr with open(self.stdin, 'rb', 0) as f: os.dup2(f.fileno(), sys.stdin.fileno()) with open(self.stdout, 'ab', 0) as f: os.dup2(f.fileno(), sys.stdout.fileno()) with open(self.stderr, 'ab', 0) as f: os.dup2(f.fileno(), sys.stderr.fileno()) # Write the PID file with open(self.pidfile, 'w') as f: print(os.getpid(), file=f) # Arrange to have the PID file removed on exit/signal atexit.register(lambda: os.remove(self.pidfile)) signal.signal(signal.SIGTERM, self.__sigterm_handler) # Signal handler for termination (required) @staticmethod def __sigterm_handler(signo, frame): raise SystemExit(1) def start(self): try: self.daemonize() except RuntimeError as e: print(e, file=sys.stderr) raise SystemExit(1) self.run() def stop(self): try: if os.path.exists(self.pidfile): with open(self.pidfile) as f: os.kill(int(f.read()), signal.SIGTERM) else: print('Not running.', file=sys.stderr) raise SystemExit(1) except OSError as e: if 'No such process' in str(e) and os.path.exists(self.pidfile): os.remove(self.pidfile) def restart(self): self.stop() self.start() def run(self): #繼承類(lèi)重寫(xiě)該方法 pass
編寫(xiě)自己的類(lèi):
#導(dǎo)入剛剛編寫(xiě)的基類(lèi) from daemon import Daemon #繼承 class MyTestDaemon(Daemon): #重寫(xiě)run方法,就是你要后臺(tái)運(yùn)行的函數(shù) def run(self): #后臺(tái)運(yùn)行的函數(shù),比如shell輸出到自己定義的文件 sys.stdout.write('Daemon started with pid {}\n'.format(os.getpid())) while True: sys.stdout.write('Daemon Alive! {}\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) if len(sys.argv) != 2: print('Usage: {} [start|stop]'.format(sys.argv[0]), file=sys.stderr) raise SystemExit(1) if 'start' == sys.argv[1]: daemon.start() elif 'stop' == sys.argv[1]: daemon.stop() elif 'restart' == sys.argv[1]: daemon.restart() else: print('Unknown command {!r}'.format(sys.argv[1]), file=sys.stderr) raise SystemExit(1)
關(guān)于兩次fork
第二個(gè)fork不是必須的,只是為了防止進(jìn)程打開(kāi)控制終端。
打開(kāi)一個(gè)控制終端的條件是該進(jìn)程必須是session leader。第一次fork,setsid之后,子進(jìn)程成為session leader,進(jìn)程可以打開(kāi)終端;第二次fork產(chǎn)生的進(jìn)程,不再是session leader,進(jìn)程則無(wú)法打開(kāi)終端。
也就是說(shuō),只要程序?qū)崿F(xiàn)得好,控制程序不主動(dòng)打開(kāi)終端,無(wú)第二次fork亦可。
代碼實(shí)現(xiàn)
# coding: utf-8 import os import sys import time import atexit import signal class Daemon: 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 def daemonize(self): if os.path.exists(self.pidfile): raise RuntimeError('Already running.') # First fork (detaches from parent) try: if os.fork() > 0: raise SystemExit(0) except OSError as e: raise RuntimeError('fork #1 faild: {0} ({1})\n'.format(e.errno, e.strerror)) os.chdir('/') os.setsid() os.umask(0o22) # Second fork (relinquish session leadership) try: if os.fork() > 0: raise SystemExit(0) except OSError as e: raise RuntimeError('fork #2 faild: {0} ({1})\n'.format(e.errno, e.strerror)) # Flush I/O buffers sys.stdout.flush() sys.stderr.flush() # Replace file descriptors for stdin, stdout, and stderr with open(self.stdin, 'rb', 0) as f: os.dup2(f.fileno(), sys.stdin.fileno()) with open(self.stdout, 'ab', 0) as f: os.dup2(f.fileno(), sys.stdout.fileno()) with open(self.stderr, 'ab', 0) as f: os.dup2(f.fileno(), sys.stderr.fileno()) # Write the PID file with open(self.pidfile, 'w') as f: print(os.getpid(), file=f) # Arrange to have the PID file removed on exit/signal atexit.register(lambda: os.remove(self.pidfile)) signal.signal(signal.SIGTERM, self.__sigterm_handler) # Signal handler for termination (required) @staticmethod def __sigterm_handler(signo, frame): raise SystemExit(1) def start(self): try: self.daemonize() except RuntimeError as e: print(e, file=sys.stderr) raise SystemExit(1) self.run() def stop(self): try: if os.path.exists(self.pidfile): with open(self.pidfile) as f: os.kill(int(f.read()), signal.SIGTERM) else: print('Not running.', file=sys.stderr) raise SystemExit(1) except OSError as e: if 'No such process' in str(e) and os.path.exists(self.pidfile): os.remove(self.pidfile) def restart(self): self.stop() self.start() def run(self): pass
使用測(cè)試
import os import sys import time from daemon import Daemon class MyTestDaemon(Daemon): def run(self): sys.stdout.write('Daemon started with pid {}\n'.format(os.getpid())) while True: sys.stdout.write('Daemon Alive! {}\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) if len(sys.argv) != 2: print('Usage: {} [start|stop]'.format(sys.argv[0]), file=sys.stderr) raise SystemExit(1) if 'start' == sys.argv[1]: daemon.start() elif 'stop' == sys.argv[1]: daemon.stop() elif 'restart' == sys.argv[1]: daemon.restart() else: print('Unknown command {!r}'.format(sys.argv[1]), file=sys.stderr) raise SystemExit(1)
[daemon] python test.py start 23:45:42 [daemon] cat /tmp/daemon-example.pid 23:45:49 8532 [daemon] ps -ef|grep 8532 | grep -v grep 23:46:07 502 8532 1 0 11:45下午 ?? 0:00.00 python test.py start [daemon] tail -f /tmp/daemon-example.log 23:46:20 Daemon started with pid 8532 Daemon Alive! Fri Dec 2 23:45:49 2016 Daemon Alive! Fri Dec 2 23:45:54 2016 Daemon Alive! Fri Dec 2 23:45:59 2016 Daemon Alive! Fri Dec 2 23:46:04 2016 Daemon Alive! Fri Dec 2 23:46:09 2016 Daemon Alive! Fri Dec 2 23:46:14 2016 Daemon Alive! Fri Dec 2 23:46:19 2016 Daemon Alive! Fri Dec 2 23:46:24 2016 Daemon Alive! Fri Dec 2 23:46:29 2016 Daemon Alive! Fri Dec 2 23:46:34 2016 [daemon] python test.py stop 23:46:36 [daemon] ps -ef|grep 8532 | grep -v grep 23:46:43
- Python全棧之進(jìn)程和守護(hù)進(jìn)程
- python 如何設(shè)置守護(hù)進(jìn)程
- Python 創(chuàng)建守護(hù)進(jìn)程的示例
- Python守護(hù)進(jìn)程實(shí)現(xiàn)過(guò)程詳解
- python實(shí)現(xiàn)守護(hù)進(jìn)程、守護(hù)線程、守護(hù)非守護(hù)并行
- Python如何實(shí)現(xiàn)守護(hù)進(jìn)程的方法示例
- python daemon守護(hù)進(jìn)程實(shí)現(xiàn)
- Python守護(hù)進(jìn)程用法實(shí)例分析
- Python實(shí)現(xiàn)日志備份守護(hù)進(jìn)程的示例
相關(guān)文章
python常用web框架簡(jiǎn)單性能測(cè)試結(jié)果分享(包含django、flask、bottle、tornado)
這篇文章主要介紹了python常用web框架簡(jiǎn)單性能測(cè)試結(jié)果分享(包含django、flask、bottle、tornado),需要的朋友可以參考下2014-08-08pandas通過(guò)字典生成dataframe的方法步驟
這篇文章主要介紹了pandas通過(guò)字典生成dataframe的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07Python二叉樹(shù)的遍歷操作示例【前序遍歷,中序遍歷,后序遍歷,層序遍歷】
這篇文章主要介紹了Python二叉樹(shù)的遍歷操作,結(jié)合實(shí)例形式分析了Python針對(duì)二叉樹(shù)的前序遍歷,中序遍歷,后序遍歷,層序遍歷等相關(guān)操作實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-12-12python flask中動(dòng)態(tài)URL規(guī)則詳解
今天小編就為大家分享一篇python flask中動(dòng)態(tài)URL規(guī)則詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11Python從文件中讀取指定的行以及在文件指定位置寫(xiě)入
這篇文章主要給大家介紹了關(guān)于Python從文件中讀取指定的行及在文件中指定位置寫(xiě)入的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Python具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09