參考《Linux/Unix 系統編程手冊》
變成daemon,一個程序需要完成以下步驟:
1、執行一個fork(),父進程退出,子進程繼續執行。(daemon成爲了init進程的子進程)
--假設daemon是從命令行啓動,父進程的終止會被shell發現,之後shell會顯示出另一個shell提示符並讓子進程在後臺運行;
--子進程被確保不會成爲一個進程組的首進程(它從父進程繼承了進程組ID,並擁有了自己唯一的進程ID,該進程ID與繼承來的進程組ID是不同的,這樣才能成功執行下面一個步驟)
2、子進程調用setsid()開啓一個新會話並釋放它與控制終端之間的所有關聯;
3、對於終端設備的處理:
--daemon從未打開過終端設備,不需做任何處理;
--daemon後面可能會打開一個終端設備,必須採取措施確保該設備不會成爲控制終端,具體措施:
1、在所有可能應用到一個終端設備上的open() 調用中指定O_NOCTTY標記;
2、在setsid()調用之後執行第二個fork(),再次讓父進程退出並讓孫子進程繼續執行,(這樣確保了子進程不會成爲會話組長,進程永遠不會重新請求一個控制終端--根據SystemV中獲取終端規則)
4、清除進程的umask(確保daemon創建文件和目錄時擁有必要的權限)
5、修改進程當前工作目錄(通常改爲根目錄 /,保證根目錄的文件系統不會被卸載即可,如:cron會將自身放在/var/spool/cron 目錄下)
6、關閉daemon從父進程繼承而來的所有打開着的文件描述符(由於daemon失去了控制終端,對0,1,2描述符完全可以關閉;無法卸載長時間運行的daemon打開的文件所在的文件系統----文件描述符是一種有限資源)
7、關閉0,1,2文件描述符後,daemon通常會打開/dev/null,並讓所有描述符都指向該設備
--這樣確保了當daemon調用在這些文件描述符上執行IO的庫函數時不會出乎意料的失敗;
實例:
import sys,os,time def main(): """ A demo daemon main routine, write a datestamp to /tmp/daemon-log every 10 seconds.""" f = open("/tmp/daemon-log", "w") while 1: f.write('%s/n' % time.ctime(time.time())) f.flush() time.sleep(10) if __name__ == "__main__": # do the UNIX double-fork magic try: pid = os.fork() if pid > 0: # exit first parent sys.exit(0) except OSError, e: print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror) sys.exit(1) # decouple from parent environment os.chdir("/") os.setsid() os.umask(0) # do second fork try: pid = os.fork() if pid > 0: # exit from second parent, print eventual PID before print "Daemon PID %d" % pid sys.exit(0) except OSError, e: print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror) sys.exit(1) # start the daemon main loop main()