作者:王姍姍,華清遠見嵌入式學(xué)院講師。
exec用被執(zhí)行的程序完全替換調(diào)用它的程序的影像。fork創(chuàng)建一個新的進程就產(chǎn)生了一個新的PID,exec啟動一個新程序,替換原有的進程,因此這個新的被exec執(zhí)行的進程的PID不會改變,和調(diào)用exec函數(shù)的進程一樣。
下面來看下exec函數(shù)族:
#include <unistd.h>
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[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
exec函數(shù)族裝入并運行程序pathname,并將參數(shù)arg0(arg1,arg2,argv[],envp[])傳遞給子程序,出錯返回-1。在exec函數(shù)族中,后綴l、v、p、e添加到exec后,所指定的函數(shù)將具有某種操作能力有后綴:
其中只有execve是真正意義上的系統(tǒng)調(diào)用,其它都是在此基礎(chǔ)上經(jīng)過包裝的庫函數(shù)。
我們來看下面的一個實例:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main(int argc,char *argv[],char *envp[])
{
char *arg[]={"ls","-a",NULL};
if(fork()==0)
{
printf("execl...........n");
if(execl("/bin/ls","ls","-a",NULL)<0)
{
fprintf(stderr,"execl failed:%s",strerror(errno));
return -1;
}
}
if(fork()==0)
{
printf("execv...........n");
if(execv("/bin/ls",arg)<0)
{
fprintf(stderr,"execl failed:%sn",strerror(errno));
return -1;
}
}
if(fork()==0)
{
printf("execlp...........n");
if(execlp("ls","ls","-a",NULL)<0)
{
fprintf(stderr,"execl failed:%s",strerror(errno));
return -1;
}
}
if(fork()==0)
{
printf("execvp...........n");
if(execvp("ls",arg)<0)
{
fprintf(stderr,"execl failed:%sn",strerror(errno));
return -1;
}
}
if(fork()==0)
{
printf("execle...........n");
if(execle("/bin/ls","ls","-a",NULL,envp)<0)
{
fprintf(stderr,"execl failed:%s",strerror(errno));
return -1;
}
}
if(fork()==0)
{
printf("execve...........n");
if(execve("/bin/ls",arg,envp)<0)
{
fprintf(stderr,"execl failed:%sn",strerror(errno));
return -1;
}
}
return 0;
}
程序里調(diào)用了ls這個Linux常用的系統(tǒng)命令。
由于各個子進程執(zhí)行的順序無法控制,所以有可能出現(xiàn)一個比較混亂的輸出--各子進程打印的結(jié)果交雜在一起,而不是嚴(yán)格按照程序中列出的次序。
編譯并運行:
下面我們來分析下所寫的程序。
其實我們留心看一下這6個函數(shù),可以發(fā)現(xiàn)前3個函數(shù)都是以execl開頭的,后3個都是以execv開頭的。
首先來比較前兩個函數(shù)execv和execl。execv開頭的函數(shù)是把參數(shù)以"char *argv[]"這樣的形式傳遞命令行參數(shù)。而execl開頭的函數(shù)采用了我們更容易習(xí)慣的方式,把參數(shù)一個一個列出來,然后以一個NULL表示結(jié)束,也可以寫成(char *)0。
其次緊跟著的2個以p結(jié)尾的函數(shù)execlp和execvp。與其他幾個函數(shù)相比,除execlp和execvp之外的4個函數(shù)都要求,它們的第1個參數(shù)path必須是一個完整的路徑,如"/bin/ls";而execlp和execvp的第1個參數(shù)file可以簡單到僅僅是一個文件名,如"ls",這兩個函數(shù)可以自動到環(huán)境變量PATH制定的目錄里去尋找。
最后兩個函數(shù)execle和execve,都使用了char *envp[]來傳遞環(huán)境變量。在全部6個函數(shù)中,只有execle和execve需要傳遞環(huán)境變量,其它的4個函數(shù)都沒有這個參數(shù),這并不意味著它們不傳遞環(huán)境變量,這4個函數(shù)將把默認的環(huán)境變量不做任何修改地傳給被執(zhí)行的應(yīng)用程序。而execle和execve會用指定的環(huán)境變量去替代默認的那些。
最后要強調(diào)一點,大家在平時的編程中,如果用到了exec函數(shù)族,一定記得要加錯誤判斷語句。因為與其他系統(tǒng)調(diào)用比起來,exec很容易受傷,被執(zhí)行文件的位置,權(quán)限等很多因素都能導(dǎo)致該調(diào)用的失敗。最常見的錯誤是:
1. 找不到文件或路徑,此時errno被設(shè)置為ENOENT;
2. 數(shù)組argv和envp忘記用NULL結(jié)束,此時errno被設(shè)置為EFAULT;
3. 沒有對要執(zhí)行文件的運行權(quán)限,此時errno被設(shè)置為EACCES。
“本文由華清遠見http://www.embedu.org/index.htm提供”
來源:華清遠見11次