【Linux系统编程】进程替换:exec 函数族
00. 目錄
文章目錄
- 00. 目錄
- 01. exec函數(shù)族
- 02. 參考示例
- 2.1 execl函數(shù)示例
- 2.2 execv函數(shù)示例
- 2.3 execlp() 或 execvp()函數(shù)示例
- 2.4 execle() 或 execve()函數(shù)示例
- 03. 附錄
01. exec函數(shù)族
在 Windows 平臺(tái)下,我們可以通過雙擊運(yùn)行可執(zhí)行程序,讓這個(gè)可執(zhí)行程序成為一個(gè)進(jìn)程;而在 Linux 平臺(tái),我們可以通過 ./ 運(yùn)行,讓一個(gè)可執(zhí)行程序成為一個(gè)進(jìn)程。
但是,如果我們本來就運(yùn)行著一個(gè)程序(進(jìn)程),我們?nèi)绾卧谶@個(gè)進(jìn)程內(nèi)部啟動(dòng)一個(gè)外部程序,由內(nèi)核將這個(gè)外部程序讀入內(nèi)存,使其執(zhí)行起來成為一個(gè)進(jìn)程呢?這里我們通過 exec 函數(shù)族實(shí)現(xiàn)。
exec 函數(shù)族,顧名思義,就是一簇函數(shù),在 Linux 中,并不存在 exec() 函數(shù),exec 指的是一組函數(shù),一共有 6 個(gè):
#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[]);其中只有 execve() 是真正意義上的系統(tǒng)調(diào)用,其它都是在此基礎(chǔ)上經(jīng)過包裝的庫(kù)函數(shù)。
exec 函數(shù)族提供了六種在進(jìn)程中啟動(dòng)另一個(gè)程序的方法。exec 函數(shù)族的作用是根據(jù)指定的文件名或目錄名找到可執(zhí)行文件,并用它來取代調(diào)用進(jìn)程的內(nèi)容,換句話說,就是在調(diào)用進(jìn)程內(nèi)部執(zhí)行一個(gè)可執(zhí)行文件。
進(jìn)程調(diào)用一種 exec 函數(shù)時(shí),該進(jìn)程完全由新程序替換,而新程序則從其 main 函數(shù)開始執(zhí)行。因?yàn)檎{(diào)用 exec 并不創(chuàng)建新進(jìn)程,所以前后的進(jìn)程 ID (當(dāng)然還有父進(jìn)程號(hào)、進(jìn)程組號(hào)、當(dāng)前工作目錄……)并未改變。exec 只是用另一個(gè)新程序替換了當(dāng)前進(jìn)程的正文、數(shù)據(jù)、堆和棧段(進(jìn)程替換)。
exec 函數(shù)族的 6 個(gè)函數(shù)看起來似乎很復(fù)雜,但實(shí)際上無論是作用還是用法都非常相似,只有很微小的差別。
l(list):參數(shù)地址列表,以空指針結(jié)尾。
v(vector):存有各參數(shù)地址的指針數(shù)組的地址。
p(path):按 PATH 環(huán)境變量指定的目錄搜索可執(zhí)行文件。
e(environment):存有環(huán)境變量字符串地址的指針數(shù)組的地址。
exec 函數(shù)族裝入并運(yùn)行可執(zhí)行程序 path/file,并將參數(shù) arg0 ( arg1, arg2, argv[], envp[] ) 傳遞給此程序。
exec 函數(shù)族與一般的函數(shù)不同,exec 函數(shù)族中的函數(shù)執(zhí)行成功后不會(huì)返回,而且,exec 函數(shù)族下面的代碼執(zhí)行不到。只有調(diào)用失敗了,它們才會(huì)返回 -1,失敗后從原程序的調(diào)用點(diǎn)接著往下執(zhí)行。
02. 參考示例
2.1 execl函數(shù)示例
#include <stdio.h> #include <unistd.h>int main(int argc, char *argv[]) {printf("before exec\n");/* /bin/ls:外部程序,這里是/bin目錄的 ls 可執(zhí)行程序,必須帶上路徑(相對(duì)或絕對(duì))ls:沒有意義,如果需要給這個(gè)外部程序傳參,這里必須要寫上字符串,至于字符串內(nèi)容任意-a,-l,-h:給外部程序 ls 傳的參數(shù)NULL:這個(gè)必須寫上,代表給外部程序 ls 傳參結(jié)束*/execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);// 如果 execl() 執(zhí)行成功,下面執(zhí)行不到,因?yàn)楫?dāng)前進(jìn)程已經(jīng)被執(zhí)行的 ls 替換了perror("execl");printf("after exec\n");return 0; }執(zhí)行結(jié)果:
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out before exec總用量 23K drwxrwxrwx 1 root root 4.0K 11月 5 15:38 . drwxrwxrwx 1 root root 4.0K 11月 4 18:05 .. -rwxrwxrwx 1 root root 576 11月 5 15:38 1.c -rwxrwxrwx 1 root root 704 11月 5 13:42 1.c.bak drwxrwxrwx 1 root root 4.0K 11月 3 20:46 4sys -rwxrwxrwx 1 root root 8.2K 11月 5 15:38 a.out deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$2.2 execv函數(shù)示例
execv() 和 execl() 的用法基本是一樣的,無非將列表傳參,改為用指針數(shù)組。
#include <stdio.h> #include <unistd.h>int main(int argc, char *argv[]) {// execv() 和 execl() 的用法基本是一樣的,無非將列表傳參,改為用指針數(shù)組// execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);/* 指針數(shù)組ls:沒有意義,如果需要給這個(gè)外部程序傳參,這里必須要寫上字符串,至于字符串內(nèi)容任意-a,-l,-h:給外部程序 ls 傳的參數(shù)NULL:這個(gè)必須寫上,代表給外部程序 ls 傳參結(jié)束*/char *arg[]={"ls", "-a", "-l", "-h", NULL};// /bin/ls:外部程序,這里是/bin目錄的 ls 可執(zhí)行程序,必須帶上路徑(相對(duì)或絕對(duì))// arg: 上面定義的指針數(shù)組地址execv("/bin/ls", arg);perror("execv");return 0; }測(cè)試結(jié)果:
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out 總用量 23K drwxrwxrwx 1 root root 4.0K 11月 5 15:40 . drwxrwxrwx 1 root root 4.0K 11月 4 18:05 .. -rwxrwxrwx 1 root root 643 11月 5 15:40 1.c -rwxrwxrwx 1 root root 576 11月 5 15:38 1.c.bak drwxrwxrwx 1 root root 4.0K 11月 3 20:46 4sys -rwxrwxrwx 1 root root 8.2K 11月 5 15:40 a.out deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$2.3 execlp() 或 execvp()函數(shù)示例
execlp() 和 execl() 的區(qū)別在于,execlp() 指定的可執(zhí)行程序可以不帶路徑名,如果不帶路徑名的話,會(huì)在環(huán)境變量 PATH指定的目錄里尋找這個(gè)可執(zhí)行程序,而 execl() 指定的可執(zhí)行程序,必須帶上路徑名。
#include <stdio.h> #include <unistd.h>int main(int argc, char *argv[]) {// 第一個(gè)參數(shù) "ls",沒有帶路徑名,在環(huán)境變量 PATH 里尋找這個(gè)可執(zhí)行程序// 其它參數(shù)用法和 execl() 一樣execlp("ls", "ls", "-a", "-l", "-h", NULL);/*char *arg[]={"ls", "-a", "-l", "-h", NULL};execvp("ls", arg);*/perror("execlp");return 0; }執(zhí)行結(jié)果:
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out 總用量 22K drwxrwxrwx 1 root root 4.0K 11月 5 15:42 . drwxrwxrwx 1 root root 4.0K 11月 4 18:05 .. -rwxrwxrwx 1 root root 354 11月 5 15:42 1.c -rwxrwxrwx 1 root root 643 11月 5 15:40 1.c.bak drwxrwxrwx 1 root root 4.0K 11月 3 20:46 4sys -rwxrwxrwx 1 root root 8.2K 11月 5 15:42 a.out deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$2.4 execle() 或 execve()函數(shù)示例
execle() 和 execve() 改變的是 exec 啟動(dòng)的程序的環(huán)境變量(只會(huì)改變進(jìn)程的環(huán)境變量,不會(huì)影響系統(tǒng)的環(huán)境變量),其他四個(gè)函數(shù)啟動(dòng)的程序則使用默認(rèn)系統(tǒng)環(huán)境變量。
#include <stdio.h> #include <unistd.h> #include <stdlib.h> // getenv()int main(int argc, char *argv[]) {// getenv() 獲取指定環(huán)境變量的值printf("before exec:USER=%s, HOME=%s\n", getenv("USER"), getenv("HOME"));// 指針數(shù)據(jù)char *env[]={"USER=TEST", "HOME=/tmp", NULL};/* ./test:外部程序,當(dāng)前路徑的 test 程序,通過 gcc test.c -o test 編譯test:這里沒有意義NULL:給 test 程序傳參結(jié)束env:改變 test 程序的環(huán)境變量,正確來說,讓 test 程序只保留 env 的環(huán)境變量*/execle("./test", "test", NULL, env);/*char *arg[]={"test", NULL}; execve("./test", arg, env); */perror("execle");return 0; }外部程序,test.c 示例代碼:
#include <stdio.h> #include <stdlib.h> #include <unistd.h>int main(int argc, char *argv[]) {printf("\nin the test fun, after exec: \n");printf("USER=%s\n", getenv("USER"));printf("HOME=%s\n", getenv("HOME"));return 0; }03. 附錄
3.1 參考博客:【Linux系統(tǒng)編程】進(jìn)程替換:exec 函數(shù)族
總結(jié)
以上是生活随笔為你收集整理的【Linux系统编程】进程替换:exec 函数族的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux系统编程】特殊进程之守护进程
- 下一篇: 【Linux系统编程】进程间通信概述