目錄
一、說在前面
fork函數創建新的子進程後,子進程往往需要調用一個 exec 函數來執行另外一個程序。當進程調用一種 exec 函數時,該進程執行的程序完全替換爲新程序,而新程序則從main函數開始執行。因爲調用 exec 函數並不創建新的進程,所以前後的進程ID並未改變。exec只是用磁盤上的一個新程序替換了當前進程的正文段、數據段、堆段和棧段。
二、函數介紹
2.1、函數原型
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...
/* (char *) NULL */);
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
int execle(const char *path, const char *arg, ...
/*, (char *) NULL, char * const envp[] */);
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[]);
#include <unistd.h>
int fexecve(int fd, char *const argv[], char *const envp[]);
2.2、函數功能
exec 函數族的作用是運行第一個參數指定的可執行程序。但其工作過程與 fork 完全不同, fork 是在複製一份原進程,而 exec 函數執行第一個參數指定的可執行程序之後,這個新程序運行起來後也是一個進程,而這個進程會覆蓋原有進程空間,即原有進程的所有內容都被新運行起來的進程全部覆蓋了,所以 exec 函數後面的所有代碼都不再執行,但它之前的代碼當然是可以被執行的。
2.3、函數參數
path 是包括執行文件名的全路徑名 file 既可是全路徑名也可是可執行文件名;
最後一個取文件描述符作爲參數
arg 是可執行文件的全部命令行參數,可以用多個,注意最後一個參數必須爲 NULL;
argv 是一個字符串的數組 char *argv[]={“full path”,”param1”,”param2”,...NULL};
envp 指定新進程的環境變量 char *envp[]={“name1=val1”,”name2=val2”,...NULL};
2.3.1參數傳遞方式
exec函數族的參數傳遞方式: 第一種是逐個列舉的方式;
第二種則是將所有參數整體構造指針數組傳遞。
在這裏是以函數名的第 5 位字母來區分的:
1、字母爲“l”(list)的表示逐個列舉的方式,其語法爲 char *arg;
2、字母爲“v” (vertor)的表示將所有命令行參數整體構造指針數組傳遞,其語法爲 char *const argv[]。
2.3.2 區別與不同
以字母 p 結尾的函數通過搜索系統 PATH 這個環境變量來查找新程序的可執行文件的路徑。如果可執行程序不在 PATH 定義的路徑中,我們就需要把包括目錄在內的使用絕對路徑的文件名作爲參數傳遞給函數。
以字母 e 結尾的函數通過傳遞 envp 傳遞字符串數組作爲新程序的環境變量。新進程中的全局變量 environ 指針指向的環境變量數組將會被 envp 中的內容替代。
注意:對於有參數 envp 的函數,它會使用程序員自定義的環境變量,如果自定義的環境變量中包含了將要執行的可執行程序的路徑,那麼第一個參數中是不是我們就可以不用寫全路徑了呢?不是的,必須寫全路徑。因爲我們自定義的環境變量不是用來尋找這個可執行程序的,而是在這個可執行程序運行起來之後給新進程用的。
可以用 env 命令查看環境變量。
2.4 函數關係
三、實例歷程
3.1 execl 示例:
#include <unistd.h>
#include <stdio.h>
int main()
{
printf("aaaa\n");
execl("/bin/ls", "ls", "-l", NULL);
printf("bbbb\n");
printf("bbbb\n");
return 0;
}
3.2 execlp 示例:
(在上面的基礎上去掉了 ls 的路徑/bin/)
#include <unistd.h>
#include <stdio.h>
int main()
{
printf("aaaa\n");
execlp("ls", "ls", "-l", NULL);
printf("bbbb\n");
printf("bbbb\n");
return 0;
}
3.3 execv 示例:
(把"ls","-l",NULL 這些命令行參數通過指針數組 str 傳給 exec 函數)
#include <unistd.h>
#include <stdio.h>
int main()
{
char *str[] = {"ls", "-l", NULL};
printf("aaaa\n");
execv("/bin/ls", str);
printf("bbbb\n");
printf("bbbb\n");
return 0;
}
3.4 execvp 示例:
(在上面的基礎上去掉了 ls 的路徑/bin/)
#include <unistd.h>
#include <stdio.h>
int main()
{
char *str[] = {"ls", "-l", NULL};
printf("aaaa\n");
execvp("ls", str);
printf("bbbb\n");
printf("bbbb\n");
return 0;
}
3.5execle 示例:
#include <unistd.h>
#include <stdio.h>
int main()
{
char *arrEnv[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
printf("aaaa\n");
execle("/bin/ls", "ls", "-l", NULL, arrEnv);
printf("bbbb\n");
printf("bbbb\n");
return 0;
}
3.6 execve 示例:
#include <unistd.h>
#include <stdio.h>
int main()
{
char *str[] = {"ls", "-l", NULL};
char *arrEnv[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
printf("aaaa\n");
execve("/bin/ls", str, arrEnv);
printf("bbbb\n");
printf("bbbb\n");
return 0;
}
3.7 其它示例:
get_arg.c 測試 environ,用來輸出本進程默認的環境變量
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
extern char **environ; //聲明該變量
int main(int argc,char *argv[])
{
int i=0;
for(;i<argc;i++)
printf("arg%d:%s\n",i,argv[i]);
char **ppEnv=environ;
while(ppEnv && *ppEnv)
printf("%s\n",*ppEnv++);
return 0;
}
四、總結
exec 函數族的作用是根據指定的文件名找到可執行文件,並用它來取代調用進程的內容,換句話說,就是在調用進程內部執行一個可執行文件。這裏的可執行文件既可以是二進制文件,也可以是任何 Linux 下可執行的腳本文件,如果不是可以執行的文件,那麼就解釋成爲一個 shell 文件, sh **執行!
上面 7 條函數看起來似乎很複雜,但實際上無論是作用還是用法都非常相似,只有很微小的差別。
參數 argc 指出了運行該程序時命令行參數的個數,數組 argv 存放了所有的命令行參數,數組 envp 存放了所有的環境變量。環境變量指的是一組值,從用戶登錄後就一直存在,很多應用程序需要依靠它來確定系統的一些細節,我們最常見的環境變量是 PATH,它指出了應到哪裏去搜索應用程序,如 /bin; HOME 也是比較常見的環境變量,它指出了我們在系統中的個人目錄。環境變量一般以字符串"XXX=xxx"的形式存在, XXX 表示變量名, xxx 表示變量的值。
值得一提的是, argv 數組和 envp 數組存放的都是指向字符串的指針,這兩個數組都以一個 NULL 元素表示數組的結尾。
前 3 個函數都是以 execl 開頭的,後 3 個都是以 execv 開頭的,它們的區別在於, execv 開頭的函數是以"char
*argv[]"這樣的形式傳遞命令行參數,而 execl 開頭的函數採用了我們更容易習慣的方式,把參數一個一個列出來,
然後以一個 NULL 表示結束。這裏的 NULL 的作用和 argv 數組裏的 NULL 作用是一樣的。
這裏建議使用 (char *)0 代替 NULL。
在全部 7 個函數中,只有 execle、 execve 和 fexecve使用了 char *envp[]傳遞環境變量,其它的 4 個函數都沒有這個參
數,這並不意味着它們不傳遞環境變量,這 4 個函數將把默認的環境變量不做任何修改地傳給被執行的應用程序。而 execle 和 execve 和 fexecve會用指定的環境變量去替代默認的那些。
還有 2 個以 p 結尾的函數 execlp 和 execvp,咋看起來,它們和 execl 與 execv 的差別很小,事實也確是如此,除 execlp 和 execvp 之外的 4 個函數都要求,它們的第 1 個參數 path 必須是一個完整的路徑,如"/bin/ls";而 execlp和 execvp 的第 1 個參數 file 可以簡單到僅僅是一個文件名,如 "ls",這兩個函數可以自動到環境變量 PATH 制定的目錄裏去尋找。