本文轉載自:http://blog.csdn.net/tennysonsky/article/details/46004367
在 Windows 平臺下,我們可以通過雙擊運行可執行程序,讓這個可執行程序成爲一個進程;而在 Linux 平臺,我們可以通過 ./ 運行,讓一個可執行程序成爲一個進程。
但是,如果我們本來就運行着一個程序(進程),我們如何在這個進程內部啓動一個外部程序,由內核將這個外部程序讀入內存,使其執行起來成爲一個進程呢?這裏我們通過 exec 函數族實現。
exec 函數族,顧名思義,就是一簇函數,在 Linux 中,並不存在 exec() 函數,exec 指的是一組函數,一共有 6 個:
- #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 函數族提供了六種在進程中啓動另一個程序的方法。exec 函數族的作用是根據指定的文件名或目錄名找到可執行文件,並用它來取代調用進程的內容,換句話說,就是在調用進程內部執行一個可執行文件。
進程調用一種 exec 函數時,該進程完全由新程序替換,而新程序則從其 main 函數開始執行。因爲調用 exec
並不創建新進程,所以前後的進程 ID (當然還有父進程號、進程組號、當前工作目錄……)並未改變。exec 只是用另一個新程序替換了當前進程的正文、數據、堆和棧段(進程替換)。
exec
函數族的 6 個函數看起來似乎很複雜,但實際上無論是作用還是用法都非常相似,只有很微小的差別。
l(list):參數地址列表,以空指針結尾。
v(vector):存有各參數地址的指針數組的地址。
p(path):按 PATH 環境變量指定的目錄搜索可執行文件。
e(environment):存有環境變量字符串地址的指針數組的地址。
exec 函數族裝入並運行可執行程序 path/file,並將參數 arg0 ( arg1, arg2, argv[], envp[] ) 傳遞給此程序。
exec 函數族與一般的函數不同,exec 函數族中的函數執行成功後不會返回,而且,exec 函數族下面的代碼執行不到。只有調用失敗了,它們纔會返回 -1,失敗後從原程序的調用點接着往下執行。
execl() 示例代碼:
- #include <stdio.h>
- #include <unistd.h>
- int main(int argc, char *argv[])
- {
- printf("before exec\n\n");
- /* /bin/ls:外部程序,這裏是/bin目錄的 ls 可執行程序,必須帶上路徑(相對或絕對)
- ls:沒有意義,如果需要給這個外部程序傳參,這裏必須要寫上字符串,至於字符串內容任意
- -a,-l,-h:給外部程序 ls 傳的參數
- NULL:這個必須寫上,代表給外部程序 ls 傳參結束
- */
- execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);
- // 如果 execl() 執行成功,下面執行不到,因爲當前進程已經被執行的 ls 替換了
- perror("execl");
- printf("after exec\n\n");
- return 0;
- }
運行結果如下:
execv()示例代碼:
execv() 和 execl() 的用法基本是一樣的,無非將列表傳參,改爲用指針數組。
- #include <stdio.h>
- #include <unistd.h>
- int main(int argc, char *argv[])
- {
- // execv() 和 execl() 的用法基本是一樣的,無非將列表傳參,改爲用指針數組
- // execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);
- /* 指針數組
- ls:沒有意義,如果需要給這個外部程序傳參,這裏必須要寫上字符串,至於字符串內容任意
- -a,-l,-h:給外部程序 ls 傳的參數
- NULL:這個必須寫上,代表給外部程序 ls 傳參結束
- */
- char *arg[]={"ls", "-a", "-l", "-h", NULL};
- // /bin/ls:外部程序,這裏是/bin目錄的 ls 可執行程序,必須帶上路徑(相對或絕對)
- // arg: 上面定義的指針數組地址
- execv("/bin/ls", arg);
- perror("execv");
- return 0;
- }
execlp() 或 execvp() 示例代碼:
execlp() 和 execl() 的區別在於,execlp() 指定的可執行程序可以不帶路徑名,如果不帶路徑名的話,會在環境變量 PATH指定的目錄裏尋找這個可執行程序,而 execl() 指定的可執行程序,必須帶上路徑名。
- #include <stdio.h>
- #include <unistd.h>
- int main(int argc, char *argv[])
- {
- // 第一個參數 "ls",沒有帶路徑名,在環境變量 PATH 裏尋找這個可執行程序
- // 其它參數用法和 execl() 一樣
- execlp("ls", "ls", "-a", "-l", "-h", NULL);
- /*
- char *arg[]={"ls", "-a", "-l", "-h", NULL};
- execvp("ls", arg);
- */
- perror("execlp");
- return 0;
- }
execle() 或 execve() 示例代碼:
execle() 和 execve() 改變的是 exec 啓動的程序的環境變量(只會改變進程的環境變量,不會影響系統的環境變量),其他四個函數啓動的程序則使用默認系統環境變量。
execle()示例代碼:
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h> // getenv()
- int main(int argc, char *argv[])
- {
- // getenv() 獲取指定環境變量的值
- printf("before exec:USER=%s, HOME=%s\n", getenv("USER"), getenv("HOME"));
- // 指針數據
- char *env[]={"USER=MIKE", "HOME=/tmp", NULL};
- /* ./mike:外部程序,當前路徑的 mike 程序,通過 gcc mike.c -o mike 編譯
- mike:這裏沒有意義
- NULL:給 mike 程序傳參結束
- env:改變 mike 程序的環境變量,正確來說,讓 mike 程序只保留 env 的環境變量
- */
- execle("./mike", "mike", NULL, env);
- /*
- char *arg[]={"mike", NULL};
- execve("./mike", arg, env);
- */
- perror("execle");
- return 0;
- }
外部程序,mike.c 示例代碼:
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- int main(int argc, char *argv[])
- {
- printf("\nin the mike fun, after exec: \n");
- printf("USER=%s\n", getenv("USER"));
- printf("HOME=%s\n", getenv("HOME"));
- return 0;
- }
運行結果如下: