fork知識入門
python的os module中有fork()函數用于生成子程序,生成的子程序是父程序的鏡像,但是它們有各自的位址空間,子程序複制一份父程序記憶體給自己,兩個程序之間的執行是互相獨立的,其執行順序可以是不确定的、随機的、不可預測的,這點與多線程的執行順序相似。
import os
import time
try:
forkpid = os.fork()
time.sleep()
print type(forkpid)
except OSError:
sys.exit('Unable to fork.')
輸出如下
wayne@Z-Beatles:~/python$ python demo
<type 'int'>
<type 'int'>
3秒後可見fork的傳回值是兩個int型數值
python運作時建立程序
當python腳本運作,系統會生成一個新的程序。先看下面代碼:
from time import sleep
sleep()
因為代碼執行完後,程序就會被銷毀,是以這裡睡眠30秒,友善看到效果。再執行這個腳本檔案:
python testfork.py &
加上&符号,可以讓程式在背景運作,不會占用終端。輸入ps -l指令檢視程序,在電腦上輸出如下:
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
S - wait pts/ :: bash
S - poll_s pts/ :: python
R - - pts/ :: ps
其中第二條記錄就是剛才運作的python腳本了。
使用fork來建立一個新程序
使用fork建立一個新程序成功後,新程序會是原程序的子程序,原程序稱為父程序。如果發生錯誤,則會抛出OSError異常。
from time import sleep
import os
try:
pid = os.fork()
except OSError, e:
pass
sleep()
運作代碼後檢視程序,輸出如下:
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
S - wait pts/ :: bash
S - poll_s pts/ :: python
S - poll_s pts/ :: python
R - - pts/ :: ps
可以看出第二條python程序就是第一條的子程序。
fork程序後的程式流程
使用fork建立子程序後,子程序會複制父程序的資料資訊,而後程式就分兩個程序繼續運作後面的程式,這也是fork(分叉)名字的含義了。在子程序内,這個方法會傳回0;在父程序内,這個方法會傳回子程序的編号PID。可以使用PID來區分兩個程序:
import os
from time import sleep
source =
try:
pid = os.fork()
if pid == : #子程序
source = source -
sleep()
print "this is child process.source is %d" %source
else:
print "this is parent process.source is %d" %source
except OSError, e:
pass
上面代碼中,在子程序建立前,聲明了一個變量source,然後在子程序中自減1,最後列印出source的值,顯然父程序列印出來的值應該為10,4秒後子程序列印出來的值應該為9。
守護程序
既然子程序是父程序建立的,那麼父程序退出之後,子程序會怎麼樣呢?此時,子程序會被PID為1的程序接管,就是init程序了。這樣子程序就不會受終端退出影響了,使用這個特性就可以建立在背景執行的程式,俗稱守護程序(daemon)。
mark-守護程序
- linux python守護程序編寫
- Python執行個體淺談之五Python守護程序和腳本單例運作
- Linux守護程序設計規範及python實作
Tips
- fork()函數用來建立新的程序
- os.fork() 會有兩次傳回值,分别是父程序和子程序的傳回值
- 在父程序中,fork傳回的值是子程序的PID;
- 子程序中,這個傳回值為0
- 子程序會複制父程序的上下文
- 父子程序并不能确定執行順序
- os.getpid()傳回目前程序的ID,os.getppid()傳回目前程序的父程序的ID
- os.fork() 之後,子程序一定要使用 exit() 或者 os._exit() 來退出子程序環境,建議使用 os._exit()
- 可以使用os.waitpid(pid,0)來使父程序等待子程序執行完再執行父程序
waitpid()的使用
import os
import sys
import time
try :
forkPID1 = os.fork()
except OSError : # 如果作業系統不能建立程序,osfork()将會發出一個OSError異常
sys.exit('Unable to create first child.')
if forkPID1 != :
try :
forkPID2 = os.fork()
except OSError :
sys.exit('Unable to create first child.')
if forkPID2 > :
print 'Parent waiting for child precesses ...\n' + \
'\t tpid : %d , forkPID1 : %d , forkPID2 : %d' \
% ( os.getpid() , forkPID1 , forkPID2)
try :
child2 = os.waitpid (forkPID2 , )[]
except OSError :
sys.exit("No child process with pid %d." %(forkPID2) )
print 'Parent Child %d finished.' % child2
elif forkPID2 == :
print 'Chile2 sleeping fot 4 seconds ....\n' + \
'\tpid : %d , forkPID1: %d , forkPID2: %d' \
% (os.getpid() , forkPID1 , forkPID2 )
time.sleep() #規定程序保持的休眠時間 , 以秒為機關
elif forkPID1 == :
print 'Child1 sleeping for 2 seconds ....\n' + \
'\tpid : %d , forkPID1: %d' \
% (os.getpid() , forkPID1)
time.sleep()
- 這裡的’\’為‘反斜杠’or‘續航符’
嚴格地講, 在小括号, 方括号或大括号中的表達式 (如 定義一個 dictionary) 可以用或者不用續行符 (“\”) 分割成多行。甚至在不是必需的時候,也可以使用續行符,那可以讓代碼讀起來更容易。使用續行符隻是風格的問題。
3個示範輸出
Parent waiting for child precesses ...
tpid : , forkPID1 : , forkPID2 :
Child1 sleeping for seconds ....
pid : , forkPID1:
Chile2 sleeping fot seconds ....
pid : , forkPID1: , forkPID2:
Parent Child finished.
Parent waiting for child precesses ...
tpid : , forkPID1 : , forkPID2 :
Child1 sleeping for seconds ....
Chile2 sleeping fot seconds ....
pid : , forkPID1:
pid : , forkPID1: , forkPID2:
Parent Child finished.
Parent waiting for child precesses ...
tpid : , forkPID1 : , forkPID2 :
Child1 sleeping for seconds ....
pid : , forkPID1:
Chile2 sleeping fot seconds ....
pid : , forkPID1: , forkPID2:
Parent Child finished.