2017-04-16 173 views
0

我想了解這一段代碼,在這種情況下如何處理SIGCHILD信號?

int count =0; 
void handler(int sig){ 
    count++; 
} 

int main(){ 
    signal(SIGCHILD,handler); 
    for(int i=0;i<4;i++){ 
     if(!fork()){ 
      exit(0); 
     } 
    } 

    while(wait(NULL) ! = -1){ 

    } 

    print(count) 
} 

於是,我想到的是,因爲有4個SIGCHILD信號,處理器有望被稱爲四倍。但是,由於我們可以有最多一個未決信號,因此可能會丟棄一些信號並且計數可能不是四。

但是,如果父進程在單個子進程退出之前等待調用等待,那麼將如何處理SIGCHILD信號?在這種情況下,計數是4嗎?

在這種情況下,SIGCHILD,處理程序代碼和父進程等待之間的流程如何?

+0

信號如何發送? –

+0

@ Jean-BaptisteYunès信號將在子進程退出時發送。 – Dude

+0

對不起,我沒有注意到它是SIGCHILD ... –

回答

0

你的代碼的有些清理後的版本成爲MCVE(Minimal, Complete, Verifiable Example):

#include <assert.h> 
#include <errno.h> 
#include <signal.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/wait.h> 
#include <unistd.h> 

static volatile sig_atomic_t count = 0; 

static void handler(int sig) 
{ 
    assert(sig == SIGCHLD); 
    count++; 
} 

int main(void) 
{ 
    signal(SIGCHLD, handler); 
    for (int i = 0; i < 4; i++) 
    { 
     if (fork() == 0) 
     { 
      exit(16 * (i + 1)); 
     } 
    } 

    int corpse; 
    int status; 
    while ((corpse = wait(&status)) != -1 || errno == EINTR) 
    { 
     if (corpse == -1 && errno == EINTR) 
      printf("Interrupted by a signal\n"); 
     else 
      printf("Child %d exited with status 0x%.4X\n", corpse, status); 
    } 

    printf("Count = %d\n", count); 
    return 0; 
} 

當在Mac上運行的MacOS塞拉利昂10.12.4與GCC 6.3.0運行,我上了主題的變化的:

Child 74003 exited with status 0x1000 
Child 74004 exited with status 0x2000 
Child 74005 exited with status 0x3000 
Child 74006 exited with status 0x4000 
Count = 4 

在這個機器上(現代15" 2016年的MacBook Pro),我總是似乎得到的是作爲輸出 - 按順序爲孩子進程ID,與精心剪裁的退出狀態

當我改變了處理程序是這樣的(銘記的how to avoid calling printf() in a signal handler的限制 - 是的,我知道我可以鍵入STDIN_FILENO,而不是一些人1 S的):

static void handler(int sig) 
{ 
    assert(sig == SIGCHLD); 
    count++; 
    char s[2] = { count + '0', '\n' }; 
    write(1, "SH: count = ", sizeof("SH: count = ")-1); 
    write(1, s, 2); 
} 

然後輸出改爲更多的東西像這樣:

SH: count = 1 
SH: count = 2 
Child 74113 exited with status 0x1000 
Child 74114 exited with status 0x2000 
SH: count = 3 
Child 74115 exited with status 0x3000 
SH: count = 4 
Child 74116 exited with status 0x4000 
Count = 4 

這表明信號處理程序在循環過程中的不同時間被調用。 BSD信號處理程序(和macOS或Darwin有些基於BSD)傾向於重新啓動系統調用而不是中斷。因此,我所看到的不一定是您在不同平臺上看到的內容。

例如,在一個Ubuntu 16.04 LTS VM上運行,我得到的輸出:

SH: count = 1 
Child 13310 exited with status 0x4000 
Child 13309 exited with status 0x3000 
Child 13308 exited with status 0x2000 
Child 13307 exited with status 0x1000 
Count = 1 

然而,隨着信號處理的另一適應 - 恢復的信號處理,信號處理,因爲傳統通過signal()設定處理程序(非BSD)的行爲是被稱爲處理函數之前默認的復位,並檢查書面因爲Linux已經到位警告,如果你忽略write()返回值的字節數:

static void handler(int sig) 
{ 
    //assert(sig == SIGCHLD); 
    signal(sig, handler); 
    count++; 
    char s[2] = { count + '0', '\n' }; 
    int nb = write(1, "SH: count = ", sizeof("SH: count = ")-1); 
    assert(nb == sizeof("SH: count = ")-1); 
    nb = write(1, s, 2); 
    assert(nb == 2); 
} 

然後輸出成爲:

SH: count = 1 
Child 13838 exited with status 0x4000 
SH: count = 2 
Child 13837 exited with status 0x3000 
SH: count = 3 
Child 13836 exited with status 0x2000 
SH: count = 4 
Child 13835 exited with status 0x1000 
Count = 4 

所以,你可以看到,你看到的結果取決於您運行代碼的平臺上,並在handler()功能究竟是如何寫的。