守護進程(Daemon)也稱爲精靈進程是一種生存期較長的一種進程。它們獨立於控制終端而且週期性的執行某種任務或等待處理某些發生的事件。他們經常在系統引導裝入時啓動,在系統關閉時終止。unix系統有不少守護進程,大多數服務器都是用守護進程實現的,例如inetd守護進程。html
可參考如下博文python
參考 APUE關於守護進程的章節git
大體流程以下:github
首次fork,建立父-子進程,使父進程退出服務器
經過setsid使子進程成爲process group leader、session leadersession
二次fork,建立子-孫進程,使sid不等pid測試
一般就關閉STDIN、STDOUT和STDERRui
防止佔用別的路徑的working dir的fd,致使一些block不能unmountspa
防止後續子進程繼承非默認umask形成奇怪的行爲.net
非必需
輸出重定向後,須要有機制放映內部狀況
第二個fork不是必須的,只是爲了防止進程打開控制終端。
打開一個控制終端的條件是該進程必須是session leader。第一次fork,setsid以後,子進程成爲session leader,進程能夠打開終端;第二次fork產生的進程,再也不是session leader,進程則沒法打開終端。
也就是說,只要程序實現得好,控制程序不主動打開終端,無第二次fork亦可。
# 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
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
也可使用 Supervisor 管理進程,具體可看 Supervisor安裝與配置