南寧網(wǎng)絡(luò)系統(tǒng)開發(fā)win10優(yōu)化大師是官方的嗎
在上一小節(jié)中我們介紹了進程的創(chuàng)建(fork)與退出(main函數(shù)的return與exit函數(shù))
并且要有一個意識,進程退出的時候只有三種情況:
1.進程退出,結(jié)果正確
2.進程退出,結(jié)果不正確
3.運行異常,收到信號退出
文章目錄
- 1.進程等待
- 2.wait/waitpid
- 1). wait
- 2). waitpid
- 2.進程等待如何完成它的任務(wù)
- 1). 進程等待解決僵尸進程內(nèi)存泄漏
- 2). 進程等待如何收到子進程退出信息?
- 3). 從系統(tǒng)層面大致了解父進程獲取子進程退出信息信息
- 3.阻塞等待
現(xiàn)在我們還需要認識一個函數(shù)_exit函數(shù)

可以看到它在二號手冊里,所以它是系統(tǒng)調(diào)用函數(shù)。它也能夠終止進程。用法跟exit函數(shù)一樣,那它與exit函數(shù)有什么區(qū)別呢?代碼演示一下:
exit函數(shù)

_exit函數(shù)
我們發(fā)現(xiàn)exit函數(shù)會先刷新緩沖區(qū)的內(nèi)容到屏幕上,然后再退出進程,但是
_exit函數(shù)是直接退出,沒有刷新緩沖區(qū)內(nèi)容,并且exit函數(shù)是標(biāo)準庫函數(shù),_exit函數(shù)是系統(tǒng)調(diào)用函數(shù),由此我們可以得到,exit函數(shù)是封裝了_exit函數(shù),內(nèi)部還有諸如刷新緩沖區(qū)這樣的其他的工作。
我們知道庫函數(shù),系統(tǒng)調(diào)用操作系統(tǒng)內(nèi)核是這樣的關(guān)系,exit函數(shù)刷新緩存區(qū),_exit函數(shù)沒有刷新緩存區(qū),那么我們也可以得到一個結(jié)論,我們所認知的緩存區(qū)不在操作系統(tǒng)內(nèi)部。這就是進程退出所用到的函數(shù)。
說明:雖然_exit的參數(shù)是int,但是僅有低8位可以被父進程所用。
所以_exit(-1)時,在終端執(zhí)行$?發(fā)現(xiàn)返回值是255。
1.進程等待
當(dāng)一個進程退出后沒有被回收,那么它就會進入僵尸狀態(tài),并且它的父進程不回收它的話,這個進程會一直存在,會發(fā)生內(nèi)存泄漏。那么進程等待的一個任務(wù)就是為了解決僵尸狀態(tài)伴隨的內(nèi)存泄漏。
那么什么是進程等待呢?
它其實是使用wait/waitpid的方式來實現(xiàn)父進程對子進程資源回收的等待過程。
為什么要進程等待?上面已經(jīng)說過
1.為了解決僵尸狀態(tài)不回收造成的內(nèi)存泄漏問題。
2.要知道子進程把任務(wù)完成的怎么樣,因為什么終止,退出(退出碼和接受信號),這一點不是必要的。
比如一些場景下父進程是不需要子進程把任務(wù)完成與否,子進程因為什么而退出、終止的。
2.wait/waitpid
1). wait
wait是一個系統(tǒng)調(diào)用函數(shù),它有一個參數(shù),我們稍后再說。它會回收任意一個子進程并會返回回收進程的id。
我們現(xiàn)在來展示一下任意這兩個字的含義:
我們這里創(chuàng)建了五個子進程,所以掉用五次wait。
可以看到操作系統(tǒng)調(diào)度進程是不確定的,wait回收進程也是不確定的。
2). waitpid
waitpid可以看到有三個參數(shù),第二個參數(shù)和第三個參數(shù)也先不說,那么顯然它是專門回收特定進程的,當(dāng)它的參數(shù)是下面紅框中的那樣的時候,它和wait沒有區(qū)別,當(dāng)waitpid第一個參數(shù)小于零的時候,它也是回收任意一個子進程。
2.進程等待如何完成它的任務(wù)
1). 進程等待解決僵尸進程內(nèi)存泄漏
我們先搞出一個僵尸進程:
然后使用wait回收這個進程:
2). 進程等待如何收到子進程退出信息?
這就談?wù)摰絯ait和waitpid中的int* status這個參數(shù)了。我們用C語言做題的時候,也會碰到這樣的接口:
這種叫做輸出型參數(shù),目的是調(diào)用這個接口帶回來某些東西,而這里的status帶回來的就是退出碼和子進程異常終止收到的信號。
話不多說代碼展示:
這里退出碼是0:
退出碼為10:
我們看到我們的退出碼是10,但是打印出來的是2560。
我們再用一個11號信號來終止子進程:
發(fā)現(xiàn)status的值又是10。
我們說了,這個變量可以接收到退出碼和異常終止的信號,而這兩個是兩個東西,所以我們的status不是一個單純的int變量,而是一個32個比特位的二進制數(shù)字,而我們也只用它的低十六位:
那我們現(xiàn)在,來驗證一下是否真的是我說的這樣呢,我們只需要對status進行位運算就可以:
確實是我們說的那樣。那么說明wait/waitpid確實可以接收退出碼和終止信號。
那我們可以這樣寫:
但是這里介紹兩個宏:
WIFEXITED(status) 若此值為非0 表明進程正常結(jié)束。 若上宏為真,此時可WEXITSTATUS(status)獲取進程退出狀態(tài)。
3). 從系統(tǒng)層面大致了解父進程獲取子進程退出信息信息
我們知道進程間具有獨立性,所以我們無法通過一個簡單的變量來獲取子進程的退出信息(因為同一個變量,但凡有一個進程改變它的值,都會發(fā)生寫時拷貝),所以需要系統(tǒng)調(diào)用來實現(xiàn)。
子進程在退出的時候會把代碼數(shù)據(jù)銷毀之后,將退出信息寫到自己的pcb結(jié)構(gòu)體中并將狀態(tài)改為Z,然后等待父進程回收,而父進程使用wait/waitpid回收后,才可以釋放子進程pcb。
3.阻塞等待
我們前面的的代碼都是父進程等到子進程結(jié)束后,再開始回收子進程,那直接回收呢?
我們看到,父進程回收的時候是一直在等待子進程結(jié)束之后才進行的回收,才會執(zhí)行后續(xù)的代碼,這就是阻塞等待。這就涉及到waitpid的第三個參數(shù):
當(dāng)這個參數(shù)值為0時,就是阻塞等待。
當(dāng)這個參數(shù)為WNOHANG(一個#define的常量)時,是非阻塞等待:
在這里我們發(fā)現(xiàn)它返回了一個值是0,但是應(yīng)該是子進程的id才對,這里返回值有了不一樣的解釋:
返回值大于0:等待成功
返回值等于0:等待成功,但是子進程還沒有結(jié)束
返回值小于0:等待失敗(例如等待一個不存在的進程)
所以當(dāng)我們使用非阻塞等待時需要以一個循環(huán)的方式多次等待,而我們也可以再這個循環(huán)里做一些父進程比較簡單的任務(wù)。