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