一、僵死進程
1.1僵死進程產生的原因
?????? 子進程先于父進程結束, 而父進程沒有獲取子進程退出碼,釋放子進程占用的資源,此時子進程將成為一個僵死進程。
在第一個框這里時父進程子進程都沒有結束,顯示其pid 父進程是2349,子進程是2350
在第二個框這里時父進程沒有結束,子進程結束,因此顯示父進程的pid,但是因為父進程沒有獲取子進程的退出碼,子進程就處于僵死狀態<defunct>
第三個框這里父進程結束,父進程成功獲取子進程的退出碼,子進程結束。
1.2 PCB消失的條件
?????? 獲取到退出碼,子進程要父進程獲取子進程的退出碼才能完全結束。
1.3危害
??????? 占用資源空間。如果進程不調用wait/waitpid,那么保留的那段信息就不會被釋放,其進程號就會一 直被占用,但是系統能使用的進程號是有限的,如果產生大量的僵死進程,將因為沒有可用的進程號而 導致系統不能產生新的進程。
1.4 如何處理
1.父進程調用wait()方法獲取子進程的退出碼
wait(&val):執行該指令,會將子進程的退出碼填到val中。
結果:
這個時候我們不難發現:先打印的都是子進程,父進程一點都沒有打印,當子進程結束才開始父進程,等val退出碼打印后才開始。
原因:wait,即先阻塞父進程,直至子進程運行結束,父進程獲取到子進程的退出碼后,父進程才繼續運行。
wait(&val):返回值pid_t,調用函數后會將退出碼通過指針賦值到val上。
WIFEXITED(val):由于退出碼為1字節,val為4字節,通過該函數可以將其轉化為1字節,返回值為bool類型,判斷進程是否正常結束。
WEXITSTATUS(val):獲取進程退出狀態。
2.父進程先結束----孤兒進程

結果
?在父進程結束后出現了提示符,但是此時子進程還沒有結束,所以還在執行
第一個框,父進程的id為2621,他的父進程是2135,子進程的id為2622,他的父進程為2621
父進程結束后,會隨機為子進程分配一個父進程,該父進程的id為1536,收養了這個孤兒進程
注意:老版本是 init進程,其進程號為1,新版本是隨機分配一個系統進程
init進程一定會對子進程執行wait()指令,獲取子進程的退出碼,子進程的pcb被操作系統刪除,至此子進程徹底結束。
1.5 練習
1.5.1? 練習一
結果
原因
板塊一:此時i=0,執行fork語句生成板塊2,打印第一個A,此時i++變成1,然后繼續循環,執行fork語句,生成板塊3(板塊三的i是從1開始的),打印第二個A,i++變成2,結束板塊一
板塊三:因為是子進程,從fork語句后執行,打印第一個A,i++變成2,結束板塊三
板塊二:也是子進程,從fork語句后執行,打印第一個A,i++變成1,繼續循環,執行fork語句生成板塊四(板塊四的i是從1開始的),打印第二個A,i++變成2,結束板塊二。
板塊四:也是子進程,打印第一個A,i++變成 2,結束板塊2.
總計:6個A
?1.5.2 練習二
?
結果
原因
注意一點:fork()執行完后,子進程從fork語句后執行
第一個板塊:執行fork語句生成板2,此時將打印的A放在緩沖區,為什么呢?可以看下linux day06講printf這塊,此時i++變成1,繼續循環,執行fork語句生成板塊3,(注意板塊三內容有原先父進程的緩沖區:A和i=1),然后執行printf,此時板塊1的緩沖區中有AA兩個,i++等于2,結束板塊1.
第三個板塊:因為復制了板塊1的,i從1開始,直接執行printf語句,緩沖區有AA兩個,i++變成2,j結束板塊3.
第二個板塊:因為是板塊1的子進程,從printf處執行,緩沖區有一個A,i++等于1,繼續for循環,執行fork語句形成板塊4,然后緩沖區:AA,i++->2,結束板塊2
第四個板塊:因為復制了板塊2,從printf處執行,緩沖區:AA,i++ ->2結束板塊4
所以總計8個A
?
?注意:練習一和練習二最大的區別在于緩存區的刷新
1.5.3 練習三
結果
?原因
首先執行fork()||fork()語句,復制了一份,因為父進程的id>0,根據或語句特點直接結束,打印第一個A。然后子進程中0||fork(),或語句如果第一個為1 就直接跳過了,但是為0 就要繼續執行后面語句,因此復制了一份代碼,由于第二個fork()返回了當前父進程的id,直接結束語句,打印第二個A。最后子進程的pid為0,所以返回值為0,0||0為假,打印最后一個A結束整個程序,所以打印三個A
?
二、操作文件的系統調用
在C語言中有fopen,fclose等文件操作
頭文件:#include<fcntl.h>
2.1 open()
?文件已存在:int open(const char *pathname, int flags);
?文件之前不存在,需要創建:int open(const char *pathname, int flags, mode_t mode);
pathname :將要打開的文件路徑和名稱flags : 打開標志,如 O_WRONLY 只寫打開??? O_RDONLY 只讀打開? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? O_RDWR 讀寫方式打開?? O_CREAT 文件不存在則創建? ?????????????????????????????????? O_APPEND 文件末尾追加? O_TRUNC 清空文件,重新寫入mode : 權限 如:“0600”? 0是八進制,6是4 r+2 w返回值: 為文件描述符
2.2 read()
從文件中提取數據
ssize_t read( int fd, void * buf, size_t count);fd 對應打開的文件描述符buf 存放數據的空間count 計劃一次從文件中讀多少字節數據?返回值 :為實際讀到的字節數
2.3 write()
從文件中寫入數據
ssize_t write( int fd, const void * buf, size_t count);參數介紹 :fd 對應打開的文件描述符buf 存放待寫入的數據count 計劃一次向文件中寫多少數據
2.4 close()
關閉文件
int close( int fd);參數介紹 :fd 要關閉的文件描述符
2.5 舉例
2.5.1 寫文件
?fd:文件描述符,通過編號id可以找到文件,0 標準輸入,1標準輸出,2標準錯誤輸出
所以fd值為3
2.5.2 讀文件
2.6? 父進程先打開一個文件,fork 后子進程是否可以共享使用?
2.6.1 文件表
?當不使用file.txt,把三關閉了,如果打開另外一個文件,則繼續復用三。(不使用就關閉,這樣就不會一直占用,使表不變大)
2.6.2 先open后fork
2.6.1.2 結果:
?父進程打開的文件,子進程也可以訪問,并且共享文件偏移量,如果想要關閉文件,需要父子進程都關閉文件。
2.6.1.3原因
父子進程共用一個結構體,引用計數為2,所以文件偏移量+1,那么子進程就輸出d,再+1,父進程輸出,所以結果出現四個偏移。
2.6.3 先fork后open
2.6.2.2 結果
?
2.6.2.3 原因
?
?因為父和子進程各有一個結構體,打開一個文件,其偏移量并不互相影響,所以都是從a開始。
三、系統調用與庫函數的區別
庫函數的實現在函數庫中,屬于用戶空間,系統調用的實現在內核中,屬于內核空間。
?