歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> linux系統編程之進程(四) wait/waitpid函數與僵屍進程

linux系統編程之進程(四) wait/waitpid函數與僵屍進程

日期:2017/3/3 16:24:20   编辑:關於Linux

一、僵屍進程

當子進程退出的時候,內核會向父進程發送SIGCHLD信號,子進程的退出是個異步事件(子進程可以在父進程運行的任何時刻終止)

子進程退出時,內核將子進程置為僵屍狀態,這個進程稱為僵屍進程,它只保留最小的一些內核數據結構,以便父進程查詢子進程的退出狀態。

父進程查詢子進程的退出狀態可以用wait/waitpid函數。

二、如何避免僵屍進程

當一個子進程結束運行時,它與其父進程之間的關聯還會保持到父進程也正常地結束運行或者父進程調用了wait/waitpid才告終止。

進程表中代表子進程的數據項是不會立刻釋放的,雖然不再活躍了,可子進程還停留在系統裡,因為它的退出碼還需要保存起來以備父進程中後續的wait/waitpid調用使用。它將稱為一個“僵進程”。

調用wait或者waitpid函數查詢子進程退出狀態,此方法父進程會被掛起(waitpid可以設置不掛起)。

如果不想讓父進程掛起,可以在父進程中加入一條語句:signal(SIGCHLD,SIG_IGN);表示父進程忽略SIGCHLD信號,該信號是子進程退出的時候向父進程發送的。也可以不忽略SIGCHLD信號,而接收在信號處理函數中調用wait/waitpid。

而在運維中常用的手段是殺死父進程,這樣子進程會由init 進程接管,由它來清理子進程的狀態。

三、wait函數

頭文件<sys/types.h>和<sys/wait.h>

函數功能:當我們用fork啟動一個進程時,子進程就有了自己的生命,並將獨立地運行。有時,我們需要知道某個子進程是否已經結束了,我們可以通過wait安排父進程在子進程結束之後。

函數原型

pid_t wait(int *status)

函數參數

status:該參數可以獲得你等待子進程的信息

返回值:

成功等待子進程函數返回等待子進程的ID

wait系統調用會使父進程暫停執行,直到它的一個子進程結束為止。

返回的是子進程的PID,它通常是結束的子進程

狀態信息允許父進程判定子進程的退出狀態,即從子進程的main函數返回的值或子進程中exit語句的退出碼。

如果status不是一個空指針,狀態信息將被寫入它指向的位置

通過以下的宏定義可以獲得子進程的退出狀態

WIFEXITED(status) 如果子進程正常結束,返回一個非零值

WEXITSTATUS(status) 如果WIFEXITED非零,返回子進程退出碼

WIFSIGNALED(status) 子進程因為捕獲信號而終止,返回非零值

WTERMSIG(status) 如果WIFSIGNALED非零,返回信號代碼

WIFSTOPPED(status) 如果子進程被暫停,返回一個非零值

WSTOPSIG(status) 如果WIFSTOPPED非零,返回一個信號代碼

四、waitpid函數

函數功能:用來等待某個特定進程的結束

函數原型:

pid_t waitpid(pid_t pid, int *status,int options)

參數:

status:如果不是空,會把狀態信息寫到它指向的位置

options:允許改變waitpid的行為,最有用的一個選項是WNOHANG,它的作用是防止waitpid把調用者的執行掛起等待

返回值:如果成功返回等待子進程的ID,失敗返回-1

對於waitpid的p i d參數的解釋與其值有關:

pid == -1 等待任一子進程。於是在這一功能方面waitpid與wait等效。

pid > 0 等待其進程I D與p i d相等的子進程。

pid == 0 等待其組I D等於調用進程的組I D的任一子進程。換句話說是與調用者進程同在一個組的進程。

pid < -1 等待其組I D等於p i d的絕對值的任一子進程。

五、wait和waitpid函數的區別

兩個函數都用於等待進程的狀態變化,包括正常退出,被信號異常終止,被信號暫停,被信號喚醒繼續執行等。

在一個子進程終止前, wait 使其調用者阻塞,而waitpid 有一選擇項,可使調用者不阻塞。

waitpid並不只能等待第一個終止的子進程—它有若干個選擇項,可以控制它所等待的特定進程。

實際上wait函數是waitpid函數的一個特例。

示例程序:

/*************************************************************************
> File Name: process_.c
> Author: Simba
> Mail: [email protected]
> Created Time: Sat 23 Feb 2013 02:34:02 PM CST
************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/wait.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main(int argc, char *argv[])
{
pid_t pid;
pid = fork();
if (pid == -1)
ERR_EXIT("fork error");
if (pid == 0)
{
sleep(3);
printf("this is child\n");
// exit(100);
abort();
}
printf("this is parent\n");
int status;
int ret;
ret = wait(&status); // 阻塞等待子進程退出
// ret = waitpid(-1, &status, 0);
// ret = waitpid(pid, &status, 0);
/* waitpid可以等待特定的進程,而不僅僅是第一個退出的子進程
* 且可以設置option為WNOHANG,即不阻塞等待 */
printf("ret=%d, pid=%d\n", ret, pid);
if (WIFEXITED(status))
printf("child exited normal exit status=%d\n", WEXITSTATUS(status));
else if (WIFSIGNALED(status))
printf("child exited abnormal signal number=%d\n", WTERMSIG(status));
else if (WIFSTOPPED(status))
printf("child stopped signal number=%d\n", WSTOPSIG(status));
return 0;
}

輸出為:

simba@ubuntu:~/Documents/code/linux_programming/APUE/process$ ./wait

this is parent

this is child

ret=7156, pid=7156

child exited abnormal signal number=6

說明子進程被信號異常終止,因為我們調用了abort(), 即產生SIGABRT信號將子進程終止,可以查到此信號序號為6。如果我們不使用abort 而是exit(100), 則應該輸出 child exited normal exit status=100 ,即正常退出。

Copyright © Linux教程網 All Rights Reserved