天天看點

Linux——程序控制1.程序的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程序建立6.程序終止7.程序等待8.程序替換

文章目錄

  • 1.程序的建立
  • 2. fork函數傳回值
    • 2.1 fork為什麼有兩個傳回值
    • 2.2 為什麼給子程序傳回0,給父程序傳回子程序的pid?
  • 3 寫時拷貝
    • 3.1代碼為什麼要共享
    • 3.2資料為什麼要私有
    • 3.3何謂寫實拷貝
    • 3.4執行個體分析
  • 4.fork正常用法和調用失敗原因
  • 5.如何理fork和子程序建立
  • 6.程序終止
    • 6.1程序退出場景
    • 6.2程序退出的常見方法
  • 7.程序等待
    • 7.1程序等待的必要性
    • 7.2wait方法
    • 7.3waitpid方法
      • 7.3.1擷取子程序的status
  • 8.程序替換
    • 8.1替換原理
    • 8.2替換函數
    • 8.3函數解釋
    • 8.4命名了解
    • 8.5實戰操作

1.程序的建立

fork在已存在程序之中建立一個新的程序,這個被建立的程序稱之為子程序,原來已知的程序稱之為父程序;

程序是由可執行程式和它對應的資料和代碼以及一堆資料結構組成(PCB,虛拟位址空間,頁表等);

而子程序的建立是以父程序為模闆的,是以調用fork之後就有兩個代碼相同的程序,并且兩個程序都會從fork下一行代碼開始執行

父程序調用fork,當控制權轉移到核心中的fork代碼後(fork是系統調用接口),核心會做如下事情:

1.配置設定新的記憶體塊和核心資料結構給子程序

fork在核心之中,作業系統是管理軟硬體資源的,是以有權限管理程序,并且給程序配置設定記憶體和資料結構

2.将父程序部分資料結構内容拷貝至子程序

子程序的建立是以父程序為模闆的,将父程序的PCB、位址空間、頁表映射内容拷貝給子程序

3.添加子程序到系統程序清單當中

當子程序被添加到程序清單中之後就可以被排程了

4.fork傳回,開始排程器排程

當fork傳回,開始排程之後就完成了一個子程序的建立

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

2. fork函數傳回值

子程序傳回0,父程序傳回的是子程序的pid

2.1 fork為什麼有兩個傳回值

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

2.2 為什麼給子程序傳回0,給父程序傳回子程序的pid?

因為一個父程序或許會有多個子程序,那麼它如何準确的定位尋找它的這些子程序呢?靠的就是建立時候傳回來的子程序的pid,這就是父程序尋找子程序的辨別,父程序通過這個辨別就可以對子程序精準定位。而子程序永遠隻有一個父程序,是以不需要辨別

3 寫時拷貝

通常父子代碼共享,父子不再寫入時,資料也是共享的,當任意一方試圖寫入,便以寫入時拷貝的方式各自一副副本,這種情況就叫做寫時拷貝

3.1代碼為什麼要共享

程式=代碼(邏輯)+資料;

子程序是以父程序為模闆建立而來的,是以父子程序代碼都是一樣的,同時因為頁表的映射權限問題,代碼是不可以被修改的,如果各自父子程序各自私有一份,會造成空間的浪費

分流之後代碼也是共享的,隻是會不會去執行的問題。比如,你家裡有一百輛車,但是你最常開的隻有3輛,其餘的車也是你的,隻是沒有去碰罷了

3.2資料為什麼要私有

在頁表中,程序資料的權限大多是可讀可寫的,如果共享,一個程序的資料改變就會影響另外一個程序,這樣就破壞了程序之間的獨立性,是以資料是私有的

3.3何謂寫實拷貝

顧名思義,就是寫的時候進行拷貝

一個程序的資料是很多的,不是所有的資料子都要立即使用,且不是所有的資料都需要進行拷貝。但是如果馬上要獨立就需要将資料全部拷貝,把本來可以在後面拷貝的,甚至不需要拷貝的資料都拷貝了,就比較浪費空間

通常父子代碼共享,父子不再寫入時,資料也是共享的,當任意一方試圖寫入,便以寫入時拷貝的方式各自一副副本,這種情況就叫做寫時拷貝

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

3.4執行個體分析

當有資料20M,需要修改其中10M的時候,發生寫時拷貝,是拷貝10M還是拷貝20M?

答案是拷貝10M,因為另外的10M不一定有用,寫時拷貝隻拷貝當下有用的資料

從作業系統的角度出發,OS隻會盡可能的節省空間

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

4.fork正常用法和調用失敗原因

正常用法:

1.子程序指派父程序,通過if、else來實作父子程序執行不同的代碼段。比如父程序等待用戶端請求,生成子程序來進行處理。

2.一個程序要執行一個不同的程式。比如fork後,子程序調用exec函數

調用失敗原因:

1.系統中有太多的程序

程序是需要占據空間的,包括PCB,頁表,代碼,資料等等,都需要向系統申請空間,有可能空間不夠就被系統拒絕了

2.實際使用者的程序數超過了限制

有可能一個使用者申請的程序數超過了作業系統允許的數量,是以申請失敗

5.如何理fork和子程序建立

子程序建立本質上是系統多了一個程序,是以作業系統需要将新的程序管理起來,就需要給程序建立PCB、虛拟空間、頁表、映射關系等。

而子程序是以父程序為模闆的,這些資料大體上是類似的(比如pid等等就不會拷貝過來),并且在一開始是共享代碼和資料的,隻有當任何一方發生資料寫入的時候才會發生寫時拷貝,将資料獨立出來;

6.程序終止

6.1程序退出場景

程序退出有如下三種場景

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

1.代碼運作完畢,結果正确

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

2.代碼運作完畢,結果不正确

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

3.代碼異常終止

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

6.2程序退出的常見方法

通過指令 echo $? 可以檢視最近一次程序的退出碼

正常終止:

1.從main函數傳回

在main中執行return 等同于執行exit(n),因為調用main的運作時,函數會将main的傳回值當做exit的參數

2.調用exit

a.執行使用者通過atexit或on_exit定義的清理函數

b.關閉所有打開的流,所有緩存資料均被寫入

c.調用_exit

3.調用_exit,直接清理程序,不會重新整理緩沖區

異常退出:

ctrl +c,信号終止

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

7.程序等待

是父程序通過wait等系統調用,用來等待子程序狀态的一種現象,且是必須的

7.1程序等待的必要性

子程序被建立出來,誰先運作是由排程器來決定的

程序建立本質是系統多了一個程序,是多了PCB、位址空間、頁表、代碼資料等

一個程序終止分為僵屍和徹底釋放兩種情況

徹底釋放:釋放PCB、釋放位址空間、釋放頁表、代碼和資料被情況

僵屍狀态:程序的代碼和資料被釋放了、甚至部分頁表被釋放了、但是這個程序的PCB要被保留下來、PCB中會記錄程序的退出碼,這個退出碼和資料資訊将來要被父程序通過wait/waitpid進行讀取

一般而言是讓子程序先退出的:

1.如果父程序先退出,子程序變成了孤兒程序,子程序被系統接管,資源也被系統回收,但是回收的時間和系統有關系,不一定是立即回收的

2.如果父程序先退出,子程序變成了僵屍程序,會導緻記憶體洩漏

2.因為父程序很容易對子程序進行管理(進行垃圾回收)

3.建立出來的子程序是用來處理業務的,什麼時候執行完是不确定的,需要父程序幫我們拿到子程序處理的結果,讀取子程序狀态

一般子程序是需要被父程序等待的,等待的方式有wait/waitpid

等待的目的是為了進行垃圾回收和獲知子程序運作結果

7.2wait方法

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

傳回值:

成功傳回被等待程序的pid,失敗傳回-1

參數:

輸出型參數,擷取子程序退出狀态,不關心則可以設定為NULL

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

7.3waitpid方法

當一個子程序退出變成僵屍狀态時,我們的父程序通過waitpid釋放它的僵屍狀态,并且讀取子程序的退出碼和退出時所收到的信号資訊

是以需要傳入一個st參數,st讀取的資料是在程序的PCB中儲存的。當程序退出的時候,我們擷取到st中的資訊就可以對程序的退出狀态進行分析(正常退出結果正确、正常退出結果不正确、被信号殺死),我們可以通過這些資訊提示使用者或加入一些資訊進行判斷

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換
Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

傳回值:

當正常傳回的時候waitpid傳回收集到的子程序的程序ID

如果設定了選項WNOHANG,而調用中waitpid發現自己沒有已退出的子程序可收集則傳回0

如果的調用出錯,則傳回-1,這時errno會被設定成相應的值以訓示錯誤所在

參數:

pid:

pid=-1,等待任意一個子程序,與wait等效

pid>0,等待其程序id與pid相等的子程序

staus:

WIFEXITED(status):若為正常終止子程序傳回的狀态,則為真。(檢視程序是否正常退出):!(status & 0x7f)

WEXITSTATUS(status):若WIFEXITED非零,提取子程序退出碼。(檢視程序的退出碼):(status>>8) & 0xff

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

options:

1.預設是0,表示阻塞方式,隻要子程序不退出,父程序就一直在等,容易卡住,于是下面就有了對立的WNOHANG(非阻塞等待)

2.WNOHANG:若pid指定的子程序沒有結束,則waitpid()函數傳回0,不予等待。若正常結束則傳回該子程序的id

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

7.3.1擷取子程序的status

wait和waitpid,都有一個status參數,該參數是一個輸出型參數,由作業系統填充

如果傳遞NULL,表示不關心子程序的退出狀态資訊

否則,作業系統會根據該參數,将子程序的退出資訊回報給父程序

status不能簡單的當作整形來看待,可以當作位圖來看待

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

8.程序替換

8.1替換原理

fork建立子程序的目的:

讓子程序和執行父程序一部分代碼:代碼共享

讓子程序執行和父程序完全不同的事:調用exce函數,代碼也要發生寫時拷貝

當程序調用另外一種exce函數時,該程序的使用者空間和代碼和資料将完全被新程式替換,從新程式啟動的例程開始執行

調用exce函數并不是建立新程序,是以調用exce前後該程序的id并沒有改變

新的代碼和資料的替換過程是由作業系統來進行的,我們隻需要調用作業系統提供的系統調接口函數即可,下面介紹六種exec開頭的替換函數

8.2替換函數

有六種exec開頭的函數,統稱為exce函數

int execl(const char *path,const char *arg,…);

int execlp(const char *file,const chat *arg,…);

int execle(const char *path,const char *arg,…,char *const envp[]);

int execv(const char *path,char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execve(const char *pash,char *const argv[],cahr *const envp[]);

作業系統隻提供了execve一個調用接口,其餘的五個函數都是堆execve的封裝

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換
Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

8.3函數解釋

這些函數如果調用成功則加載新的程式從啟動代碼開始執行,不再傳回

調用出錯則傳回-1

exce函數隻有出錯傳回值,成功沒有傳回值,因為成功源程式所有資料和代碼都會被覆寫

8.4命名了解

l(list):表示參數采用清單

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

v(cevtor):參數采用數組

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

p(path):有p自動搜尋環境變量PATH

帶p的傳入系統中的程式或者指令的時候,會自動去環境變量PATH中尋找對應的指令,不需要傳入檔案路徑

不帶p的,則需要自己傳入檔案路徑

e(env):表示自己維護環境變量

8.5實戰操作

Linux——程式控制1.程式的建立2. fork函數傳回值3 寫時拷貝4.fork正常用法和調用失敗原因5.如何理fork和子程式建立6.程式終止7.程式等待8.程式替換

繼續閱讀