系統編程-進程-exec系列函數超級詳解(帶各種實操代碼)


我的相關博文:

系統編程-進程-close-on-exec機制

 

PART1 

exec系列函數功能簡介

 

exec系列函數登場

常規操作是先fork一個子進程,然后在子進程中調用exec系列函數執行新的目標程序,

雖然exec系列函數執行成功不返回,但是我們仍然i要使用wait或waitpid讓父進程給該子進程收屍,否則將會產生一個僵屍進程(子進程死了,老爸沒給收屍,子成為僵屍)!

並且,不論子進程內的exec系列函數執行成功或是失敗,我們都要在父進程給對其收屍!

待實驗,見實驗1(實驗1,使用execl),  思路:

讓子進程內調用exec系列函數執行的新程序的生命周期大概是5秒,觀察父進程wait成功且執行到wait后面的打印語句的時間,判斷是否也為5秒。

 

該系列函數辨識方法

該系列函數都以“exec”為前綴,后面的字母有各自固定的含義,可以根據這點來進行區分,而無需強行記憶。看下圖詳解:

補充知識點:

讀完上面的小結,我們可以分析出,例如execl,其第一個參數pathname,必須要求是絕對路徑。

 

exec系列函數關系剖析

 

注意事項:

如果代碼想下圖這樣寫,因為exec函數執行出錯,但是后續代碼仍然會被執行,可是:當前進程的內存空間(堆、棧、數據區)可能已經被破壞,所以這種寫法是不妥的!

上圖代碼不妥,應該修改為下圖方式,即設置進程退出:

同時,若exec執行成功,則后續代碼不會被執行。

 

PART2

實驗部分 

實驗0

實驗目的:execl錯誤使用展示

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h>

char* elf_name = "do cat"; char* path1 = "ls";      // 相對路徑
char* path2 = "/bin/ls"; // 絕對路徑

char* argv1 = "-al"; char* argv2 = "a.out"; int main(void) { int status = 0; pid_t pid; printf("main-process-pid: %ld\n", (long)getpid()); pid = fork(); if (pid < 0) { printf("fork error"); } else if (pid == 0) { printf("son-process-pid: %ld\n", (long)getpid()); if(execl(path1, elf_name, argv1, argv2, NULL)<0){
        perror("execl error"); exit(1); }else{ printf("execl %s success\n", elf_name); } } wait(NULL); printf("-------main process ending------\n"); return 0; }

編譯運行:

錯誤原因:execl的第一個參數不支持是相對路徑,所以上述實驗中execl的第一個參數應該是path2 .

 

實驗1

實驗目的1: execl使用展示

實驗目的2:

雖然exec系列函數執行成功不返回,但是我們仍然要使用wait或waitpid讓父進程給該子進程收屍,否則將會產生一個僵屍進程(子進程死了,老爸沒給收屍,子成為僵屍)!

並且,不論子進程內的exec系列函數執行成功或是失敗,我們都要在父進程給對其收屍!

讓子進程內調用exec系列函數執行的新程序的生命周期大概是5秒,觀察父進程wait成功且執行到wait后面的打印語句的時間,判斷是否也為5秒。

屏蔽父進程內的wait函數與否將產生不同的效果,可使用ps -aux查看子進程是否變成了僵屍進程。

 

為了承接本文章上下文的連續性,將實驗1分為三個小實驗,逐步加深理解。

 

實驗1-1: 驗證僵屍進程的產生

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h>

char* elf_name = "do cat"; char* path1 = "ls";      // 相對路徑
char* path2 = "/bin/ls"; // 絕對路徑

char* argv1 = "-al"; char* argv2 = "a.out"; int main(void) { int status = 0; pid_t pid; printf("main-process-pid: %ld\n", (long)getpid()); pid = fork(); if (pid < 0) { printf("fork error"); } else if (pid == 0) { printf("son-process-pid: %ld\n", (long)getpid()); if(execl(path2, elf_name, argv1, argv2, NULL)<0){ perror("execl error"); exit(1); }else{ printf("execl %s success\n", elf_name); } } //wait(NULL); 這里不回收子進程
    while(1); return 0; }

在一個終端內編譯運行:

可見,子進程的進程ID是21018, 我們來看看在父進程不回收子進程,而子進程內又使用execl執行完畢了新程序后,是否會產生僵屍進程吧!

在另一個終端內查看:

實驗證實,子進程21018成為了僵屍進程。

同時,printf(“exec %s success\n”, elf_name)這句代碼未打印,由此,我們也可以看出,通過execl執行的新程序正常執行是不會返回給主程序的,

實際上,通過整個exec系列函數成功執行新程序,都是不會返回給主程序的。

然而,父進程fork了子進程后就要遵循給其收屍的原則,即使使用了exec系列函數,也是如此。

 

相關知識點補充 - 進程狀態及其標識 :

 

實驗1-2

在實驗1-1的代碼基礎上,父進程fork子進程后增加wait函數的使用,以用於對子進程的回收。此時我們重復實驗1-1的操作過程,我們不會見到子進程成為僵屍進程。

 本實驗和1-1極其相似,故省略。

 

實驗1-3

hello.c :

#include <stdio.h> #include <unistd.h>

int main(){ sleep(5); printf("---<hello>elf, is ending---\n"); return 0; }

exec.c:

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h>

char* elf_name = "do hello"; char* path = "/home/lmw/MINE/Linux_C_exercise/fork/exec/hello"; // 絕對路徑

int main(void) { int status = 0; pid_t pid; printf("main-process-pid: %ld\n", (long)getpid()); pid = fork(); if (pid < 0) { printf("fork error"); } else if (pid == 0) { printf("son-process-pid: %ld\n", (long)getpid()); if(execl(path, elf_name, NULL, NULL, NULL)<0){ perror("execl error"); exit(1); }else{ printf("execl %s success\n", elf_name); } } wait(NULL); printf("father waits son successfully \n"); return 0; }

編譯運行:

通過實驗可見,可執行程序hello被成功執行起來了並且在5秒后退出(讀者可以自行設置為6秒或者7秒或者8秒...),

同時,主進程內的wait調用也在5秒后成功返回(通過肉眼觀察代碼執行效果得出),表明在ecex函數裝載的新程序結束后,父進程就對其展開收屍動作了。

本1-3實驗可以進一步加深我們對fork exec wait等api進行混合使用的理解。

 

 

實驗2 

實驗目的:execvp使用展示( 本質和execl一樣,都是為了調用新程序去執行,只是使用的方式不一樣而已 )

根據前面的介紹,exec后面的v表示argv,所以execvp有一個參數是char* argv[]. 后面的p表示path,且支持相對路徑,但是該相對路徑必須要在系統的環境表中,

Linux下查看系統環境表中的路徑:

 

 

實驗3  system介紹,自己編寫功能更為強大的mysystem(自己手動調用exec系列函數可在需要時攜帶更多參數)。

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h>

char* test_cmd = "ps"; void mysystem(char* cmd){ pid_t pid; //printf("main-process-pid: %ld\n", (long)getpid());
 pid = fork(); if (pid < 0) { printf("fork error"); } else if (pid == 0) { //printf("son-process-pid: %ld\n", (long)getpid());
        if(execlp("bash", "I am bash", "-c", cmd, NULL)<0){ perror("execl error"); exit(1); } //printf("mysystem success \n");
 } wait(NULL); } int main(void) { mysystem(test_cmd); return 0; }

編譯運行:

 

 

 

 

.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM