10.30.2015

Linux C waitpid()

在之前的學習心得:Linux C Parent-Child Process中,我已經瞭解了Linux中fork(), Parent-Child Process, wait(), WIFEXITED(), WEXITSTATUS()等的應用;這一次想要更進一步地瞭解waitpid(), WIFSIGNALED(), WTERMSIG()的用法。

功能說明:等待子行程中斷或結束。

標頭檔:#include <sys/wait.h> \n #include <sys/types.h>

函式宣告:pid_t waitpid(pid_t pid, int *status, int options);

函式說明:

1. waitpid()會暫停目前行程的執行,直到有訊號來到,或是子行程的結束。
2. 如果在呼叫waitpid()時子行程已經結束,則waitpid()會立即傳回子行程的結束狀態值。
3. 子行程的結束狀態值,會由第2個參數status以Call by Reference的方式傳回。
4. 若不在意子行程的結束狀態值,可將第2個參數status設定成NULL。
5. 第1個參數pid_t pid的設定如下:

a) pid < -1:等待行程群組識別碼為pid絕對值的任何子行程。
b) pid = -1:等待任何一個子行程,相當於wait()。
c) pid = 0:等待行程群組識別碼與目前行程相同的任何一個子行程。
d) pid > 1:等待任何子行程識別碼為pid的子行程。

這一部分我也搞不太懂何謂「行程的群組識別碼」??....@@

如果要把waitpid()當成wait()來用,就把pid設成-1;

至於第4種傳進子行程的PID,可以刻意地等待某個子行程的PID。但waitpid()的回傳值就是子行程的PID,那我們該如何事先得知?? 可以在子行程執行的過程中,將PID寫進暫存檔,如/var/run/*.pid_t,再從父行程去讀取。

6. 第3個參數int options的設定如下:
a) NULL:相當於wait()。
b) WNOHANG:如果沒有任何已經結束的子行程則馬上返回,不予等待。
c) WUNTRACED:如果子行程進入暫停執行情況則馬上返回,但結束狀態不予理會。

如果想把waitpid()當wait()用,就設成NULL值;WNOHANG (Wait_No_Hang)和WUNTRACED (Wait_Untraced)的定義如下:

/usr/include/bits/waitflags.h
// Bits in the third argument to "waitpid".
#define WNOHANG   1 // Don't block waiting.
#define WUNTRACED 2 // Report status of stopped children.
設定成WNOHANG,是可以讓父行程不必等待子行程的回應而繼續做事 (寫的文謅謅的....~"~);

而WUNTRACED....「子行程進入"暫停執行"情況」,這一部分我也一直不知道該如何模擬....(先跳過)

回傳值:如果執行成功則傳回子行程識別碼 (CPID);如果有錯誤發生則傳回-1。

範例:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/wait.h>
#include <signal.h>

int 
main()
{
    pid_t pid = -2, PPID = -3, CPID = -4;
    int nStatus = 0, _Exit = 0;

    pid = fork();
    if (pid == 0) {
        signal(SIGHUP, SIG_DFL);
        CPID = getpid();
        PPID = getppid();
        printf("\nChild (PPID / CPID): %d / %d \n", PPID, CPID);
        usleep(10 * 1000000);
        exit(EXIT_SUCCESS);
    }

    PPID = getpid();
    sleep(20);
    CPID = waitpid(-1, &nStatus, WNOHANG);
    printf("Parent (PPID / CPID): %d / %d \n", PPID, CPID);
    if (WIFEXITED(nStatus) != 0) {
        _Exit = WEXITSTATUS(nStatus);
        switch (_Exit) {
            default:
                printf("The Child Process EXIT-STATUS code: %d \n\n", _Exit);
                break;
        }
    }
    else if (WIFSIGNALED(nStatus) != 0) {
        _Exit = WTERMSIG(nStatus);
        switch (_Exit) {
            default:
                printf("The Child Process TERMINAL-SIGNAL code: %d \n\n", _Exit);
                break;
        }
    }
/*  else if (WIFSTOPPED(nStatus) != 0) {
        _Exit = WSTOPSIG(nStatus);
        switch (_Exit) {
            default:
                printf("The Child Process STOP-SIGNAL code: %d \n", _Exit);
                break;
        }
    }
*/  else
        printf("Child terminated abnormally. \n");

    return 0;
}
首先,我各自在父子程序中均讀取父子行程的PID。在父行程中,讀取自己的PID,用getpid();讀取子行程的PID,則是用wait()或waitpid()。在子行程中,讀取自己的PID,也是用getpid();讀取父行程的PID,則用getppid()。

因為我要讓子行程比父行程先行結束,所以我在子行程的執行過程中睡眠10秒,而父行程則是20秒,好讓waitpid()接收子行程的執行結果狀態。

此外,為了印證WIFSIGNALED (Wait_If_Signaled)和WTERMSIG (Wait_Terminal_Signal)的功能,所以我在子行程的一開始就向系統註冊了一個SIGHUP (掛斷)的訊號。

執行結果:

直接執行主程式,而不做任何的動作,可以跟之前的wait()範例一樣,驗證WIFEXITED()和WEXITSTATUS()的功能;

執行主程式之後,我們開啟另一個終端機視窗,趕緊對子程序傳送一個掛斷 (HUP)的訊號 (因為子程序10秒后就會結束),輸入"kill -s SIGHUP (CPID)"。

然後等待父子程序結束,可以驗證WIFSIGNALED()WTERMSIG()的用法,如下圖:


在上圖中,第一次的執行結果「The Child Process EXIT-STATUS code: 0」,0就是從子程序裡的"exit(EXIT_SUCCESS); "得來的;

第二次的執行結果「The Child Process TERMINAL-SIGNAL code: 1」,1就是訊號列表 (kill -l)中的1) SIGHUP

在程式碼中,我還註解掉了一大段WIFSTOPPED() (Wait_If_Stopped)WSTOPSIG() (Wait_Stop_Signal)的功能,因為我還搞不太清楚這一部份的用法。根據課本上的簡述,似乎是要搭配第3個參數WUNTRACED來使用。

沒有留言:

張貼留言