在之前的樹莓派網關項目中遇到了這樣一個問題,由於要把網關寫的Server持續運行,尤其是要加電自動開啟。發現ssh登錄開啟服務程序之后,當把pty退出時Server端自動斷開了,這里想到的APUE中第九章的內容,回顧了下關於會話首進程,進程組,控制終端的概念,所以我們需要把自己寫的Server端變為父進程為init(1)的守護進程。
--->首先想到的辦法是使用nohup命令,這里遇到一個坑:
當我們用nohup去處理shell腳本時是沒有問題的,但是在嘗試執行一個Python腳本時:
nohup python tcp_server.py > ser_log.out 2>&1 &
結果很出了問題:竟然查不到重定向的ser_log.out的輸出!后來發現:python的輸出有緩沖,導致ser_log.out並不能夠馬上看到輸出。
我們應該加 -u參數,使得python不啟用緩沖,如下:
nohup python -u tcp_server.py > ser_log.out 2>&1 &
#!/usr/bin/env python # coding:utf-8 import os,sys,time def daemon_init(stdin='/dev/null',stdout='/dev/null',stderr='/dev/null'): sys.stdin = open(stdin,'r') sys.stdout = open(stdout,'a+') sys.stderr = open(stderr,'a+') try: pid = os.fork() if pid > 0: #parrent os._exit(0) except OSError,e: sys.stderr.write("first fork failed!!"+e.strerror) os._exit(1) # 子進程, 由於父進程已經退出,所以子進程變為孤兒進程,由init收養 '''setsid使子進程成為新的會話首進程,和進程組的組長,與原來的進程組、控制終端和登錄會話脫離。''' os.setsid() '''防止在類似於臨時掛載的文件系統下運行,例如/mnt文件夾下,這樣守護進程一旦運行,臨時掛載的文件系統就無法卸載了,這里我們推薦把當前工作目錄切換到根目錄下''' os.chdir("/") '''設置用戶創建文件的默認權限,設置的是權限“補碼”,這里將文件權限掩碼設為0,使得用戶創建的文件具有最大的權限。否則,默認權限是從父進程繼承得來的''' os.umask(0) try: pid = os.fork() #第二次進行fork,為了防止會話首進程意外獲得控制終端 if pid > 0: os._exit(0) #父進程退出 except OSError,e: sys.stderr.write("second fork failed!!"+e.strerror) os._exit(1) # 孫進程 # for i in range(3,64): # 關閉所有可能打開的不需要的文件,UNP中這樣處理,但是發現在python中實現不需要。 # os.close(i) sys.stdout.write("Daemon has been created! with pid: %d\n" % os.getpid()) sys.stdout.flush() #由於這里我們使用的是標准IO,回顧APUE第五章,這里應該是行緩沖或全緩沖,因此要調用flush,從內存中刷入日志文件。 def main(): print '========main function start!============' #在調用daemon_init函數前是可以使用print到標准輸出的,調用之后就要用把提示信息通過stdout發送到日志系統中了 daemon_init('/dev/null','/tmp/daemon.log','/tmp/daemon.err') # 調用之后,你的程序已經成為了一個守護進程,可以執行自己的程序入口了 time.sleep(10) #daemon化自己的程序之后,sleep 10秒,模擬阻塞 if __name__ == '__main__': main()
這樣,通過調用daemon_init方法,就可以把自己的程序變為守護進程了,實現了nohup的功能。這樣做就更加靈活了,之后的事情就要具體問題具體分析了,比如針對自己實際的應用程序來決定是否要設置umask,是否要關閉不必要的文件描述符,是否要改變當前工作目錄等等。