我的相關博文:
系統編程-行程-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;
}
編譯運行:

.
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/270517.html
標籤:嵌入式
