三:execve系統調用 int execve(const char *filename, char *const argv[],char *const envp[]); fork創建了一個新的進程,產生一個新的PID execve用被執行的程序完全替換了調用進程的映像。 execve啟動一個新程序,替換原有進程,所以被執行進程的PID不會改變。 execve函數接受三個參數 --path 要執行的文件完整路徑 --argv 傳遞給程序完成參數列表,包括argv[0],它一般是執行程序的名字,最后一個參數一般是NULL --envp 是指向執行execed程序的環境指針,一般設為NULL
//execve函數的定義 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> int main(int arg, char * args[]) { /* 第一個參數是程序的名字,第二個參數是被調用程序的參數,最后一個參數必須是NULL 這個跟main函數的參數args數組很相似 */ char * argv[]={"/bin/ls","-l",NULL}; execve("/bin/ls",argv,NULL); /* execve函數是替換原來的程序代碼,但是進程PID不會變,文件描述符不會變,只是程序代碼被替換了 所以execve函數后面的語句都不會被執行 */ printf("program is end!\n"); return 0; }
wait和waitpid函數可以收集子進程的退出狀態 pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options); stauts保存子進程的退出狀態(本質上就是子進程的返回值,但是我們不能直接查看這個返回值,必須用宏定義轉化一下) 在waitpid函數中,參數pid是等待進程的PID,他能夠接受一下四種值 "<-1" 等待任何PGID等於PID的絕對值子進程 "-1" 等待任何子進程(這個進程創建的子進程) "0" 等待任何PGID等於調用進程的子進程 ">0" 等待PID等於pid的子進程
//wait函數的定義 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main(int arg, char * args[]) { pid_t child=0; int status=0; child=fork(); if(child==-1) { printf("system is game over!\n"); return -1; } if(child==0) { //in child process printf("child is begining!\n"); sleep(3); printf("child is end , now is your turn!\n"); }else{ //in parent process printf("parent is begining , i am wait my son!\n"); wait(&status); printf("child status=%d\n",WEXITSTATUS(status)); printf("my son is end , now i am go !\n"); } printf("all is end!\n"); return 10; }
用fork或者execve函數創建一個新進程,為了收集新進程的退出狀態並且防止出現僵死進程(zombie process),父進程應該調用wait或者waitpid函數等待進程終止。
僵死進程
一個僵死進程是在父進程退出之前就終止的子進程(子進程在父進程之前終止,此時因為父進程還沒有退出,所以系統會保留子進程的pid,退出 狀態,返回給父進程有wait函數,如果父進程沒有wait函數,系統就會一直保持子進程的PID等信息,直到父進程退出)
之所以被稱為僵死進程是因為他雖然死掉了,但依然在進程表中存在。
子進程退出后分配給他的內存和其他資源都被釋放,但是子進程還在內核進程表保留一條,內核在父進程回收子進程的退出狀態前一直保留它。
有一個兩個僵死進程不算什么問題,但一旦一個程序頻繁執行fork或者execv卻又不能手機退出狀態,那么最終將會填滿進程表(內核進程表是有上限的),這會影響性能,可能導致系統重啟
孤兒進程
孤兒進程是一個父進程在調用wait或者waitpid之前就已經退出了(即父進程在子進程之前退出)。此時init進程成為子進程的父進程。
init進程成為子進程的父進程,收集子進程的退出狀態,從而避免出現僵死進程。
注意:任何一個進程都必須有父進程,init的父進程是0號進程,父進程與子進程是相對獨立的(子進程會拷貝進程控制器和代碼區,寫時拷貝數據區),父進程退出,子進程不一定會退出