Python 簡明教程 --- 26,Python 多進(jìn)程編程

學(xué)編程最有效的方法是動手敲代碼。

目錄

1矿咕,什么是多進(jìn)程

我們所寫的Python 代碼就是一個程序,Python 程序用Python 解釋器來執(zhí)行厌杜。程序是存儲在磁盤上的一個文件榕栏,Python 程序需要通過Python 解釋器將其讀入內(nèi)存,然后進(jìn)行解釋執(zhí)行胯陋。

處于執(zhí)行運(yùn)行)狀態(tài)的程序叫做進(jìn)程尸疆。進(jìn)程是由操作系統(tǒng)分配資源并進(jìn)行調(diào)度才能執(zhí)行。操作系統(tǒng)會為每個進(jìn)程分配進(jìn)程ID(非負(fù)整數(shù))惶岭,作為進(jìn)程的唯一標(biāo)識寿弱。

現(xiàn)代操作系統(tǒng)都提供了多進(jìn)程同步執(zhí)行的機(jī)制,也就是操作系統(tǒng)允許多個進(jìn)程同時運(yùn)行按灶。操作系統(tǒng)負(fù)責(zé)進(jìn)程的管理工作症革。比如我們在處理word 文檔的同時還在聽音樂,這就需要有一個word 程序和一個音樂軟件在同步運(yùn)行鸯旁。

多進(jìn)程機(jī)制的硬件支持是由CPU 提供的噪矛,CPU 有單核多核之分。

單核CPU 只有一個核心铺罢,在同一時刻只能有一個進(jìn)程在執(zhí)行艇挨,單核CPU 上的多個進(jìn)程的執(zhí)行,實(shí)際上是并發(fā)執(zhí)行韭赘。其背后的原理是缩滨,CPU 的運(yùn)行速度是相當(dāng)快的,多進(jìn)程執(zhí)行實(shí)際上是每個進(jìn)程間隔運(yùn)行泉瞻,而間隔的時間非常短脉漏,人類是無法察覺到這種間隔的袖牙,這樣侧巨,人類感覺起來就像多個進(jìn)程同時執(zhí)行一樣。

多核CPU 有多個核心鞭达,每個核心都可以處理進(jìn)程司忱,這樣每個進(jìn)程都可以運(yùn)行在不同的CPU 上皇忿,這叫做并行執(zhí)行,是真正的在同一時刻運(yùn)行坦仍。

2禁添,fork 函數(shù)

Python 語言也支持多進(jìn)程編程,以此來支持更加復(fù)雜的桨踪,高性能的應(yīng)用老翘。

為了支持多進(jìn)程編程,操作系統(tǒng)提供了最原始的系統(tǒng)調(diào)用fork() 函數(shù)锻离,使得當(dāng)前進(jìn)程可以創(chuàng)建出一個子進(jìn)程铺峭,這樣父進(jìn)程和子進(jìn)程就可以處理不同的事務(wù)。

Python 中的fork() 函數(shù)被封裝在os 模塊中汽纠,該函數(shù)原型很簡單卫键,沒有任何參數(shù),如下:

fork()

與一般函數(shù)不同的是虱朵,該函數(shù)的返回值比較特殊莉炉,fork 函數(shù)執(zhí)行一次,返回兩次值:

  • 返回值為0: 為子進(jìn)程范圍碴犬,子進(jìn)程可通過getppid() 函數(shù)得到父進(jìn)程ID
  • 返回值為子進(jìn)程ID: 為父進(jìn)程范圍絮宁,這樣父進(jìn)程可得到子進(jìn)程ID

示例:

#! /usr/bin/env python3

import os

# 這里是父進(jìn)程
# 創(chuàng)建子進(jìn)程
pid = os.fork()

if pid == 0:
    # 子進(jìn)程范圍,編寫子進(jìn)程需要處理的事務(wù)
    print('這里是子進(jìn)程服协,父進(jìn)程ID 為:%s绍昂,子進(jìn)程ID 為:%s' % (
        os.getppid(), os.getpid()))
else:
    # 父進(jìn)程范圍,編寫父進(jìn)程需要處理的事務(wù)
    print('這里是父進(jìn)程, 父進(jìn)程ID 為:%s, 子進(jìn)程ID 為:%s' % (
        os.getpid(), pid))

# 父進(jìn)程和子進(jìn)程都會執(zhí)行到這里
print('進(jìn)程ID:%s' % os.getpid())

在上面代碼中偿荷,我們調(diào)用了fork() 函數(shù)窘游,返回值為pid

  • pid 為0 時: 進(jìn)入了子進(jìn)程范圍,我們使用getppid() 函數(shù)獲取了父進(jìn)程ID跳纳,使用getpid() 函數(shù)獲取了當(dāng)前進(jìn)程(子進(jìn)程)ID
  • pid 不為0 時: 進(jìn)入了父進(jìn)程范圍忍饰,此時pid 就是子進(jìn)程ID,我們使用getpid() 函數(shù)獲取了當(dāng)前進(jìn)程(父進(jìn)程)ID

代碼的最后一行print('進(jìn)程ID:%s' % os.getpid())寺庄,父進(jìn)程和子進(jìn)程都會執(zhí)行到艾蓝。

這段代碼的執(zhí)行結(jié)果如下:

$ python3 Test.py 
這里是父進(jìn)程, 父進(jìn)程ID 為:1405, 子進(jìn)程ID 為:1406
進(jìn)程ID:1405   # 最后一行代碼的輸出
這里是子進(jìn)程,父進(jìn)程ID 為:1405铣揉,子進(jìn)程ID 為:1406
進(jìn)程ID:1406   # 最后一行代碼的輸出

從上面的執(zhí)行結(jié)果饶深,我們可以看到,父進(jìn)程ID 為 1405逛拱,子進(jìn)程ID 為1406

最后一行代碼台猴,子進(jìn)程和父進(jìn)程都能執(zhí)行到的原因是朽合,在執(zhí)行了fork() 函數(shù)后俱两,之后的代碼就同時存在于兩個進(jìn)程(父子進(jìn)程)空間中。返回值pid0 時曹步,是子進(jìn)程空間宪彩;返回值pid 不為0 時,是父進(jìn)程空間讲婚。

而最后一行代碼尿孔,即屬于pid == 0 的范圍,又屬于else 的范圍筹麸,所以父子進(jìn)程都會執(zhí)行該代碼活合。

3,孤兒進(jìn)程與僵尸進(jìn)程

我們已經(jīng)知道物赶,在fork() 函數(shù)之后白指,就會有兩個進(jìn)程,分別是父進(jìn)程子進(jìn)程酵紫。那這兩個進(jìn)程是哪個先執(zhí)行呢告嘲?是父進(jìn)程先于子進(jìn)程執(zhí)行,還是子進(jìn)程先于父進(jìn)程執(zhí)行奖地?

答案是不確定橄唬。因?yàn)楦缸舆M(jìn)程哪個先執(zhí)行不是程序能夠決定的,而是由操作系統(tǒng)的調(diào)度決定的参歹,操作系統(tǒng)先調(diào)度到誰轧坎,誰就先執(zhí)行。

另外泽示,在父子進(jìn)程退出時缸血,由于退出的先后順序不一樣,也會造成孤兒進(jìn)程僵尸進(jìn)程

  • 孤兒進(jìn)程:父進(jìn)程先于子進(jìn)程退出械筛,子進(jìn)程會變成孤兒進(jìn)程捎泻。孤兒進(jìn)程會被系統(tǒng)進(jìn)程接管,系統(tǒng)進(jìn)程變成孤兒進(jìn)程的父進(jìn)程埋哟。在孤兒進(jìn)程退出時笆豁,系統(tǒng)進(jìn)程會進(jìn)行處理。
  • 僵尸進(jìn)程:如果子進(jìn)程退出時赤赊,其父進(jìn)程沒有處理子進(jìn)程的退出狀態(tài)闯狱,那么這個進(jìn)程退出后,其占用的系統(tǒng)資源就不會釋放抛计,也就是哄孤,這個進(jìn)程即不進(jìn)行正常的工作,卻依然占用系統(tǒng)資源吹截,這樣的進(jìn)程叫做僵尸進(jìn)程瘦陈。

下面我們編寫一段會產(chǎn)生僵尸進(jìn)程的代碼:

#! /usr/bin/env python3

import os
import time

# 這里是父進(jìn)程
# 創(chuàng)建子進(jìn)程
pid = os.fork()

if pid == 0:
    # 子進(jìn)程范圍凝危,編寫子進(jìn)程需要處理的事務(wù)
    print('這里是子進(jìn)程,父進(jìn)程ID 為:%s晨逝,子進(jìn)程ID 為:%s' % (
        os.getppid(), os.getpid()))
else:
    # 父進(jìn)程范圍蛾默,編寫父進(jìn)程需要處理的事務(wù)
    print('這里是父進(jìn)程, 父進(jìn)程ID 為:%s, 子進(jìn)程ID 為:%s' % (
        os.getpid(), pid))
        
    print('父進(jìn)程正在sleep 600S...')
    time.sleep(600)

# 父進(jìn)程和子進(jìn)程都會執(zhí)行到這里
print('進(jìn)程ID:%s' % os.getpid())

上面的代碼中,我們在父進(jìn)程中sleep600 秒捉貌,這樣支鸡,子進(jìn)程會先于父進(jìn)程退出,而父進(jìn)程沒有處理子進(jìn)程的退出狀態(tài)趁窃,這必然造成子進(jìn)程變?yōu)榻┦M(jìn)程牧挣。

我們使用python3 執(zhí)行該程序,如下:

$ python3 Test.py 
這里是父進(jìn)程, 父進(jìn)程ID 為:1524, 子進(jìn)程ID 為:1525
父進(jìn)程正在sleep 600S...
這里是子進(jìn)程棚菊,父進(jìn)程ID 為:1524浸踩,子進(jìn)程ID 為:1525
進(jìn)程ID:1525
`注意,這里父進(jìn)程在sleep统求,程序并沒有退出`

從上面的輸出检碗,我們可以知道,父進(jìn)程ID 為 1524码邻,子進(jìn)程ID 為1525折剃。

然后,我們用ps 命令像屋,來查看當(dāng)前的python3 進(jìn)程怕犁,如下:

$ ps -aux| grep python3
1      2    3    4     5      6     7      8      9      10          11
wp   1524  1.0  0.0  23992  6604  pts/2   `S`   09:13   0:00  python3 Test.py
wp   1525  0.0  0.0      0     0  pts/2   `Z`   09:13   0:00  [python3] <defunct>

(為了方便查看,我在上面的輸出中添加了列數(shù)己莺,共11 列奏甫。)

其中第 2 列為進(jìn)程ID,第 8 列為進(jìn)程狀態(tài)凌受。我們看到父進(jìn)程(1524)處于S 狀態(tài)(即休眠狀態(tài))阵子,子進(jìn)程(1525)處于Z 狀態(tài)(即僵尸狀態(tài))。

這說明胜蛉,子進(jìn)程先于父進(jìn)程退出挠进,而父進(jìn)程又沒有處理子進(jìn)程的退出狀態(tài),所以使得子進(jìn)程變?yōu)榱?code>僵尸進(jìn)程誊册。

4领突,避免僵尸進(jìn)程

孤兒進(jìn)程不會造成什么危害,而僵尸進(jìn)程會造成系統(tǒng)資源浪費(fèi)案怯,所以僵尸進(jìn)程是應(yīng)該被避免的情況君旦。

既然僵尸進(jìn)程會導(dǎo)致資源浪費(fèi)的情況,那么操作系統(tǒng)為什么還要設(shè)計(jì)僵尸進(jìn)程的存在呢?

僵尸進(jìn)程存在的意義是保存了進(jìn)程退出時的一些狀態(tài)于宙,比如進(jìn)程ID浮驳,終止?fàn)顟B(tài)悍汛,資源使用情況等信息捞魁,這些信息都可以讓其父進(jìn)程獲取到,來做適當(dāng)?shù)奶幚怼?/p>

所以离咐,在子進(jìn)程退出后谱俭,只有經(jīng)過父進(jìn)程的處理才能避免僵尸進(jìn)程的出現(xiàn)。

wait 函數(shù)

父進(jìn)程可以通過wait() 函數(shù)來獲取子進(jìn)程的退出狀態(tài)宵蛀。需要說明的是昆著,調(diào)用wait() 函數(shù)的進(jìn)程將會阻塞,直到該進(jìn)程的某個子進(jìn)程退出术陶。

wait 函數(shù)原型如下:

wait()
`
該函數(shù)返回一個元組(pid, status)
pid 為退出進(jìn)程的ID
status 為退出進(jìn)程的狀態(tài)
`

父進(jìn)程調(diào)用wait() 函數(shù)有兩種情況凑懂,這兩種情況都會正確的避免僵尸進(jìn)程的出現(xiàn):

  • 父進(jìn)程在子進(jìn)程退出調(diào)用wait()
  • 父進(jìn)程在子進(jìn)程退出調(diào)用wait()

我們分別對這兩種情況進(jìn)行代碼演示,通過sleep 函數(shù)來控制哪個進(jìn)程先退出:

  1. 父進(jìn)程在子進(jìn)程退出調(diào)用wait()

代碼:

#! /usr/bin/env python3

import os
import time

# 這里是父進(jìn)程
# 創(chuàng)建子進(jìn)程
pid = os.fork()

if pid == 0:
    # 子進(jìn)程調(diào)用sleep梧宫,保證父進(jìn)程先調(diào)用wait
    print('這里是子進(jìn)程, 父進(jìn)程pid:%s, 子進(jìn)程pid:%s sleep 5 秒' % (
        os.getppid(), os.getpid()
        ))
    time.sleep(5)

else:
    # 父進(jìn)程調(diào)用wait接谨,且出阻塞在這里
    child_pid, child_status = os.wait()
    print('這里是父進(jìn)程, 父進(jìn)程pid:%s, 子進(jìn)程pid:%s, 子進(jìn)程退出狀態(tài):%s' % (
        os.getpid(), child_pid, child_status))

    print('父進(jìn)程sleep 600 秒, 此時用 ps 命令查看進(jìn)程狀態(tài)')
    time.sleep(600)

該代碼的執(zhí)行結(jié)果如下:

$ python3 Test.py 
這里是子進(jìn)程, 父進(jìn)程pid:1585, 子進(jìn)程pid:1586 sleep 5 秒
這里是父進(jìn)程, 父進(jìn)程pid:1585, 子進(jìn)程pid:1586, 子進(jìn)程退出狀態(tài):0
父進(jìn)程sleep 600 秒, 此時用 ps 命令查看進(jìn)程狀態(tài)

當(dāng)打印出父進(jìn)程sleep 600 秒, 此時用 ps 命令查看進(jìn)程狀態(tài) 這句話時,證明子進(jìn)程已經(jīng)退出塘匣,我們用ps 命令查看python3 進(jìn)程狀態(tài)脓豪,如下:

$ ps -aux| grep python3
1     2    3    4     5      6    7    8    9     10          11
wp  1585  0.0  0.0  23992  6604 pts/2  S  10:10  0:00  python3 Test.py

可見此時只有父進(jìn)程存活,子進(jìn)程已經(jīng)成功退出忌卤,沒有處于僵尸進(jìn)程狀態(tài)扫夜。

  1. 父進(jìn)程在子進(jìn)程退出調(diào)用wait()

代碼:

#! /usr/bin/env python3

import os
import time

# 這里是父進(jìn)程
# 創(chuàng)建子進(jìn)程
pid = os.fork()

if pid == 0:
    # 子進(jìn)程范圍
    print('這里是子進(jìn)程, 父進(jìn)程pid:%s, 子進(jìn)程pid:%s' % (
        os.getppid(), os.getpid()
        ))

else:
    # 父進(jìn)程先 sleep,保證子進(jìn)程先退出驰徊,然后再調(diào)用 wait
    time.sleep(5)

    child_pid, child_status = os.wait()
    print('這里是父進(jìn)程, 父進(jìn)程pid:%s, 子進(jìn)程pid:%s, 子進(jìn)程退出狀態(tài):%s' % (
        os.getpid(), child_pid, child_status))

    print('父進(jìn)程sleep 600 秒, 此時用 ps 命令查看進(jìn)程狀態(tài)')
    time.sleep(600)

該代碼執(zhí)行結(jié)果如下:

$ python3 Test.py 
這里是子進(jìn)程, 父進(jìn)程pid:1591, 子進(jìn)程pid:1592
這里是父進(jìn)程, 父進(jìn)程pid:1591, 子進(jìn)程pid:1592, 子進(jìn)程退出狀態(tài):0
父進(jìn)程sleep 600 秒, 此時用 ps 命令查看進(jìn)程狀態(tài)

當(dāng)打印出父進(jìn)程sleep 600 秒, 此時用 ps 命令查看進(jìn)程狀態(tài) 這句話時笤闯,我們用ps 命令查看python3 進(jìn)程狀態(tài),如下:

執(zhí)行結(jié)果:

$ ps -aux| grep python3
1     2    3    4     5      6    7    8    9     10          11
wp  1591  0.2  0.0  23992  6620 pts/2  S  10:20  0:00  python3 Test.py

可見此時只有父進(jìn)程存活棍厂,子進(jìn)程已經(jīng)成功退出颗味,沒有處于僵尸進(jìn)程狀態(tài)。

5勋桶,使用信號處理僵尸進(jìn)程

因?yàn)?code>wait() 函數(shù)會導(dǎo)致調(diào)用進(jìn)程阻塞脱衙,那就使得調(diào)用進(jìn)程無法處理別的事情。這其實(shí)不是很合理例驹,因?yàn)榘装桌速M(fèi)了一個進(jìn)程捐韩。

這種情況我們可以使用信號來處理。

信號是一種系統(tǒng)中斷鹃锈,當(dāng)進(jìn)程遇到系統(tǒng)中斷時荤胁,就會打斷進(jìn)程正在執(zhí)行的正常流程,轉(zhuǎn)而去處理中斷函數(shù)屎债。進(jìn)程處理完中斷函數(shù)后仅政,又會回到進(jìn)程原來的處理流程垢油。

中斷函數(shù)是用戶向系統(tǒng)注冊的一個函數(shù),用于在遇到某個信號時圆丹,要做哪些處理滩愁。

因?yàn)樽舆M(jìn)程在退出時會向父進(jìn)程發(fā)送SIGCHLD 信號,所以父進(jìn)程可以通過捕獲該信號來處理子進(jìn)程辫封。

signal 模塊

在Linux 系統(tǒng)中硝枉,我們可以通過kill -l 命令來查看系統(tǒng)中的信號,共64 個信號:

$ kill -l
 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP
 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR
31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

在Python 中通過signal 模塊來處理信號倦微,我們通過dir(signal) 來查看signal 模塊都有哪些內(nèi)容:

>>> dir(signal)
['Handlers', 'ITIMER_PROF', 'ITIMER_REAL', 
'ITIMER_VIRTUAL', 'ItimerError', 'NSIG', 
'SIGABRT', 'SIGALRM', 'SIGBUS', 'SIGCHLD', 
'SIGCLD', 'SIGCONT', 'SIGFPE', 'SIGHUP', 
'SIGILL', 'SIGINT', 'SIGIO', 'SIGIOT', 
'SIGKILL', 'SIGPIPE', 'SIGPOLL', 'SIGPROF', 
'SIGPWR', 'SIGQUIT', 'SIGRTMAX', 'SIGRTMIN', 
'SIGSEGV', 'SIGSTOP', 'SIGSYS', 'SIGTERM', 
'SIGTRAP', 'SIGTSTP', 'SIGTTIN', 'SIGTTOU', 
'SIGURG', 'SIGUSR1', 'SIGUSR2', 'SIGVTALRM', 
'SIGWINCH', 'SIGXCPU', 'SIGXFSZ', 'SIG_BLOCK', 
'SIG_DFL', 'SIG_IGN', 'SIG_SETMASK', 
'SIG_UNBLOCK', 'Sigmasks', 'Signals', 
'_IntEnum', '__builtins__', '__cached__', 
'__doc__', '__file__', '__loader__', 
'__name__', '__package__', '__spec__', 
'_enum_to_int', '_int_to_enum', '_signal', 
'alarm', 'default_int_handler', 'getitimer', 
'getsignal', 'pause', 'pthread_kill', 
'pthread_sigmask', 'set_wakeup_fd', 'setitimer', 
'siginterrupt', 'signal', 'sigpending', 
'sigtimedwait', 'sigwait', 'sigwaitinfo', 
'struct_siginfo']

可以看到妻味,signal 模塊中包含了一些信號相關(guān)函數(shù),和絕大部分信號欣福。

signal 函數(shù)

要想處理信號责球,則需要使用signal 模塊中的signal 函數(shù)向系統(tǒng)注冊,捕獲哪個信號拓劝,以及處理該信號的函數(shù)雏逾。

signal 函數(shù)原型如下:

signal(signalnum, handler)
  • 該函數(shù)接收兩個參數(shù),分別是signalnumhandler
  • signalnum 是要捕獲的信號
  • handler 是信號處理函數(shù)

handler 參數(shù)有三種取值:

  • SIG_DFL:表示系統(tǒng)設(shè)置的默認(rèn)值
  • SIG_IGN:表示忽略該信號
  • 一個函數(shù)類型的參數(shù):該函數(shù)接收兩個參數(shù)分別是信號編號當(dāng)前的棧幀

接下來凿将,我們編寫代碼校套,用信號來處理僵尸進(jìn)程。

示例代碼:

#! /usr/bin/env python3

import os
import time
import signal

# 這里是父進(jìn)程

# 信號處理函數(shù)
# 該函數(shù)須有兩個參數(shù)
def sig_handelr(signum, frame):
    # print(frame)

    # 父進(jìn)程中調(diào)用 wait 來處理子進(jìn)程
    child_pid, child_status = os.wait()
    print('這里是父進(jìn)程, 接收到了信號:%s, 此時用 ps 命令查看進(jìn)程狀態(tài)牧抵。父進(jìn)程pid:%s, 子進(jìn)程pid:%s, 子進(jìn)程退出狀態(tài):%s' % (
        signum, os.getpid(), child_pid, child_status))

# 父進(jìn)程注冊信號處理函數(shù)
signal.signal(signal.SIGCHLD, sig_handelr)

# 創(chuàng)建子進(jìn)程
pid = os.fork()

if pid == 0:
    # 子進(jìn)程范圍

    print('這里是子進(jìn)程, 父進(jìn)程pid:%s, 子進(jìn)程pid:%s, 子進(jìn)程 sleep 10 秒' % (
        os.getppid(), os.getpid()
        ))

    # 先讓子進(jìn)程sleep 10 秒笛匙,然后退出
    time.sleep(10)

else:
    print('這里是父進(jìn)程, 父進(jìn)程sleep 600 秒, 保證子進(jìn)程先退出')
    time.sleep(600)

注意:信號處理函數(shù)signal 的調(diào)用,一定要在fork 函數(shù)之前犀变。

執(zhí)行結(jié)果如下:

$ python3 Test.py 
這里是父進(jìn)程, 父進(jìn)程sleep 600 秒, 保證子進(jìn)程先退出
這里是子進(jìn)程, 父進(jìn)程pid:1651, 子進(jìn)程pid:1652, 子進(jìn)程 sleep 10 秒
這里是父進(jìn)程, 接收到了信號:17, 此時用 ps 命令查看進(jìn)程狀態(tài)妹孙。父進(jìn)程pid:1651, 子進(jìn)程pid:1652, 子進(jìn)程退出狀態(tài):0
`這里程序并沒有退出,因?yàn)楦高M(jìn)程在sleep 600 秒`

等待子進(jìn)程sleep10 秒获枝,退出之后蠢正,我們用ps 命令查看進(jìn)程狀態(tài):

ps -aux| grep python3
1    2     3    4     5      6    7     8     9      10          11
wp  1651  0.0  0.0  23992  6708 pts/2   S   21:38   0:00  python3 Test.py

通過ps 命令可以看出,在子進(jìn)程退出之后省店,并沒有變成僵尸進(jìn)程嚣崭,說明我們的處理沒有問題。

6懦傍,忽略SIGCHLD 信號

更簡單處理辦法是直接將SIGCHLD 信號忽略掉雹舀,而不需要為信號注冊處理函數(shù)忽略信號也是處理信號的一種粗俱,同樣不會使子進(jìn)程變成僵尸進(jìn)程说榆。

代碼如下:

#! /usr/bin/env python3

import os
import time
import signal

# 這里是父進(jìn)程
# 父進(jìn)程注冊信號,處理方法是忽略
signal.signal(signal.SIGCHLD, signal.SIG_IGN)

# 創(chuàng)建子進(jìn)程
pid = os.fork()

if pid == 0:
    # 子進(jìn)程范圍
    print('這里是子進(jìn)程, 父進(jìn)程pid:%s, 子進(jìn)程pid:%s, 子進(jìn)程 sleep 10 秒' % (
        os.getppid(), os.getpid()
        ))

    # 先讓子進(jìn)程sleep 10 秒,然后退出
    time.sleep(10)

else:
    print('這里是父進(jìn)程, 父進(jìn)程sleep 600 秒, 保證子進(jìn)程先退出')
    time.sleep(600)

我們將signal 函數(shù)的第二個參數(shù)設(shè)置為signal.SIG_IGN签财,意思是忽略掉信號串慰。

執(zhí)行結(jié)果如下:

$ python3 Test.py 
這里是父進(jìn)程, 父進(jìn)程sleep 600 秒, 保證子進(jìn)程先退出
這里是子進(jìn)程, 父進(jìn)程pid:1659, 子進(jìn)程pid:1660, 子進(jìn)程 sleep 10 秒
`這里程序并沒有退出,因?yàn)楦高M(jìn)程在sleep 600 秒`

我們再用 ps 命令輸出如下:

$ ps -aux| grep python3
1     2    3    4     5      6     7     8     9     10         11
wp  1659  0.1  0.0  23992  6688  pts/2   S   21:57  0:00  python3 Test.py

可以看到唱蒸,子進(jìn)程依然沒有變成僵尸進(jìn)程邦鲫。

(完。)


推薦閱讀:

Python 簡明教程 ---21油宜,Python 繼承與多態(tài)
Python 簡明教程 ---22掂碱,Python 閉包與裝飾器
Python 簡明教程 ---23怜姿,Python 異常處理
Python 簡明教程 ---24慎冤,Python 文件讀寫
Python 簡明教程 ---25,Python 目錄操作

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沧卢,一起剝皮案震驚了整個濱河市蚁堤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌但狭,老刑警劉巖披诗,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異立磁,居然都是意外死亡呈队,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門唱歧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宪摧,“玉大人,你說我怎么就攤上這事颅崩〖赣冢” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵沿后,是天一觀的道長沿彭。 經(jīng)常有香客問我,道長尖滚,這世上最難降的妖魔是什么喉刘? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮漆弄,結(jié)果婚禮上睦裳,老公的妹妹穿的比我還像新娘。我一直安慰自己置逻,他們只是感情好推沸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般鬓催。 火紅的嫁衣襯著肌膚如雪肺素。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天宇驾,我揣著相機(jī)與錄音倍靡,去河邊找鬼。 笑死课舍,一個胖子當(dāng)著我的面吹牛塌西,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筝尾,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼捡需,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了筹淫?” 一聲冷哼從身側(cè)響起站辉,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎损姜,沒想到半個月后饰剥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摧阅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年汰蓉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棒卷。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡顾孽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出娇跟,到底是詐尸還是另有隱情岩齿,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布苞俘,位于F島的核電站盹沈,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏吃谣。R本人自食惡果不足惜乞封,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望岗憋。 院中可真熱鬧肃晚,春花似錦、人聲如沸仔戈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晋修,卻和暖如春吧碾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背墓卦。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工倦春, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人落剪。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓睁本,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忠怖。 傳聞我的和親對象是個殘疾皇子呢堰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容