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