進程創建
fork函數初識
在linux中fork函數是非常重要的函數,它從已存在進程中創建?個新進程。新進程為子進程,而原進程為父進程。
進程調用fork,當控制轉移到內核中的fork代碼后,內核做:
? 分配新的內存塊和內核數據結構給子進程
? 將父進程部分數據結構內容拷貝至子進程
? 添加子進程到系統進程列表當中
? fork返回,開始調度器調度
當一個進程調用fork之后,就有兩個二進制代碼相同的進程。而且它們都運行到相同的地方。但每個進程都將可以開始它們自己的旅程,看如下程序。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main( void )
{pid_t pid;printf("Before: pid is %d\n", getpid());if ( (pid=fork()) == -1 )perror("fork()"),exit(1);printf("After:pid is %d, fork return %d\n", getpid(), pid);sleep(1);return 0;
}
運行結果如下:
這里看到了三行輸出,一行 before,兩行 after。進程 608813 先打印 before 消息,然后它有打印 after。 另一個 after 消息有 608814打印的。注意到進程 608814 沒有打印 before,為什么呢?如下圖所示
所以,fork 之前父進程獨立執行,fork 之后,父子兩個執行流分別執行。注意,fork 之后,誰先執行完全由調度器決定
寫時拷貝
通常,父子代碼共享,父子在不寫入時,數據也是共享的,當任意一方試圖寫入,便以寫時拷貝的方式各自一份副本。具體見下圖:
如果沒有子進程,父進程數據段的權限是讀寫的,有了子進程就變成只讀的。如果子進程想要修改數據段,操作系統就會發現“錯誤”,我們正在試圖向一個只讀權限字段寫入,操作系統此時就會觸發寫時拷貝
所以寫時拷貝的原理是進程觸發錯誤操作,系統會調整錯誤操作
因為有寫時拷貝技術的存在,所以父子進程得以徹底分離!完成了進程獨立性的技術保證!
寫時拷貝,是一種延時申請技術,可以提高整機內存的使用率。
fork 常規用法
一個父進程希望復制自己,使父子進程同時執行不同的代碼段。例如,父進程等待客戶端請求,
生成子進程來處理請求。
一個進程要執行一個不同的程序。例如子進程從 fork 返回后,調用 exec 函數。
fork 調用失敗的原因
系統中有太多的進程
實際用戶的進程數超過了限制
進程終止
進程終止的本質是釋放系統資源,就是釋放進程申請的相關內核數據結構和對應的數據和代碼。
進程退出場景
代碼運行完畢,結果正確
代碼運行完畢,結果不正確
代碼異常終止
這些場景是通過退出碼來判定的,main函數的返回值通常代表代碼的運行情況
進程常見退出方法
- 從 main 返回
- 調用 exit
- _exit
異常退出:
?ctrl + c,信號終止
退出碼
退出碼(退出狀態)可以告訴我們最后一次執行的命令的狀態。在命令結束以后,我們可以知道命令是成功完成的還是以錯誤結束的。其基本思想是,程序返回退出代碼 0 時表示執行成功,沒有問題。
代碼 1 或 0 以外的任何代碼都被視為不成功。
Linux基本退出碼如下:
?退出碼 0 表示命令執行無誤,這是完成命令的理想狀態。
?退出碼 1 我們也可以將其解釋為 “不被允許的操作”。例如在沒有 sudo 權限的情況下使用
yum;再例如除以 0 等操作也會返回錯誤碼 1 ,對應的命令為 let a=1/0
?130 ( SIGINT 或 ^C )和 143 ( SIGTERM )等終止信號是非常典型的,它們屬于
128+n 信號,其中 n 代表終止碼。
?可以使用 strerror 函數來獲取退出碼對應的描述。
可以通過下列代碼來查看我們上一次運行的退出碼
echo $?
那么退出碼是從哪里來的,進程的退出碼