Linux——程式建立、程式終止、程式等待、程式程式替換

@餘笙!發表於2020-10-13

一、程式建立

建立一個程式就是建立一個pcb,pcb在Linux下是一個task_struct結構體,放在核心中,只能通過呼叫介面實現建立

1.pid_t  fork(void);

通過複製父程式建立一個子程式(複製了父程式pcb中的資料)

程式碼共享,資料獨有

2.pid_t  vfork(void);

也是建立一個子程式,但是父程式使用vfork建立後,vfork的呼叫並不會立即返回(通常說會阻塞父程式),而是讓子程式先執行,直到子程式退出或執行程式替換之後父程式才能執行(vfork建立的子程式若程式替換,排程執行新的程式,就會給自己開闢新的空間,與新的程式建立對映關係)

資料共用

vfork建立的子程式特殊的地方:父子程式共用一個虛擬地址空間(父程式的虛擬地址空間)

在程式執行中每呼叫一個函式就會有一次函式壓棧——函式呼叫棧

父子程式使用了同一個棧,若父子程式同時執行會造成呼叫棧混亂,因此讓子程式先執行,直到子程式退出或程式替換後有了自己的地址空間

注意:vfork建立的子程式不能在main函式中使用return退出,因為子程式使用return退出釋放了所有資源,父程式執行的時候資源是錯誤的

 3.寫時拷貝技術

子程式建立出來後,與父程式對映關係訪問同一塊物裡記憶體(但虛擬地址空間及頁表這些資訊都是程式獨有),當物裡記憶體中資料即將發生改變時,重新為子程式開闢實體記憶體,拷貝資料過去

4.程式擁有獨立性——各有各的虛擬地址空間,對映各自資料儲存

程式之間沒有交叉關係,不會受到其他程式執行影響,就是為了保證程式的穩定執行

5.返回值

子程式中返回0;父程式中返回子程式的pid;出錯返回-1

二、程式終止

1.程式常見退出方法

(1)main函式中的return

普通函式中的return只能退出函式,不能退出程式

(2)void  exit (int status);

在任意位置呼叫程式的任意位置退出一個程式

(3)void  _exit (int status);

在任意位置呼叫程式的任意位置退出一個程式

問:exit和_exit的區別是什麼?

答:exit是庫函式,退出時會重新整理緩衝區,將緩衝區的資料寫入檔案;

      _exit是系統呼叫介面,退出程式時直接釋放資源,不會重新整理緩衝區

2.程式退出場景 

正常退出:程式執行到return或exit的地方退出

異常退出:程式執行到中途崩潰了

不管子程式正常退出或者異常退出,只要是退出了,沒有被父程式等待處理,就都會成為殭屍程式

三、程式等待

父程式等待子程式退出,為了獲取退出子程式的返回值,釋放退出子程式的所有資源,避免產生殭屍程式

殭屍程式產生的原因:子程式先於父程式退出,為了儲存退出返回值而無法完全釋放資源產生的

程式等待的方法 

1.int  wait (int* status);    [status:輸出型引數——用於獲取退出子程式的返回值]

 

處理退出的子程式,如果呼叫這個介面沒有子程式已經退出,則會使父程式阻塞等待,直到有子程式退出

阻塞:為了完成一個功能我們發起一個呼叫,但是若當前不具備完成功能的條件,則呼叫等待

非阻塞:為了完成一個功能我們發起一個呼叫,但若當前不具備完成功能的條件,則呼叫立即報錯返回

返回值:成功返回處理的退出子程式的pid,失敗(比如沒有子程式)返回-1

2.int  waitpid (int pid, int *status, int options);

返回值:成功返回退出子程式的pid,沒有子程式退出返回0,失敗(比如沒有子程式)返回-1

wait與waitpid的區別:

(1)wait等待的是任意一個子程式的退出(wait是一個父程式假設有很多子程式,任意一個退出,都會處理後呼叫返回);waitpid可以等待指定的子程式,也可以等待任意一個子程式,通過第一個引數確定(第一個引數pid == -1表示等待任意)

(2)wait是一個阻塞介面(wait如果沒有子程式退出,則會一直等待);waitpid可以預設阻塞,也可以設定為非阻塞,通過第三個引數確定(option == 0表示預設阻塞,option == WNOHANG表示非阻塞),非阻塞操作通常需要迴圈處理

3.獲取子程式status 

 一個程式退出場景有兩種:正常退出、異常退出

一個程式只有正常退出的時候返回值才有意義;若程式是異常退出,則返回值沒有意義

因此在獲取返回值之前,應先通過低七位判斷程式是否是正常退出(正常退出則異常訊號值為0;否則大於0)

判斷一個程式是否正常退出:取出低七位;status & 0x7f == 0  正常退出

如何取出返回值(低16位中的高8位):(status >> 8)& 0xff   ——因為返回值只用了一個字元,因此程式的返回值

WIFEXITED() / WEXITSTATUS

系統呼叫介面出錯後,如何獲取錯誤原因?

#include<errno.h>

#include<string.h>

char *strerror(errno);  根據錯誤編號獲取文字資訊——錯誤都是上一次系統呼叫介面使用錯誤的原因

perror(char *msg);直接列印上一次系統呼叫介面使用錯誤的原因

core dump——核心轉儲

指的是程式異常退出時,將退出前的程式執行資訊儲存下來(預設是關閉的)

四、程式程式替換

重新載入另一個程式到記憶體中,然後將現有的一個pcb的記憶體指標所指向的記憶體空間指向這個新的程式(更新頁表對映資訊),則這個現有的pcb就跑去排程這個新的程式了。

程式替換,給一個程式替換一個新的要排程執行的程式,並且因為這個程式排程的程式已經被替換,因此當執行完畢新的程式後會退出;原先的程式在程式替換以後的程式碼都不會被執行到(替換後相當於已經沒有當前的程式碼了,只有新的程式)

如何替換:exec函式族

#include<unistd.h>

int execl(const char *path, const char *arg, ...);//path—帶路徑的程式檔名稱;arg/...表示程式的執行引數,逐個賦予,最終以NULL結尾
int execlp(const char *file, const char *arg, ...);//PATH環境變數指定了一些路徑,execlp會去PATH環境變數指定的路徑下查詢程式檔案
int execle(const char *path, const char *arg, ..., char *const envp[]);

int execv(const char *path, char *const arg);
int execvp(const char *file, char *const arg);
int execve(const char *file, char *const arg, char *const envp[]);

i 和 v的區別:程式執行引數的賦予方式不同

有沒有p的區別:新的程式檔案的名稱是否需要帶路徑

有沒有e的區別:是否自定義環境變數

相關文章