安徽城鄉(xiāng)建設廳網站一鍵關鍵詞優(yōu)化
0.引入
創(chuàng)建子進程的目的是什么?
就是為了讓子進程幫我執(zhí)行特定的任務
讓子進程執(zhí)行父進程的一部分代碼
如果子進程想執(zhí)行一個全新的程序代碼呢? 那么就要使用進程的程序替換
為什么要有程序替換?
也就是說子進程想執(zhí)行一個全新的程序代碼!

這份代碼看似是子進程的代碼,其實也是父進程的代碼,只是父進程通過id的值進行判斷,讓子進程運行。
1.替換原理
用fork創(chuàng)建子進程后執(zhí)行的是和父進程相同的程序(但有可能執(zhí)行不同的代碼分支),子進程往往要調用一種exec函數 以執(zhí)行另一個程序。
當進程調用一種exec函數時,該進程的用戶空間代碼和數據完全被新程序替換,從新程序的啟動 例程開始執(zhí)行。調用exec并不創(chuàng)建新進程,所以調用exec前后該進程的id并未改變。

2.替換函數
其實有六種以exec開頭的函數,統(tǒng)稱exec函數:
使用man手冊查看相關exec函數


execl最后的...參數,表示可變參數列表,可以給C函數傳遞任意的參數
execl
int execl(const char *path, const char *arg, ...);
path:新程序的路徑和名稱。
arg0 ~ argn:新程序的命令行參數列表,以 NULL 結尾。arg0 表示新程序的名稱,arg1 ~ argn 表示新程序的各個參數。


為什么只看到了begin()... end...為什么不顯示呢?
執(zhí)行程序替換,新的代碼和數據都被加載了,后續(xù)的代碼屬于老代碼,直接被替換了,沒機會執(zhí)行了
并且程序替換是整體替換,不能局部替換!!!
ls是磁盤上的可執(zhí)行程序,execl調用了ls-la
把當前的代碼和數據,從execl到磁盤中進行替換
問題:進程的程序替換,有沒有創(chuàng)建新的進程呢?
沒有!!
為什么呢?很簡單,因為我只是把一個新的程序加載到我們當前進程所對應的代碼和數據段,我就讓CPU去調度當前進程,就可以跑起來了。其中,我們并沒有創(chuàng)建新的進程。當前的進程PID沒有變化
這是站在進程的角度來看的
那么站在程序的角度呢? -- 這個程序被加載到內存里了
所以,我們也可以說execl是加載器
問題:當創(chuàng)建進程的時候,先加載進程數據結構,還是先加載代碼和數據?
在創(chuàng)建進程時,操作系統(tǒng)通常會先創(chuàng)建進程數據結構,然后再加載代碼和數據。

這里代碼,子進程里的execl替換代碼,會影響父進程嗎?

不會,程序替換只會影響調用進程,進程具有獨立性
子進程加載新程序的時候,是需要進行程序替換的,發(fā)生寫時拷貝(子進程執(zhí)行的可是全新的程序,新的代碼,寫時拷貝在代碼區(qū)也可以發(fā)生)
execl函數調用失敗,會發(fā)生什么?
我們寫一個代碼,讓程序故意發(fā)生錯誤


父進程獲取子進程退出碼



接下來開始熟悉所有的接口

execv
int execv(const char *path, char *const argv[]);
execv() 函數接受兩個參數。第一個參數是要執(zhí)行的程序的路徑,第二個參數是傳遞給新程序的命令行參數。這些參數以一個字符串數組的形式傳遞給函數。

注意:當execv調用失敗的話,程序繼續(xù)執(zhí)行
execlp
int execlp(const char *file, const char *arg, ...);
其中,file 參數表示要執(zhí)行的可執(zhí)行文件的名稱,arg 參數表示傳遞給該可執(zhí)行文件的命令行參數,最后的參數為可變參數列表,表示該命令行參數以 NULL 結束。
execlp 函數會在 PATH 環(huán)境變量指定的路徑中查找可執(zhí)行文件,因此無需指定可執(zhí)行文件的完整路徑。如果 PATH 環(huán)境變量中有多個路徑,則 execlp 函數會按照路徑的順序查找可執(zhí)行文件。
execlp("ls","ls","-a","-l","-n",NULL);
這里的兩個ls分別是什么意思?
第一個是系統(tǒng)環(huán)境變量路徑ls,第二個是ls指令
等同于以下代碼

execle
int execle(const char *path, const char *arg,..., char * const envp[]);
該函數接收以下參數:
path:要執(zhí)行的程序的路徑名。
arg0:新程序的第一個參數,通常是新程序的名稱。
arg1~argn:新程序的參數列表。
envp:新程序的環(huán)境變量數組。
該函數的返回值是一個整數,如果成功執(zhí)行,則永遠不會返回。如果出現錯誤,則會返回-1,并設置errno來指示錯誤的類型。
envp[]數組是自定義環(huán)境變量
execvp
函數原型如下:
int execvp(const char *file, char *const argv[]);
該函數會在PATH環(huán)境變量指定的路徑中搜索指定的可執(zhí)行文件,并在找到文件后將當前進程替換為該可執(zhí)行文件。
其中,file參數是一個字符串,指定要執(zhí)行的可執(zhí)行文件的路徑和文件名。argv參數是一個指向字符串數組的指針,其中第一個字符串表示可執(zhí)行文件的名稱,后面的字符串表示傳遞給可執(zhí)行文件的命令行參數。
execvpe
函數原型如下:
int execvpe(const char *file, char *const argv[], char *const envp[]);
file 參數是要執(zhí)行的可執(zhí)行文件的路徑,argv 參數是一個指向參數列表的指針數組,envp 參數是一個指向環(huán)境變量列表的指針數組。
execvpe 函數首先會搜索 PATH 環(huán)境變量中指定的目錄,找到可執(zhí)行文件后,它會用新的進程替換當前進程,并開始執(zhí)行新的程序。在新的進程中,參數和環(huán)境變量都被設置成 argv 和 envp 中指定的值。
execvpe 函數和其他 exec 系列函數的主要區(qū)別在于它會搜索 PATH 環(huán)境變量中指定的目錄來查找可執(zhí)行文件,并且可以設置環(huán)境變量。
execve
函數原型如下:
int execve(const char *filename, char *const argv[], char *const envp[]);
filename 參數是要執(zhí)行的可執(zhí)行文件的路徑,argv 參數是一個指向參數列表的指針數組,envp 參數是一個指向環(huán)境變量列表的指針數組。
execve 函數會用新的進程替換當前進程,并開始執(zhí)行新的程序。在新的進程中,參數和環(huán)境變量都被設置成 argv 和 envp 中指定的值。
execve 函數不會搜索 PATH 環(huán)境變量中指定的目錄來查找可執(zhí)行文件,它只會使用 filename 參數中指定的路徑來查找可執(zhí)行文件。如果指定的文件路徑不是一個可執(zhí)行文件,那么該函數會返回一個錯誤。
事實上,只有execve是真正的系統(tǒng)調用,其它五個函數最終都調用 execve,所以execve在man手冊第2節(jié),其它函數在man手冊第3節(jié)。這些函數之間的關系如下圖所示。下圖exec函數族 一個完整的例子:

3.調用自定義程序
上面都是執(zhí)行命令。能否執(zhí)行我自己寫的程序呢?
用C語言調用C++寫的可執(zhí)行程序



可以發(fā)現PID是一模一樣的,替換了C++程序,沒有創(chuàng)建新的進程
接下來嘗試在C++程序中獲取環(huán)境變量

獲取環(huán)境變量。C++的cout如果環(huán)境變量不存在,就什么都不會打印,所以要進行判斷,如果沒有就打印NULL

修改下C程序,在C程序中自定義環(huán)境變量表,使用execle函數


問題:C語言調用C++可以調用到環(huán)境變量嗎?
修改下C++代碼,獲取環(huán)境變量


C++自己寫的里有PATH,但沒有環(huán)境變量
觀察下C語言的

C語言調用的有MYENV 但是沒有PATH
調用的環(huán)境變量是覆蓋式寫入,會覆蓋老的環(huán)境變量PATH,所以看不到PATH
傳系統(tǒng)環(huán)境變量
C語言提供的環(huán)境變量表,二級指針,environ


此時MYENV就沒有了,系統(tǒng)的環(huán)境變量出現了
系統(tǒng)環(huán)境變量和自定義環(huán)境變量同時傳遞
利用putenv添加到系統(tǒng)環(huán)境變量里,依賴頭文件#include<stdlib.h>

putenv將MYENV傳到系統(tǒng)變量里,這樣調用系統(tǒng)變量,就可以將自定義和系統(tǒng)變量一塊傳遞給替換程序

環(huán)境變量具有全局屬性,可以被子進程繼承下去!
就是通過execle函數傳遞的環(huán)境變量!
使用export MYENV = You can see me 也可以
4.exec函數解釋
1.這些函數如果調用成功則加載新的程序從啟動代碼開始執(zhí)行,不再返回。
2.如果調用出錯則返回-1
3.所以exec函數只有出錯的返回值而沒有成功的返回值。
5.命名理解
這些函數原型看起來很容易混,但只要掌握了規(guī)律就很好記。
l(list) : 表示參數采用列表
v(vector) : 參數用數組
p(path) : 有p自動搜索環(huán)境變量PATH
e(env) : 表示自己維護環(huán)境變量

exec調用舉例如下:
#include <unistd.h>
int main()
{char *const argv[] = {"ps", "-ef", NULL};char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};execl("/bin/ps", "ps", "-ef", NULL);// 帶p的,可以使用環(huán)境變量PATH,無需寫全路徑execlp("ps", "ps", "-ef", NULL);// 帶e的,需要自己組裝環(huán)境變量execle("ps", "ps", "-ef", NULL, envp);execv("/bin/ps", argv);// 帶p的,可以使用環(huán)境變量PATH,無需寫全路徑execvp("ps", argv);// 帶e的,需要自己組裝環(huán)境變量execve("/bin/ps", argv, envp);exit(0);
}