进程程序替换
引入進程程序替換的概念
一般在fork了一個子進程之后,子進程往往會調用exec函數組來執行其他的程序,因為我們的子進程不可能直接執行很多的功能,這個大家在以后的操作中會慢慢的體會到
程序替換的幾個特點
- 進程在使用exec函數族進程進程替換的時候,并沒有創建一個新的進程,所以當前進程的進程ID并沒有改變。exec函數族只是用磁盤上面的一個新程序替換了當前進程的正文段、數據段、堆段和棧段。
- 調用exec函數族后,除非調用失敗,否則沒有返回值,并且調用失敗之后,調用處之后的子進程的程序繼續執行,如果調用成功,調用處之后的子進程將不再執行。
- 當子進程調用exec函數族之后,不管成功與否,父進程依然可以正常執行的。
下面我們想寫一些程序來測試上面的例子
#include<stdio.h>2 #include<stdlib.h>3 #include<unistd.h>4 int main()5 {6 pid_t son = fork();7 if(son == 0) //子進程8 {9 printf("i am son. my id is %d\n",getpid());10 execl("/bin/ls","ls","-a","-n","-l",NULL);11 printf("調用exec函數失敗\n");12 exit(0);13 }14 else //父進程15 {16 printf("i am father\n");17 sleep(5);18 }19 20 return 0;21 }
這里的結果如下圖所示
我們來根據上面的結果來分析一下,這里執行完我們的替換函數之后,就沒有執行printf(“執行完畢exec函數\n”);說明 我們上文中的第二個結論中“如果調用成功,調用處之后的子進程將不再執行”是正確的。
接下來我們把程序改一下,測試一下如果調用失敗是一個什么樣的結果
我們這里只把exec函數中的”/bin/ls”改成”/bin/lsss”這樣它在bin目錄下肯定是找不到了,所以程序應該是調用失敗的,失敗了之后顯示的結果是下圖
這說明當我們exec函數失敗后,調用的進程沒有被替換,所以后面的程序繼續執行
然后我們還需要看一下父進程的 結果是什么樣的,從 上面的結果看不管子子進程調用exec函數結果如何,父進程始終正常運行。
接下來我們試了一下vfork的結果是什么樣的,測試之后的結果還是什么上面的結論,當子進程調用exec函數族之后,不管成功與否,父進程依然可以正常執行的。
大家可以下去嘗試一下vfork,vfork的作用 是保證子進程先執行。
簡析exec函數族
首先看一下exec函數族的定義
#include <unistd.h>2 extern char **environ;3 int execl(const char *path,const char *arg, ...);5 int execlp(const char *file, 6 const char *arg, ...);7 int execle(const char *path,8 const char *arg,9 ...,
10 char * const envp[]);
11 int execv(const char *path,
12 char *const argv[]);
13 int execvp(const char *file,
14 char *const argv[]);
15 int execve(const char *file,
16 char *const argv[],
17 char *const envp[]);
淺析進程替換函數
進程替換函數有六種分別是execl、execlp、execle和execv、execvp、execve,我們首先觀察這幾個函數會發現這些函數都是以exec開頭的,由此我們稱這些函數是exec函數族。
接下來又分為兩類,一類是execl,一類是execv。前面三個是以execl開頭,這個開頭有一個特點就是在exec后面加上了l,我們發揮想象 可以想象到STL中的list,就是一個鏈表,這里可以理解為我們在使用這中類型的函數的時候是需要傳入的一個可變的參數列表的。
這個可變參數列表實際上就是一些程序的一些可執行選項,比如我們在執行ls命令的時候,實際上就是在shell程序下進行了一次程序替換,在執行ls的時候我們可以給他它加上許多選項,比如ls -a -l等等。
另外就是execv,這個v可以讓我們想起STL中的vector,就是線性表,就是我們在使用這個函數的時候需要傳入的是一個數組。
再來解釋一下帶有p的函數,表示不需要加入替換程序的絕對路徑或者是相對路徑,不帶p的就是要加入絕對路徑或者是相對路徑,另外還有就是帶有e的函數,這個時候我們可以傳入特定的環境變量。
接下來舉例子來具體看看這些函數是如何使用,比如我們在父進程中創建了一個子進程,然后又在這個子進程中的合適位置調用了exec函數,那么這個時候就會發生程序替換
execl
execl("/bin/ls","ls","-a","-l","-n",NULL)
這里因為是一個execl函數,他沒有帶有p,所以我們第一個參數傳遞的是替換程序的絕對路徑或者是相對路徑,這里我門替換的函數是ls函數,它的絕對路徑是”/bin/ls”,下面的那些事執行這個程序的一些選項,注意要以NULL結尾
execlp
execlp("ls","ls","-a","-l","-n",NULL)
加上這個p之后我們就不需要加上程序的路徑了,execlp會自己搜索,這個時候有人就會問了,為什么還要寫兩次ls呢,這里第一個ls是我們需要執行的程序,第二個ls結合著后面的一些選項就是我們是要如何執行。
execv
char* _agrv[] = {"ls","-a","-n","-l",NULL};
execv("/bin/ls",_agrv);
其實這里很簡單,我們無非就是把上面的那個函數的后面的選項加入到一個指針數組里面去了,這個就是execv函數組的意義,其他的兩個函數這里就不解釋了。
execle
這個函數我們需要傳入特定的環境變量,這個留個懸念,大家自己網上搜索吧。
補充說明,上面替換的程序可以是大家自己寫的程序哦…
總結