Linux系統編程——進程替換:exec 函數族

本文轉載自:http://blog.csdn.net/tennysonsky/article/details/46004367

在 Windows 平臺下,我們可以通過雙擊運行可執行程序,讓這個可執行程序成爲一個進程;而在 Linux 平臺,我們可以通過 ./ 運行,讓一個可執行程序成爲一個進程。


但是,如果我們本來就運行着一個程序(進程),我們如何在這個進程內部啓動一個外部程序,由內核將這個外部程序讀入內存,使其執行起來成爲一個進程呢?這裏我們通過 exec 函數族實現。


exec 函數族,顧名思義,就是一簇函數,在 Linux 中,並不存在 exec() 函數,exec 指的是一組函數,一共有 6 個:

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. #include <unistd.h>  
  2. int execl(const char *path, const char *arg, ...);  
  3. int execlp(const char *file, const char *arg, ...);  
  4. int execle(const char *path, const char *arg, ..., char * const envp[]);  
  5. int execv(const char *path, char *const argv[]);  
  6. int execvp(const char *file, char *const argv[]);  
  7. int execve(const char *path, char *const argv[], char *const envp[]);  


其中只有 execve() 是真正意義上的系統調用,其它都是在此基礎上經過包裝的庫函數。


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() 示例代碼:

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3.   
  4. int main(int argc, char *argv[])  
  5. {  
  6.     printf("before exec\n\n");  
  7.       
  8.     /* /bin/ls:外部程序,這裏是/bin目錄的 ls 可執行程序,必須帶上路徑(相對或絕對) 
  9.        ls:沒有意義,如果需要給這個外部程序傳參,這裏必須要寫上字符串,至於字符串內容任意 
  10.        -a,-l,-h:給外部程序 ls 傳的參數 
  11.        NULL:這個必須寫上,代表給外部程序 ls 傳參結束 
  12.     */  
  13.     execl("/bin/ls""ls""-a""-l""-h", NULL);  
  14.       
  15.     // 如果 execl() 執行成功,下面執行不到,因爲當前進程已經被執行的 ls 替換了  
  16.     perror("execl");  
  17.     printf("after exec\n\n");  
  18.       
  19.     return 0;  
  20. }  


運行結果如下:



execv()示例代碼:

execv() 和 execl() 的用法基本是一樣的,無非將列表傳參,改爲用指針數組

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3.   
  4. int main(int argc, char *argv[])  
  5. {  
  6.     // execv() 和 execl() 的用法基本是一樣的,無非將列表傳參,改爲用指針數組  
  7.     // execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);  
  8.       
  9.     /* 指針數組 
  10.        ls:沒有意義,如果需要給這個外部程序傳參,這裏必須要寫上字符串,至於字符串內容任意 
  11.        -a,-l,-h:給外部程序 ls 傳的參數 
  12.        NULL:這個必須寫上,代表給外部程序 ls 傳參結束 
  13.     */  
  14.     char *arg[]={"ls""-a""-l""-h", NULL};  
  15.       
  16.     // /bin/ls:外部程序,這裏是/bin目錄的 ls 可執行程序,必須帶上路徑(相對或絕對)  
  17.     // arg: 上面定義的指針數組地址  
  18.     execv("/bin/ls", arg);  
  19.       
  20.     perror("execv");  
  21.           
  22.     return 0;  
  23. }  


execlp() 或 execvp() 示例代碼:

execlp() 和 execl() 的區別在於,execlp() 指定的可執行程序可以不帶路徑名,如果不帶路徑名的話,會在環境變量 PATH指定的目錄裏尋找這個可執行程序,而 execl() 指定的可執行程序,必須帶上路徑名。

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3.   
  4. int main(int argc, char *argv[])  
  5. {  
  6.     // 第一個參數 "ls",沒有帶路徑名,在環境變量 PATH 裏尋找這個可執行程序  
  7.     // 其它參數用法和 execl() 一樣  
  8.     execlp("ls""ls""-a""-l""-h", NULL);  
  9.       
  10.     /* 
  11.     char *arg[]={"ls", "-a", "-l", "-h", NULL}; 
  12.     execvp("ls", arg); 
  13.     */  
  14.       
  15.     perror("execlp");  
  16.       
  17.     return 0;  
  18. }  


execle() 或 execve() 示例代碼:

execle() 和 execve() 改變的是 exec 啓動的程序的環境變量(只會改變進程的環境變量,不會影響系統的環境變量),其他四個函數啓動的程序則使用默認系統環境變量。


execle()示例代碼:

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <stdlib.h> // getenv()  
  4.   
  5. int main(int argc, char *argv[])  
  6. {  
  7.     // getenv() 獲取指定環境變量的值  
  8.     printf("before exec:USER=%s, HOME=%s\n", getenv("USER"), getenv("HOME"));  
  9.       
  10.     // 指針數據  
  11.     char *env[]={"USER=MIKE""HOME=/tmp", NULL};  
  12.       
  13.     /* ./mike:外部程序,當前路徑的 mike 程序,通過 gcc mike.c -o mike 編譯 
  14.         mike:這裏沒有意義 
  15.         NULL:給 mike 程序傳參結束 
  16.         env:改變 mike 程序的環境變量,正確來說,讓 mike 程序只保留 env 的環境變量 
  17.      */  
  18.     execle("./mike""mike", NULL, env);  
  19.       
  20.     /* 
  21.     char *arg[]={"mike", NULL};      
  22.     execve("./mike", arg, env);  
  23.     */  
  24.       
  25.     perror("execle");  
  26.       
  27.     return 0;  
  28. }  


外部程序,mike.c 示例代碼:

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4.   
  5. int main(int argc, char *argv[])  
  6. {  
  7.     printf("\nin the mike fun, after exec: \n");  
  8.     printf("USER=%s\n", getenv("USER"));  
  9.     printf("HOME=%s\n", getenv("HOME"));  
  10.       
  11.     return 0;  
  12. }  

運行結果如下:



本教程示例代碼下載請點此處。

發佈了41 篇原創文章 · 獲贊 145 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章