python fork()建立新的程序,daemon程序
fork程序後的程式流程
使用fork建立子程序後,子程序會複製父程序的資料資訊,而後程式就分兩個程序繼續執行後面的程式,這也是fork(分叉)名字的含義了。在子程序內,這個方法會返回0;在父程序內,這個方法會返回子程序的編號PID。可以使用PID來區分兩個程序:
#!/usr/bin/env python #coding=utf8 import os #建立子程序之前宣告的變數 source = 10 try: pid = os.fork() if pid == 0: #子程序 print "this is child process." #在子程序中source自減1 source = source - 1 sleep(3) else: #父程序 print "this is parent process." print source except OSError, e: pass
上面程式碼中,在子程序建立前,聲明瞭一個變數source,然後在子程序中自減1,最後打印出source的值,顯然父程序打印出來的值應該為10,子程序打印出來的值應該為9。為了明顯區分父程序和子程序,讓子程序睡3秒,就看的比較明顯了。
既然子程序是父程序建立的,那麼父程序退出之後,子程序會怎麼樣呢?此時,子程序會被PID為1的程序接管,就是init程序了。這樣子程序就不會受終端退出影響了,使用這個特性就可以建立在後臺執行的程式,俗稱守護程序(daemon)。
---------------------
在我們載入了os模組之後,我們parent函式中fork()函式生成了一個子程序,返回值newpid有兩個,一個為0,用以表示子程序,一個是大於0的整數,用以表示父程序,這個常數正是子程序的pid. 通過print語句我們可以清晰看到兩個返回值。如果fork()返回值是一個負值,則表明子程序生成不成功(這個簡單程式中沒有考慮這種情況)。如果newpid==0,則表明我們進入到了子程序,也就是child()函式中,在子程序中我們輸出了自己的id和父程序的id。如果進入了else語句,則表明newpid>0,我們進入到父程序中,在父程序中os.getpid()得到自己的id,fork()返回值newpid表示了子程序的id,同時我們輸出了父程序的父程序的id. 通過實驗我們可以看到if和else語句的執行順序是不確定的,子、父程序的執行順序由作業系統的排程import os def child(): print 'A new child:', os.getpid() print 'Parent id is:', os.getppid() os._exit(0) def parent(): while True: newpid=os.fork() print newpid if newpid==0: child() else: pids=(os.getpid(),newpid) print "parent:%d,child:%d"%pids print "parent parent:",os.getppid() if raw_input()=='q': break parent()
----------
#!/usr/bin/env python
#coding=utf8
import os, sys, time
#產生子程序,而後父程序退出
pid = os.fork()
if pid > 0:
sys.exit(0)
#修改子程序工作目錄
os.chdir("/")
#建立新的會話,子程序成為會話的首程序
os.setsid()
#修改工作目錄的umask
os.umask(0)
#建立孫子程序,而後子程序退出
pid = os.fork()
if pid > 0:
sys.exit(0)
#重定向標準輸入流、標準輸出流、標準錯誤
sys.stdout.flush()
sys.stderr.flush()
si = file("/dev/null", 'r')
so = file("/dev/null", 'a+')
se = file("/dev/null", 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
#孫子程序的程式內容
while True:
time.sleep(10)
f = open('/home/test.txt', 'a')
f.write('hello')
上面的程式沒有任何錯誤處理,但是不影響原理分析。如果要應用到專案裡,還需完善。下面筆者談下自己對每個步驟的理解。
1、fork子程序,父程序退出
通常,我們執行服務端程式的時候都會通過終端連線到伺服器,成功連線後會載入shell環境,終端和shell都是程序,shell程序是終端程序的子程序,通過ps命令可以很容易的檢視到。在這個shell環境下一開始執行的程式都是shell程序的子程序,自然會受到shell程序的影響。在程式裡fork子程序後,父程序退出,對了shell程序來說,這個父程序就算執行完了,而產生的子程序會被init程序接管,從而也就脫離了終端的控制。
2、修改子程序的工作目錄
子程序在建立的時候會繼承父程序的工作目錄,如果執行的程式是在u盤裡的,就會導致u盤不能解除安裝。
3、建立新會話
使用setsid後,子程序就會成為新會話的首程序(session leader);子程序會成為新程序組的組長程序;子程序沒有控制終端。
4、修改umask
由於umask會遮蔽許可權,所以設定為0,這樣可以避免讀寫檔案時碰到許可權問題。
5、fork孫子程序,子程序退出
經過上面幾個步驟後,子程序會成為新的程序組老大,可以重新申請開啟終端,為了避免這個問題,fork孫子程序出來。
6、重定向孫子程序的標準輸入流、標準輸出流、標準錯誤流到/dev/null
因為是守護程序,本身已經脫離了終端,那麼標準輸入流、標準輸出流、標準錯誤流就沒有什麼意義了。所以都轉向到/dev/null,就是都丟棄的意思。