2011-10-03 63 views
2

我想創建一個進程來管理一些其他進程的方式,如果一個孩子死亡,然後父級重啓進程和依賴於它的進程。SIGCHLD沒有交付進程樹

問題是,我注意到如果我在此結構中間重新啓動進程時創建進程的樹結構,我無法在新子進程終止時發出信號。

我寫了一個例子;假設我們有3個進程,祖父母,父母和孩子。 祖父母的叉子,並開始家長分叉和開始孩子(我把這個職位的末尾的代碼)。現在,如果我殺了孩子一切正常,孩子正確地重新啓動。

如果我殺了父...祖父母重新啓動父母,重新啓動孩子,但如果我殺了孩子,進程仍然處於殭屍狀態,並且SIGCHLD沒有傳遞給父進程。

換句話說:

  • 開始祖父母進程並等待所有3個進程一直在漲
  • 殺死父進程,等待那祖父母重啓父母是重新啓動子
  • 現在殺子過程中,進程仍然處於殭屍狀態。

我無法理解這種行爲......我看了一下信號萬噸例子和文檔的等待,嘗試在父母和祖父母叉之前重置默認的處理程序,但沒有什麼似乎工作... 這裏是代碼示例...

grandparent.cpp

#include <cstdio> 
#include <string> 
#include <cstring> 

#include <stdlib.h> 
#include <signal.h> 
#include <wait.h> 

using namespace std; 

void startProcess(string processFile); 
void childDieHandler(int sig, siginfo_t *child_info, void *context); 

FILE   *logFile; 
int   currentChildPid; 

int main(int argc, char** argv) 
{ 
    currentChildPid = 0; 
    logFile = stdout; 

    daemon(1,1); 


    struct sigaction sa; 
    bzero(&sa, sizeof(sa)); 
    sa.sa_sigaction = childDieHandler; 
    sigemptyset(&sa.sa_mask); 
    sa.sa_flags = SA_SIGINFO; 
    sigaction(SIGCHLD, &sa, NULL); 

    startProcess("parent"); 

    while(true) { 
     sleep(60); 
    } 

    return 0; 
} 

void startProcess(string processFile) 
{ 
    fprintf(logFile, "\nGP:Starting new process %s\n",processFile.c_str()); 
    // Get process field and start a new process via fork + execl 
    int pid = fork(); 
    if (pid == -1){ 
     fprintf(logFile,"GP:*** FORK ERROR on process %s !!!\n",processFile.c_str()); 
     fflush(logFile); 
     return; 
    } 

    // New child process 
    if (pid == 0) { 

     string execString = get_current_dir_name()+(string)"/"+processFile; 
     fprintf(logFile, "GP: %s \n",execString.c_str()); 

    execl(execString.c_str(), processFile.c_str(), NULL); 

     fprintf(logFile, "GP:*** ERROR on execv for process %s\n",processFile.c_str()); 
     fflush(logFile); 
     exit(1); 
    } else { 
     // Parent process 
     fprintf(logFile, "GP:New process %s pid is %d .\n", processFile.c_str(), pid); 
     fflush(logFile); 
    currentChildPid = pid; 
     sleep(2); 
    } 
} 

// Intercept a signal SIGCHLD 
void childDieHandler(int sig, siginfo_t *child_info, void *context){ 
    int status; 
    pid_t childPid; 
    while((childPid = waitpid(-1,&status, WNOHANG)) > 0) { 
     int pid = (int) childPid; 
     fprintf(logFile,"GP:*** PROCESS KILLED [pid %d]\n",pid); 

    sigset_t set; 
    sigpending(&set); 
    if(sigismember(&set, SIGCHLD)){ 
     fprintf(logFile, "GP: SIGCHLD is pending or blocked!!!!\n"); 
     fflush(logFile); 
    } 

     fflush(logFile); 

     // identify exited process and then restart it 
     if(currentChildPid == childPid){ 
     // kill any child 
     system("killall child"); 
     fprintf(logFile,"GP: Restarting parent process...\n"); 
     fflush(logFile); 
     startProcess("parent"); 
    } 

    } 

    fprintf(logFile,"GP:End of childDieHandler()... [%d]\n\n",(int)childPid); 
    fflush(logFile); 
} 

parent.cpp

#include <cstdio> 
#include <string> 
#include <cstring> 

#include <stdlib.h> 
#include <signal.h> 
#include <wait.h> 

using namespace std; 

void startProcess(string processFile); 
void childDieHandler(int sig, siginfo_t *child_info, void *context); 

FILE   *logFile; 
int   currentChildPid; 

int main(int argc, char** argv) 
{ 
    currentChildPid = 0; 
    logFile = stdout; 

    struct sigaction sa; 
    bzero(&sa, sizeof(sa)); 
    sa.sa_sigaction = childDieHandler; 
    sigemptyset(&sa.sa_mask); 
    sa.sa_flags = SA_SIGINFO; 
    sigaction(SIGCHLD, &sa, NULL); 

    startProcess("child"); 

    while(true) { 
     sleep(60); 
    } 

    return 0; 
} 

void startProcess(string processFile) 
{ 
    fprintf(logFile, "\nP : Starting new process %s\n",processFile.c_str()); 
    // Get process field and start a new process via fork + execl 
    int pid = fork(); 
    if (pid == -1){ 
     fprintf(logFile,"P : *** FORK ERROR on process %s !!!\n",processFile.c_str()); 
     fflush(logFile); 
     return; 
    } 

    // New child process 
    if (pid == 0) { 
    string execString = get_current_dir_name()+(string)"/"+processFile; 
     execl(execString.c_str(), processFile.c_str(), NULL); 

     fprintf(logFile, "P : *** ERROR on execv for process %s\n",processFile.c_str()); 
     fflush(logFile); 
     exit(1); 
    } else { 
     // Parent process 
     fprintf(logFile, "P : New process %s pid is %d .\n", processFile.c_str(), pid); 
     fflush(logFile); 
    currentChildPid = pid; 
     sleep(2); 
    } 
} 

// Intercept a signal SIGCHLD 
void childDieHandler(int sig, siginfo_t *child_info, void *context){ 
    int status; 
    pid_t childPid; 
    while((childPid = waitpid(-1,&status, WNOHANG)) > 0) { 
     int pid = (int) childPid; 
     fprintf(logFile,"P : *** PROCESS KILLED [pid %d]\n",pid); 

    sigset_t set; 
    sigpending(&set); 
    if(sigismember(&set, SIGCHLD)){ 
     fprintf(logFile, "P : SIGCHLD is pending or blocked!!!!\n"); 
     fflush(logFile); 
    } 

     fflush(logFile); 

    // identify exited process and then restart it 
    if(currentChildPid == childPid){ 
     fprintf(logFile,"P : Restarting child process...\n"); 
     fflush(logFile); 
     startProcess("child"); 
    } 

    } 

    fprintf(logFile,"P : End of childDieHandler()... [%d]\n\n",(int)childPid); 
    fflush(logFile); 
} 

child.cpp

#include <cstdio> 
#include <string> 
#include <cstring> 

int main(int argc, char** argv) 
{ 
    printf("\nC : I'm born...\n\n"); 

    while(true) { 
     sleep(60); 
    } 

    return 0; 
} 

回答

2

嗯,我有一個猜想......

內部信號處理程序中,SIGCHLD信號被阻斷(即,它是過程的任何成員信號掩碼)。

因此,當祖父母從信號處理程序中調用execl時,新的父母將啓動並阻止SIGCHLD。因此它永遠不會看到信號,也不會等待新的孩子。

嘗試在parent.cpp的開頭調用sigprocmask以便(a)驗證該理論並(b)解除阻塞SIGCHLD。

+0

是的,這個解決方案適用於測試案例,我相信它會在真正的軟件中工作!我已經在文檔中讀過信號被重置爲默認處理程序,但我沒有考慮信號掩碼...順便說一句我已經嘗試在父處理程序中設置SA_NOMASK標誌,但這不起作用...謝謝尼莫! – user976900