進程控制
寫時拷貝
本質是一種減少深拷貝的方法
Linux中有很多拷貝的場景都用得上寫時拷貝,下面以創建子進程時的寫時拷貝為例:
子進程被創建的時候:
會繼承父進程的mm_struct和頁表
所以子進程剛剛繼承時,父子進程的代碼和數據都是共享的
系統是如何知道子進程/父進程修改數據的時候要發生寫時拷貝的呢?
①父進程在使用fork創建子進程之前,就會把頁表中的數據區的權限改成r(只讀)
這樣子進程繼承到的頁表中的數據區就也是只讀的
②當子進程/父進程嘗試修改數據區中的數據時,就是修改只讀數據項,頁表就會報錯,就會觸發缺頁中斷
③系統發現缺頁中斷之后,就會檢測
1,如果發現用戶要修改的這個區域一定是只讀的,就把進程殺掉
2,如果這個區域一定是讀寫的,只是頁表中的權限設置成只讀的了,此時系統就會進行寫時拷貝
所以:不止創建子進程寫時拷貝的時候是這種原理
只要可能發生寫時拷貝的數據被頁表管理著,就都可以通過這個原理實現寫時拷貝
錯誤碼
用來判斷進程任務執行是否成功,如果失敗了錯誤是什么
main函數的返回值其實是錯誤碼,是返回給父進程/操作系統的
進程錯誤碼的的取值范圍是[0-255],即一個字節
因為使用wait/waitpid等待回收錯誤碼的時候,只給它留了8個比特位
進程中止
進程中止的方法:
①在main函數中return
②在任何地方使用exit函數[exit函數的參數就是錯誤碼,它的頭文件是stdio.h
]
③_exit使用方法和exit一模一樣
exit與_exit的區別:
①本質區別就是exit是用戶層的庫函數,_exit是內核層的系統調用
所以exit函數中,調用_exit中止進程之前,可以進行一些用戶層
的收尾工作
比如:
exit中止進程時,會刷新用戶級緩沖區和內核級緩沖區
也就是如果輸出緩沖區中有數據,使用exit中止它會幫我們打印出來
_exit中止進程時,不會
刷新用戶級緩沖區,只會刷新內核級緩沖區
②exit是庫里面的函數,是操作系統之外的操作,使用操作系統的資源時,只能對操作系統調用接口進行封裝
exit其實就封裝了_exit
_exit是系統調用接口,是操作系統內部的操作
所以:
其實我們之前再使用c/c++等高級語言時,所說的緩沖區都是語言級(用戶級)的緩沖區
這個緩沖區里面的內容是存儲在c/c++的標準庫中的,即存儲在共享區的
不是存儲在操作系統中的內核級緩沖區
不然_exit中止進程時,就也能把緩沖區中信息輸出/輸入
但是因為語言級緩沖區在操作系統的上層
,所以_exit中止進程后,內核緩沖區就關閉了
,語言緩沖區中的數據沒辦法進入操作系統了
操作系統沒看見語言緩沖區中傳來信息,就以為里面沒有
進程等待
wait(不常用)
頭文件:sys/types.h
和sys/wait.h
返回值:pid_t[小于0說明回收失敗,大于0時回收成功,返回的是回收的子進程的pid]
參數:
作用:等待并回收任意一個子進程
waitpid[常用]
頭文件:sys/types.h
和sys/wait.h
返回值:pid_t n
①n小于0,說明回收失敗/函數調用失敗
②n大于0,回收成功,返回的是成功回收的子進程的pid
③n等于0(只有非阻塞等待才會出現),則表示函數調用成功,但是子進程還沒執行完,還沒退出
參數1.pid_t pid
如果是子進程的pid,就表示指定等待這個子進程
如果是-1,就表示等待任意一個進程
參數2,int* status
如果傳nullptr則表示不需要獲取退出信息==
即:用戶自己定義一個int類型的變量
再把它的地址傳進waitpid里面,操作系統就會把進程的PCB中存儲的退出信息給status
status其實并不是一個整數,而是一個類似位圖的東西
因為進程結束,分兩種情況:
①正常退出,也就是通過main函數的return或者exit退出,這樣就可以返回退出碼
所以只有正常退出才能返回退出碼(錯誤碼)
②異常退出,也就是進程運行的途中,空指針訪問/野指針等直接導致進程崩潰了
這樣進程根本就運行不到返回退出碼的地方
但是異常退出,也會有自己的退出信號碼
[通過kill -l命令,可以看到Linux中所有的退出信號以及其對應的退出信號碼]
進程之所以會異常退出,是因為進程運行時出現了較嚴重的錯誤(野指針,除0等
)
操作系統識別到以后,在代碼還沒跑完的時候,就直接使用信號中止了進程
所以
status中不僅僅存儲了退出碼,還存儲了其他的退出信息
具體的:
status的:
①最低的7個比特位存儲退出信號值
②第8個比特位存儲core dump
標志
③第9~16個比特位存儲退出碼
宏:
WIFEXITED(status):
若為正常終止子進程返回的狀態,則為真(查看進程是否是正常退出)
WEXITSTATUS(status):
若WIFEXITED非零,提取子進程退出碼。(查看進程的退出碼)
參數3.標識是阻塞等待,還是非阻塞等待
如果該參數是0,表示阻塞式等待
如果為WNOHANG
,表示非阻塞式等待
使用子進程完成任務的好處
①非阻塞等待或者創建多個子進程時,父子進程可以同時運行
,父進程不用等子進程,提高并發度
②進程具有獨立性,所以如果子進程出了問題,不影響父進程
③數據快照,子進程被fork出來,繼承了父進程的PCB,頁表等東西之后
就獲取到了fork時父進程的數據
也就是對子進程從父進程那里獲取到的數據進行了快照
快照之后,其他任何進程對這一塊數據進行修改,這個子進程都“看不見”
因為寫時拷貝,或者說進程具有獨立性。所以數據快照之后,其他進程可以對這個數據任意修改