【Linux系统编程】进程的控制:结束进程、等待进程结束
結束進程
首先,我們回顧一下 C 語言中 continue, break, return 的作用:
continue: 結束本次循環
break: 跳出整個循環,或跳出 switch() 語句
return: 結束當前函數
而我們可以通過 exit() 或 _exit() 來結束當前進程。
所需頭文件:
#include <stdlib.h>?
void exit(int value);
功能:
結束調用此函數的進程。
參數:
status:返回給父進程的參數(低 8 位有效),至于這個參數是多少根據需要來填寫。
返回值:
無
所需頭文件:
#include <unistd.h>
void _exit(int value);
功能:
結束調用此函數的進程。
參數:
status:返回給父進程的參數(低 8 位有效),至于這個參數是多少根據需要來填寫。
返回值:
無
exit() 和 _exit() 函數功能和用法是一樣的,無非時所包含的頭文件不一樣,還有的區別就是:exit()屬于標準庫函數,_exit()屬于系統調用函數。
下面的例子驗證調用 exit() 函數,會刷新 I/O 緩沖區(關于緩沖區的更多詳情,請看《淺談標準I/O緩沖區》):
#include <stdio.h> #include <stdlib.h> #include <unistd.h>int main(int argc, char *argv[]) {printf("hi, mike, you are so good"); // 打印,沒有換行符"\n"exit(0); // 結束進程,標準庫函數,刷新緩沖區,printf()的內容能打印出來// _exit(0); // 結束進程,系統調用函數,printf()的內容不會顯示到屏幕while(1); // 不讓程序結束return 0; }運行結果如下:
上面的例子,結束進程的時候改為調用 _exit(),代碼如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h>int main(int argc, char *argv[]) {printf("hi, mike, you are so good"); // 打印,沒有換行符"\n"//exit(0); // 結束進程,標準庫函數,刷新緩沖區,printf()的內容能打印出來_exit(0); // 結束進程,系統調用函數,printf()的內容不會顯示到屏幕while(1); // 不讓程序結束return 0; }printf() 的內容是不會顯示出來的,運行結果如下:
接下來,我們一起驗證一下結束函數( return )和結束進程( exit() )的區別。
測試代碼如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h>void fun() {sleep(2);return; // 結束 fun() 函數while(1); }int main(int argc, char *argv[]) {fun();printf("after fun\n");while(1); // 不讓程序結束return 0; }運行結果如下:
通過上面的運行結果得知,return 的作用只是結束調用 return 的所在函數,只要這個函數不是主函數( main() ),只要主函數沒有結束,return 并不能結束進程。
我們將上面例子 fun() 里的 return 改為 exit():
#include <stdio.h> #include <stdlib.h> #include <unistd.h>void fun() {sleep(2);exit(0); // 結束當前進程while(1); }int main(int argc, char *argv[]) {fun();printf("after fun\n");while(1); // 不讓程序結束return 0; }exit() 是可以結束 fun() 所在的進程,即可讓程序結束運行,結果圖如下:
等待進程結束
當一個進程正常或異常終止時,內核就向其父進程發送 SIGCHLD 信號,相當于告訴父親他哪個兒子掛了,而父進程可以通過?wait() 或 waitpid() 函數等待子進程結束,獲取子進程結束時的狀態,同時回收他們的資源(相當于,父親聽聽死去兒子的遺言同時好好安葬它)。
wait() 和 waitpid() 函數的功能一樣,區別在于,wait() 函數會阻塞,waitpid() 可以設置不阻塞,waitpid() 還可以指定等待哪個子進程結束。
所需頭文件:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
功能:
等待任意一個子進程結束,如果任意一個子進程結束了,此函數會回收該子進程的資源。
在每個進程退出的時候,內核釋放該進程所有的資源、包括打開的文件、占用的內存等。但是仍然為其保留一定的信息,這些信息主要主要指進程控制塊的信息(包括進程號、退出狀態、運行時間等)。
調用 wait() 函數的進程會掛起(阻塞),直到它的一個子進程退出或收到一個不能被忽視的信號時才被喚醒(相當于繼續往下執行)。
若調用進程沒有子進程,該函數立即返回;若它的子進程已經結束,該函數同樣會立即返回,并且會回收那個早已結束進程的資源。
所以,wait()函數的主要功能為回收已經結束子進程的資源。
參數:
status:?進程退出時的狀態信息。
如果參數 status 的值不是 NULL,wait() 就會把子進程退出時的狀態取出并存入其中,這是一個整數值(int),指出了子進程是正常退出還是被非正常結束的。
這個退出信息在一個 int 中包含了多個字段,直接使用這個值是沒有意義的,我們需要用宏定義取出其中的每個字段。
下面我們來學習一下其中最常用的兩個宏定義,取出子進程的退出信息:
WIFEXITED(status)
如果子進程是正常終止的,取出的字段值非零。
WEXITSTATUS(status)
返回子進程的退出狀態,退出狀態保存在 status 變量的 8~16 位。在用此宏前應先用宏 WIFEXITED 判斷子進程是否正常退出,正常退出才可以使用此宏。
返回值:
成功:已經結束子進程的進程號
失敗:-1
從本質上講,系統調用 waitpid() 和 wait() 的作用是完全相同的,但 waitpid() 多出了兩個可由用戶控制的參數 pid 和 options,從而為我們編程提供了另一種更靈活的方式。
pid_t waitpid(pid_t pid, int *status,?int options);
功能:
等待子進程終止,如果子進程終止了,此函數會回收子進程的資源。
參數:
pid:?參數 pid 的值有以下幾種類型:
pid > 0
等待進程 ID 等于 pid 的子進程。
pid = 0
等待同一個進程組中的任何子進程,如果子進程已經加入了別的進程組,waitpid 不會等待它。
pid = -1
等待任一子進程,此時 waitpid 和 wait 作用一樣。
pid < -1
等待指定進程組中的任何子進程,這個進程組的 ID 等于 pid 的絕對值。
status:?進程退出時的狀態信息。和 wait() 用法一樣。
options:?options 提供了一些額外的選項來控制 waitpid()。
0:
同 wait(),阻塞父進程,等待子進程退出。
WNOHANG;
沒有任何已經結束的子進程,則立即返回。
WUNTRACED:
如果子進程暫停了則此函數馬上返回,并且不予以理會子進程的結束狀態。(由于涉及到一些跟蹤調試方面的知識,加之極少用到,這里就不多費筆墨了,有興趣的讀者可以自行查閱相關材料)
返回值:
waitpid() 的返回值比 wait() 稍微復雜一些,一共有 3 種情況:
當正常返回的時候,waitpid() 返回收集到的已經子進程的進程號;
如果設置了選項 WNOHANG,而調用中 waitpid() 發現沒有已退出的子進程可等待,則返回 0;
如果調用中出錯,則返回 -1,這時 errno 會被設置成相應的值以指示錯誤所在,如:當 pid 所對應的子進程不存在,或此進程存在,但不是調用進程的子進程,waitpid() 就會出錯返回,這時 errno 被設置為 ECHILD;
測試例子:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h>int main(int argc, char *argv[]) {pid_t pid;pid = fork(); // 創建進程if( pid < 0 ){ // 出錯perror("fork");exit(0);}if( pid == 0 ){// 子進程int i = 0;for(i=0;i<5;i++){printf("this is son process\n");sleep(1);}_exit(2); // 子進程退出,數字 2 為子進程退出的狀態}else if( pid > 0){ // 父進程int status = 0;// 等待子進程結束,回收子進程的資源// 此函數會阻塞// status 某個字段保存子進程調用 _exit(2) 的 2,需要用宏定義取出wait(&status); // waitpid(-1, &status, 0); // 和 wait() 沒區別,0:阻塞// waitpid(pid, &status, 0); // 指定等待進程號為 pid 的子進程, 0 阻塞// waitpid(pid, &status, WNOHANG); // WNOHANG:不阻塞if(WIFEXITED(status) != 0){ // 子進程是否正常終止printf("son process return %d\n", WEXITSTATUS(status));}printf("this is father process\n"); }return 0; }運行結果如下:
總結
以上是生活随笔為你收集整理的【Linux系统编程】进程的控制:结束进程、等待进程结束的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux系统编程】fork() 函数
- 下一篇: 【linux系统编程】进程间通信:信号中