python之多進程fork


一:多進程概念

        python中實現多進程是通過os.fork()這個函數來實現,這個函數和操縱系統本身結合的非常緊密,windows下就無法使用os.fork()這個函數。python中的os.fork()被調用後就會立即生成一個子進程,是通過copy父進程的地址空間和資源來實現子進程的創建,同時這個函數在子進程中返回的是0,在父進程中返回的是子進程的PID。子進程在結束父進程還未結束的時候,子進程這段時間是處於Zombie狀態,可以通過ps命令查看到進程的狀態,Zombie的目的是爲了在子進程結束的時候可以保存一些結束信息供父進程來收集。


二:多進程的簡單實現

#!/usr/bin/env python
import os
def task(id):
        print "work %d" %id
pid = os.fork()
if pid == 0:
        print "I am a child"
        task(1)
else:
        print "I am father child"

執行結果如下:
I am father child
I am a child
work 1

多次執行上面的例子可能結果的順序會不一樣,子進程和父進程執行的順序是無法預估的。

os.fork()執行後父進程繼續往下執行,子進程也會從os.fork()語句之後開始運行,並且子進程擁有父進程所有的變量,但是兩者是獨立的。

#!/usr/bin/env python
import os
import time
def task(id):
        print "work %d" %id
var = 10
pid = os.fork()
if pid == 0:
        print "I am a child"
        var = 9
        task(1)
else:
        print "I am father child"
        time.sleep(3)
        print var
結果是:
I am father child
I am a child
work 1
10

上面這段代碼演示了子進程和父進程的var這個變量是獨立,在子進程中改變了var變量的值,並不影響父進程中的var變量值,兩者是獨立的,爲了防止父進程先執行,子進程後執行,所以讓父進程sleep(3)了一會再輸出var的值。子進程結束後,但是父進程還沒有結束的時候,子進程是出於Zombie狀態的,這個需要父進程去收集子進程的信息釋放子進程。如果父進程結束了子進程沒有結束,那麼子進程就會寄託給pid爲1的進程來管理。

#!/usr/bin/env python
import os
import time
pid = os.fork()
if pid == 0:
        print "I am a child %d" %os.getpid()
else:
        time.sleep(100)
        print "done"
輸出信息:
I am a child 57786
一直處於等待狀態......
done
在處於等待狀態的時候立刻使用ps命令查看子進程的狀態
[root@localhost ~]# ps ax|grep 57786
57786 pts/4    Z+     0:00 [python] <defunct>
57792 pts/5    S+     0:00 grep 57786
通過上面的狀態發現子進程處於defunct這個狀態就是Zombie狀態。

三:多進程處理Zombie方式一

對於處理Zimbie狀態的子進程有兩種方式,第一種是基於信號的處理,子進程在結束後就會發出SIGCHLD這個信號,通過singal接收到這個信號就可以進行收集了。

#!/usr/bin/env python
import os,time,signal
def chldhandler(signum,stackframe):
        while 1:
                try:
                        result = os.waitpid(-1,os.WNOHANG)
                except:
                        break
                print "Reaped child process %d" % result[0]
        signal.signal(signal.SIGCHLD,chldhandler)

signal.signal(signal.SIGCHLD,chldhandler)
print "Before the fork my PID is",os.getpid()

pid = os.fork()
if pid:
        print "hello from the parent the child will be PID %d" %pid
        print "Sleeping 10 sconds....."
        time.sleep(1000)
        print "sleep done"
else:
        print "Child sleeping 5 seconds"
        time.sleep(5)
執行結果:
Before the fork my PID is 58322
hello from the parent the child will be PID 58323
Sleeping 10 sconds.....
Child sleeping 5 seconds
Reaped child process 58323
sleep done
5秒過後子進程結束出發信號處理函數,對子進程進行了收集和處理。並且信號打斷了父進程的sleep

singal是一個信號量函數,chldhandlr是一個自定義的的信號處理的handler,通過singal可以註冊接收到什麼信號就自動觸發哪個handler

waitpid的第一個參數代表等待所有的子進程終止,第二個參數代表如果沒有已經終止的子進程就立即返回。waitpid的返回結果是一個進程的PID和退出信息組成的一個元組。在while循環結束後又重新註冊了信號處理函數這是因爲有些unix在信號處理程序結束後就失效了,無法再次處理其他子進程,


四:多進程處理Zombie方式二

第二中處理Zombie進程的方式是輪訓,父進程一段時間就去收集一下子進程,釋放Zombie進行。

#!/usr/bin/env python
import os,time
def reap():
        while 1:
                try:
                        result = os.waitpid(-1,os.WNOHANG)
                except:
                        break
                print "Reaped child process %d" % result[0]
        print "Before the fork,my PID is",os.getpid()
pid = os.fork()
if pid:
        print "Hello from the parent .The child will be PID %d" % pid
        print "Parent sleeping 60 seconds"
        time.sleep(60)
        print "Parent sleep done"
        reap()
        print "Parent sleeping 60 seconds"
        time.sleep(60)
        print "Parent sleep done"
else:
        print "Child sleeping 5 seconds..."
        time.sleep(5)
        print "Child terminating"

執行結果:
Parent sleeping 60 seconds
Child sleeping 5 seconds...
#等待5秒.......
Child terminating
#子進程結束了
#等待55秒父進程睡眠結束
Parent sleep done
#啓動reap()去採集子進程的信息,釋放子進程
Reaped child process 58595
Before the fork,my PID is 58594
Parent sleeping 60 seconds
Parent sleep done




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章