文章目錄
- 一、程序建立
- fork函數初識
- fork之後,作業系統做了什麼?
- 寫時拷貝
- fork的正常用法
- fork調用失敗的原因
- 二、程序終止
- 關于終止的正确認識
- 關于終止的常見做法
- _exit函數
- 關于終止,核心做了什麼?
正文開始!
一、程序建立
fork函數初識
在Linux中fork函數是非常重要的函數,他從已存在程序中建立一個新程序。新程序稱為子程序,而原程序為父程序。
傳回值:子程序中傳回0,父程序中傳回子程序的id,出錯傳回-1。
程序調用fork,當控制轉移到核心中的fork代碼後,核心做:
- 配置設定新的記憶體塊和核心資料結構給子程序
- 将父程序部分資料結構内容拷貝至子程序
- 添加子程序到系統程序清單當中
- fork傳回,開始排程器排程
當一個程序調用fork之後,就有兩個二進制代碼相同的程序。而且他們都運作到相同的地方。但每個程序都将可以開始他們自己的執行。
看下面代碼了解:
#include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 printf("我是一個程序,pid:%d\n",getpid());
7 fork();
8 printf("我依舊是一個程序:pid:%d\n",getpid());
9 sleep(1);
return 0;
}
這裡我們看到三行輸出,一行before,兩行after。程序5034先列印before消息,然後他又列印after。另一個after消息由5035列印的。注意到程序5035沒有列印before,為什麼呢?如下圖所示
是以,fork之前父程序獨立執行,fork之後,父子兩個執行流分别執行。注意fork之後,誰先執行完全由排程器決定。
那麼fork之後,是否隻有fork之後代碼是被父子程序共享的??
一般情況下,fork之後父子程序共享所有的代碼!!
子程序執行的後續代碼!=共享的所有代碼,隻不過子程序隻能從這裡開始執行!!
那麼這是為什麼呢?
因為CPU中有一個eip,會儲存正在執行指令的下一條指令!eip程式計數器會拷貝給子程序,子程序便從該eip所指向的代碼處開始執行!!!
fork之後,作業系統做了什麼?
程序=核心的程序的資料結構+程序的代碼和資料
建立子程序的核心資料結構(struct task_struct+struct mm_struct+頁表)+代碼繼承父程序,資料以寫時拷貝的方式,來進行共享或者獨立!
寫時拷貝
通常情況下,父子代碼共享,父子在不寫入時,資料也是共享的,當任意一方試圖寫入,便以寫時拷貝的方式各自有一份副本。具體如下圖:
寫時拷貝本身就是由OS的記憶體管理子產品完成的!
那麼為什麼要寫時拷貝呢?
建立子程序的時候,就把資料分開不行嗎???
1.父程序的資料,子程序不一定全用,即便使用,也不一定全部寫入。—會有浪費空間的嫌疑
2.最理想的情況,隻有會被父子程序修改的資料,進行分離拷貝。不需要修改的共享即可—但是從技術角度實作複雜
3.如果fork的時候,就無腦拷貝資料給子程序,會增加fork的成本(記憶體和時間)
是以最終我們采用寫時拷貝!!!(隻會拷貝父子修改的,變相的就是拷貝資料的最小成本)
寫時拷貝本質上是一個延遲拷貝政策。隻有真正使用的時候,才給你!
就意味着你想要,但是不立馬使用的空間,先不給你,那麼也就意味着可以先給别人!
綜上,就是變相的提高了記憶體的使用率!
fork的正常用法
- 一個父程序希望複制自己,使得父子程序同時執行不同的代碼段。例如,父程序等待用戶端請求,生成子程序來處理請求。
- 一個程序要執行一個不同的程式。例如子程序從fork傳回後,調用exec函數。
fork調用失敗的原因
1.系統中有太多的程序了
2.實際使用者的程序數超過了限制
二、程序終止
關于終止的正确認識
我們使用C/C++的時候,main使我們的入口函數,函數的結束我們經常會寫return 0;語句!
a.return 0,給誰return
根據下面的論述我們可以得出return是給父程序的!
b.為何是0?其他值可以嗎?
答案是:程序代碼跑完,結果是否正确
0:表示success
非零:表示失敗
但是當我們失敗的時候,我們最想知道的是失敗的原因是什麼?
是以我們用非零辨別不同的原因!
首先我們先來了解常見的程序退出:
1.代碼跑完,結果正确
2.代碼跑完,結果不正确
3.代碼沒跑完,程式異常了
我們将return X;X稱為程序退出碼!表征程序退出的資訊!得出的資訊是要讓父程序讀取的!
先來看代碼!
1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6
7 return 123;
8
9 }
10
我們可以看到程式隻有一行代碼,直接運作完成就退出了!
根據我們之前的學習,我們可以直到指令行的父程序就是bash程序
我們可以用目前指令檢視退出碼
echo $?
$?代表在bash中,最近依次執行完畢時,對應程序的退出碼!
一般而言,失敗的非零值我該如何設定呢??以及預設表達的含義?
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<string.h>
4
5 int main()
6 {
7 for(int i=0;i<100;i++)
8 {
9 printf("%d:%s\n",i,strerror(i));
10 }
11 return 0;
12}
錯誤碼退出碼可以對應不同的錯誤原因,友善定位問題!
關于終止的常見做法
1.在main函數中return。為什麼其它函數不行呢?
2.在自己的代碼任意地點中,調用exit()函數!
man 3 exit
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<string.h>
4
5 void func()
6 {
7
8 printf("hello func\n");
E> 9 exit(111);
10
11
12 }
13 int main()
14 {
15
16 func();
17 return 10;
18
19}
_exit函數
exit終止程序會重新整理緩沖區
_exit直接終止程序,不會有任何重新整理操作!
其實exit底層也是在調_exit函數。
關于終止,核心做了什麼?
程序= 核心資料結構 + 程序代碼和資料