Linux系統編程——進程替換:exec 函數族
在 Windows 平臺下。我們能夠通過雙擊運行可運行程序。讓這個可運行程序成為一個進程;而在 Linux 平臺。我們能夠通過 ./ 運行,讓一個可運行程序成為一個進程。
可是,假設我們本來就執行著一個程序(進程),我們怎樣在這個進程內部啟動一個外部程序。由內核將這個外部程序讀入內存,使其執行起來成為一個進程呢?這裏我們通過 exec 函數族實現。
exec 函數族。顧名思義。就是一簇函數,在 Linux 中,並不存在 exec() 函數。exec 指的是一組函數。一共同擁有 6 個:
[cpp] view plaincopy
-
#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
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,失敗後從原程序的調用點接著往下運行。
excel代碼:
#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() 指定的可運行程序。必須帶上路徑名。
[cpp] view plaincopy
- #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=EDU", "HOME=/tmp", NULL}; /* ./edu:外部程序,當前路徑的 edu 程序,通過 gcc edu.c -o edu 編譯 edu:這裏沒有意義 NULL:給 edu 程序傳參結束 env:改變 edu 程序的環境變量,正確來說,讓 edu 程序僅僅保留 env 的環境變量 */ execle("./edu", "edu", NULL, env); /* char *arg[]={"edu", NULL}; execve("./edu", arg, env); */ perror("execle"); return 0; }
外部程序。edu.c 演示樣例代碼:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { printf("\nin the edu fun, after exec: \n"); printf("USER=%s\n", getenv("USER")); printf("HOME=%s\n", getenv("HOME")); return 0; }
執行結果:
Linux系統編程——進程替換:exec 函數族