昆明網(wǎng)站開發(fā)多少錢免費(fèi)域名注冊(cè)平臺(tái)
本節(jié)學(xué)習(xí)exec族函數(shù),并大量參考了以下鏈接:
linux進(jìn)程---exec族函數(shù)(execl, execlp, execle, execv, execvp, execvpe)_云英的博客-CSDN博客
exec族函數(shù)函數(shù)的作用
我們用fork函數(shù)創(chuàng)建新進(jìn)程后,經(jīng)常會(huì)在新進(jìn)程中調(diào)用exec函數(shù)去執(zhí)行另外一個(gè)程序。當(dāng)進(jìn)程調(diào)用exec函數(shù)時(shí),該進(jìn)程被完全替換為新程序。因?yàn)檎{(diào)用exec函數(shù)并不創(chuàng)建新進(jìn)程,所以前后進(jìn)程的ID并沒(méi)有改變。
同時(shí),由于exec族函數(shù)包含很多API,所以一個(gè)個(gè)去Linux敲man可能有些麻煩,可以直接去Linux官網(wǎng)的man Page來(lái)查看:Linux man pages
?
exec族函數(shù)中的具體函數(shù)介紹
exec族函數(shù)功能
- 在進(jìn)程內(nèi)部調(diào)用執(zhí)行一個(gè)可執(zhí)行文件??蓤?zhí)行文件既可以是二進(jìn)制文件,也可以是任何Linux下可執(zhí)行的腳本文件。
函數(shù)族成員
- execl
- execlp
- execle
- execv
- execvp
- execvpe
函數(shù)原型
#include <unistd.h>
extern char **environ;int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., char * const envp[]); //應(yīng)用相對(duì)較少
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]); //應(yīng)用相對(duì)較少
參數(shù)說(shuō)明?
- path:可執(zhí)行文件的路徑名字
- arg:可執(zhí)行程序所帶的參數(shù),第一個(gè)參數(shù)為可執(zhí)行文件名字,沒(méi)有帶路徑且arg必須以NULL結(jié)束
- file:如果參數(shù)file中包含/,則就將其視為路徑名,否則就按PATH環(huán)境變量,在它所指定的各目錄中搜尋可執(zhí)行文件
- 返回值:exec函數(shù)族的函數(shù)執(zhí)行成功后不會(huì)返回,調(diào)用失敗時(shí),會(huì)設(shè)置errno并返回-1,然后從原程序的調(diào)用點(diǎn)接著往下執(zhí)行
exec族函數(shù)參數(shù)極難記憶和分辨,函數(shù)名中的字符會(huì)給我們一些幫助:
- l : 使用參數(shù)列表
- p:使用文件名,并從PATH環(huán)境進(jìn)行尋找可執(zhí)行文件
- v:應(yīng)先構(gòu)造一個(gè)指向各參數(shù)的指針數(shù)組,然后將該數(shù)組的地址作為這些函數(shù)的參數(shù)。
- e:多了envp[]數(shù)組,使用新的環(huán)境變量代替調(diào)用進(jìn)程的環(huán)境變量
帶 “l(fā)” 的一類exec函數(shù)(l表示list)
包括execl、execlp、execle,要求將新程序的每個(gè)命令行參數(shù)都說(shuō)明為 一個(gè)單獨(dú)的參數(shù)。這種參數(shù)表以空指針結(jié)尾
通過(guò)execl舉例:
execl.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{printf("before execl\n");if(execl("./arg_go","aaa","bbb",NULL) == -1){printf("execl failed!\n");perror("why"); //用于判斷錯(cuò)誤}printf("after execl\n");return 0;
}
arg.c:
#include <stdio.h>int main(int argc,char *argv[])
{int i = 0;for(i = 0; i < argc; i++){printf("argv[%d]: %s\n",i,argv[i]); }return 0;
}
實(shí)現(xiàn)效果1:
可見,在運(yùn)行了execl函數(shù)之后,程序確實(shí)被完全替代成了另一個(gè)程序,所以原程序最后的“after execl”并不會(huì)被打印!
同時(shí)再次強(qiáng)調(diào),execl函數(shù)的第一個(gè)參數(shù),寫的是執(zhí)行文件,而不是C文件,所以,運(yùn)行主程序之前,先編譯并自己命名一個(gè)執(zhí)行文件!
那么既然execl可以使用可執(zhí)行程序來(lái)完全替換一段程序,那么系統(tǒng)的指令本質(zhì)上也是一個(gè)個(gè)可執(zhí)行文件,就比如經(jīng)常使用的 “l(fā)s” ,通過(guò)“whereis ls”可以查看這個(gè)可執(zhí)行文件的絕對(duì)路徑:
修改execl.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{printf("before execl\n");if(execl("/bin/ls","ls","-l",NULL) == -1){printf("execl failed!\n");perror("why");}printf("after execl\n");return 0;
}
實(shí)現(xiàn)效果2:
可見,通過(guò)execl函數(shù),成功將原程序替換成了ls,并且還附帶了“-l”?的參數(shù)
?
帶 “p” 的一類exec函數(shù)
包括execlp、execvp、execvpe,如果參數(shù)file中包含/,則就將其視為路徑名,否則就按 PATH環(huán)境變量,在它所指定的各目錄中搜尋可執(zhí)行文件。舉個(gè)例子,PATH=/bin:/usr/bin
通過(guò)execlp舉例:
execlp.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{printf("before execl\n");if(execlp("ls","ls","-l",NULL) == -1){printf("execl failed!\n");perror("why");}printf("after execl\n");return 0;
}
實(shí)現(xiàn)效果:
可見,實(shí)現(xiàn)了和上面使用execl來(lái)替換“l(fā)s”相同的效果,但是代碼卻有所區(qū)別,execlp的第一個(gè)參數(shù)不需要輸入絕對(duì)路徑,只需要輸入可執(zhí)行文件的名字就可以,其原因就是execlp的第一個(gè)參數(shù)不是path而是file,而file參數(shù)如同上面所說(shuō): “如果參數(shù)file中包含/,則就將其視為路徑名,否則就按PATH環(huán)境變量,在它所指定的各目錄中搜尋可執(zhí)行文件?”
為了進(jìn)一步對(duì)比區(qū)別,可以將execl.c的代碼中的這一句進(jìn)行修改,第一個(gè)參數(shù)也改成“l(fā)s”:
if(execl("ls","ls","-l",NULL) == -1)
然后運(yùn)行execl.c:
?
顯然,這再次說(shuō)明了對(duì)于execl來(lái)說(shuō),這種直接寫可執(zhí)行文件名字的方式不可取
環(huán)境變量的概念穿插
另外,此處引出了“環(huán)境變量"的概念,在Linux中可以用 “echo $PATH” 來(lái)查看環(huán)境變量:?
也就是說(shuō),只要可執(zhí)行文件在上圖的這些環(huán)境變量路徑下,那么只要調(diào)用帶 “p” 的exec族函數(shù),第一個(gè)file參數(shù)就可以直接寫可執(zhí)行文件的名字了
Q:那如何將新的路徑配置到環(huán)境變量中去?
A:使用export指令:“export PATH=$PATH:想要添加的路徑”
使用 “pwd” 顯示當(dāng)前路徑:
然后使用?“export PATH=$PATH:/home/mjm/JC”? 將當(dāng)前路徑添加到環(huán)境變量:
此時(shí),如果修改?execlp.c 的代碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{printf("before execl\n");if(execlp("arg_go","aaa","bbb",NULL) == -1){printf("execl failed!\n");perror("why");}printf("after execl\n");return 0;
}
執(zhí)行代碼:
?
?可見,由于此時(shí)當(dāng)前路徑被添加到了環(huán)境變量中,所以使用execlp函數(shù)并將第一個(gè)參數(shù)直接寫成當(dāng)前路徑下的可執(zhí)行文件,也可以成功運(yùn)行!
帶 “v” 不帶 “l(fā)” 的一類exec函數(shù)?
包括execv、execvp、execve,應(yīng)先構(gòu)造一個(gè)指向各參數(shù)的指針數(shù)組,然后將該數(shù)組的地址作為這些函數(shù)的參數(shù)
?通過(guò)execv舉例:
execv.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{printf("before execl\n");char *argv[] = {"ls","-l",NULL};if(execv("/bin/ls",argv) == -1){printf("execl failed!\n");perror("why");}printf("after execl\n");return 0;
}
可見,唯一的區(qū)別就是“先構(gòu)造了一個(gè)指向各參數(shù)的指針數(shù)組?”
實(shí)現(xiàn)效果:
可見,同樣實(shí)現(xiàn)了將“l(fā)s”替換原程序的效果,并且也增加了“-l”?的參數(shù)
?
帶 “e” 的一類exec函數(shù) (應(yīng)用相對(duì)較少)
包括execle、execvpe,可以傳遞一個(gè)指向環(huán)境字符串指針數(shù)組的指針
由于相對(duì)比較復(fù)雜且初學(xué)時(shí)并不常用,所以不做介紹,可以去本節(jié)開頭的鏈接去學(xué)習(xí)相關(guān)概念。