(1)exec函數(shù)族說(shuō)明
fork 函數(shù)是用于創(chuàng)建一個(gè)子進(jìn)程,,該子進(jìn)程幾乎拷貝了父進(jìn)程的全部?jī)?nèi)容,,但是,這個(gè)
新創(chuàng)建的進(jìn)程如何執(zhí)行呢,?這個(gè)exec 函數(shù)族就提供了一個(gè)在進(jìn)程中啟動(dòng)另一個(gè)程序執(zhí)行的
方法,。它可以根據(jù)指定的文件名或目錄名找到可執(zhí)行文件,并用它來(lái)取代原調(diào)用進(jìn)程的數(shù)據(jù)
段,、代碼段和堆棧段,,在執(zhí)行完之后,原調(diào)用進(jìn)程的內(nèi)容除了進(jìn)程號(hào)外,,其他全部被新的進(jìn)
程替換了,。另外,這里的可執(zhí)行文件既可以是二進(jìn)制文件,,也可以是Linux 下任何可執(zhí)行的
腳本文件,。
在 Linux 中使用exec函數(shù)族主要有兩種情況:
· 當(dāng)進(jìn)程認(rèn)為自己不能再為系統(tǒng)和用戶做出任何貢獻(xiàn)時(shí),就可以調(diào)用任何exec 函數(shù)族
讓自己重生,;
· 如果一個(gè)進(jìn)程想執(zhí)行另一個(gè)程序,,那么它就可以調(diào)用fork 函數(shù)新建一個(gè)進(jìn)程,然后
調(diào)用任何一個(gè)exec,,這樣看起來(lái)就好像通過(guò)執(zhí)行應(yīng)用程序而產(chǎn)生了一個(gè)新進(jìn)程,。(這種情況
非常普遍)
(2)exec函數(shù)族語(yǔ)法
實(shí)際上,在Linux 中并沒(méi)有exec()函數(shù),,而是有6 個(gè)以exec開(kāi)頭的函數(shù)族,,它們之間語(yǔ)
法有細(xì)微差別,在下面會(huì)詳細(xì)講解。
exec 函數(shù)族成員函數(shù)語(yǔ)法
所需頭文件#include <unistd.h>
函數(shù)原型 int execl(const char *path, const char *arg, ...)
int execv(const char *path, char *const argv[])
int execle(const char *path, const char *arg, ..., char *const envp[])
int execve(const char *path, char *const argv[], char *const envp[])
int execlp(const char *file, const char *arg, ...)
int execvp(const char *file, char *const argv[])
函數(shù)返回值-1:出錯(cuò)
這 6 個(gè)函數(shù)在函數(shù)名和使用語(yǔ)法的規(guī)則上都有細(xì)微的區(qū)別,,下面就可執(zhí)行文件查找方式,、
參數(shù)表傳遞方式及環(huán)境變量這幾個(gè)方面進(jìn)行比較。
· 查找方式
讀者可以注意到,,表7.3 中的前4 個(gè)函數(shù)的查找方式都是完整的文件目錄路徑,,而最后
2個(gè)函數(shù)(也就是以p結(jié)尾的兩個(gè)函數(shù))可以只給出文件名,系統(tǒng)就會(huì)自動(dòng)從環(huán)境變量“$PATH”
所指出的路徑中進(jìn)行查找,。
· 參數(shù)傳遞方式
exec函數(shù)族的參數(shù)傳遞有兩種方式:一種是逐個(gè)列舉的方式,,而另一種則是將所有參數(shù)
整體構(gòu)造指針數(shù)組傳遞。
在這里是以函數(shù)名的第5 位字母來(lái)區(qū)分的,,字母為“l”(list)的表示逐個(gè)列舉的方式,,
其語(yǔ)法為char *arg;字母為“v”(vertor)的表示將所有參數(shù)整體構(gòu)造指針數(shù)組傳遞,,其語(yǔ)法
為*const argv[],。讀者可以觀察execl、execle,、execlp的語(yǔ)法與execv,、execve、execvp的區(qū)別,。
它們具體的用法在后面的實(shí)例講解中會(huì)舉例說(shuō)明,。
這里的參數(shù)實(shí)際上就是用戶在使用這個(gè)可執(zhí)行文件時(shí)所需的全部命令選項(xiàng)字符串(包括
該可執(zhí)行程序命令本身)。要注意的是,,這些參數(shù)必須以NULL 表示結(jié)束,,如果使用逐個(gè)列
舉方式,那么要把它強(qiáng)制轉(zhuǎn)化成一個(gè)字符指針,,否則exec將會(huì)把它解釋為一個(gè)整型參數(shù),,如
果一個(gè)整型數(shù)的長(zhǎng)度char *的長(zhǎng)度不同,那么exec函數(shù)就會(huì)報(bào)錯(cuò),。
· 環(huán)境變量
exec 函數(shù)族可以默認(rèn)系統(tǒng)的環(huán)境變量,,也可以傳入指定的環(huán)境變量。這里以“e”
(Enviromen)結(jié)尾的兩個(gè)函數(shù)execle,、execve 就可以在envp[]中指定當(dāng)前進(jìn)程所使用的環(huán)境
變量,。
exec 函數(shù)名對(duì)應(yīng)含義
前4位統(tǒng)一為:execl:
第5位 l: 參數(shù)傳遞為逐個(gè)列舉方式execl、execle,、execlp
v:參數(shù)傳遞為構(gòu)造指針數(shù)組方式execv,、execve、execvp
第6位 e:可傳遞新進(jìn)程環(huán)境變量execle,、execve
p:可執(zhí)行文件查找方式為文件名execlp,、execvp
(3)exec使用實(shí)例
下面的第一個(gè)示例說(shuō)明了如何使用文件名的方式來(lái)查找可執(zhí)行文件,,同時(shí)使用參數(shù)列表
的方式,。這里用的函數(shù)是execlp,。
/*execlp.c*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
if(fork()==0){
/*調(diào)用execlp函數(shù),這里相當(dāng)于調(diào)用了“ps -ef”命令*/
if(execlp("ps","ps","-ef",NULL)<0)
perror("execlp error!");
}
}
在該程序中,,首先使用fork函數(shù)新建一個(gè)子進(jìn)程,,然后在子進(jìn)程里使用execlp函數(shù)。讀
者可以看到,,這里的參數(shù)列表就是在shell中使用的命令名和選項(xiàng),。并且當(dāng)使用文件名的方式
進(jìn)行查找時(shí),系統(tǒng)會(huì)在默認(rèn)的環(huán)境變量PATH 中尋找該可執(zhí)行文件,。
接下來(lái)的示例2 使用完整的文件目錄來(lái)查找對(duì)應(yīng)的可執(zhí)行文件,。注意目錄必須以“/”開(kāi)
頭,否則將其視為文件名,。
/*execl.c*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
if(fork()==0){
/*調(diào)用execl函數(shù),,注意這里要給出ps程序所在的完整路徑*/
if(execl("/bin/ps","ps","-ef",NULL)<0)
perror("execl error!");
}
}
示例3 利用函數(shù)execle,將環(huán)境變量添加到新建的子進(jìn)程中去,,這里的“env”是查看當(dāng)
前進(jìn)程環(huán)境變量的命令,,如下所示:
/*execle*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
/*命令參數(shù)列表,必須以NULL結(jié)尾*/
char *envp[]={"PATH=/tmp","USER=sunq",NULL};
if(fork()==0){
/*調(diào)用execle函數(shù),,注意這里也要指出env的完整路徑*/
if(execle("/bin/env","env",NULL,envp)<0)
perror("execle error!");
}
}
下載到目標(biāo)板后的運(yùn)行結(jié)果如下所示:
[root@(none) 1]# ./execle
PATH=/tmp
USER=sunq
最后一個(gè)示例使用execve函數(shù),,通過(guò)構(gòu)造指針數(shù)組的方式來(lái)傳遞參數(shù),注意參數(shù)列表一
定要以NULL作為結(jié)尾標(biāo)識(shí)符,。其代碼和運(yùn)行結(jié)果如下所示:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
/*命令參數(shù)列表,,必須以NULL結(jié)尾*/
char *arg[]={"env",NULL};
char *envp[]={"PATH=/tmp","USER=sunq",NULL};
if(fork()==0){
if(execve("/bin/env",arg,,envp)<0)
perror("execve error!");
}
}
下載到目標(biāo)板后的運(yùn)行結(jié)果如下所示:
[root@(none) 1]# ./execve
PATH=/tmp
USER=sunq
(4)exec函數(shù)族使用注意點(diǎn)
在使用exec 函數(shù)族時(shí),一定要加上錯(cuò)誤判斷語(yǔ)句,。因?yàn)閑xec 很容易執(zhí)行失敗,,其中最
常見(jiàn)的原因有:
· 找不到文件或路徑,此時(shí)errno被設(shè)置為ENOENT,;
· 數(shù)組 argv和envp忘記用NULL結(jié)束,,此時(shí)errno被設(shè)置為EFAULT;
· 沒(méi)有對(duì)應(yīng)可執(zhí)行文件的運(yùn)行權(quán)限,,此時(shí)errno被設(shè)置為EACCES,。