C ++守护程序不会收到SIGCHLD信号

时间:2013-07-21 06:07:05

标签: c++ linux daemon

我正在编写守护程序,能够恢复工作流程CentOS release 5.7 (Final)

以下是代码示例:

#define CHILD_NEED_WORK                 1
#define CHILD_NEED_TERMINATE    2

int ReloadConfig()
{
    ....
    return 0;
}

void DestroyWorkThread()
{...}

int InitWorkThread()
{
     ...
    return 0;
}

int LoadConfig(char* FileName)
{
     ...
    return 0;
}

void SetPidFile(char* Filename)
{
    FILE* f;
    f = fopen(Filename, "w+");
    if (f)
    {
        fprintf(f, "%u\n", getpid());
        fclose(f);
    }
}

int SetFdLimit(int MaxFd)
{
    struct rlimit lim;
    int          status;

    lim.rlim_cur = MaxFd;
    lim.rlim_max = MaxFd;

    status = setrlimit(RLIMIT_NOFILE, &lim);

    return status;
}

//Monitor process
int MonitorProc()
{
    int      pid;
    int      status;
    int      need_start = 1;
    sigset_t sigset;
    siginfo_t siginfo;

    parent_pid = getpid();

    sigemptyset(&sigset);

    sigaddset(&sigset, SIGQUIT);

    sigaddset(&sigset, SIGINT);

    sigaddset(&sigset, SIGTERM);

    sigaddset(&sigset, SIGCHLD);

    sigaddset(&sigset, SIGUSR1);

    sigprocmask(SIG_BLOCK, &sigset, NULL);

    SetPidFile(PID_FILE);

    for (;;)
    {
        if (need_start)
        {
            pid = fork();
        }

        need_start = 1;

        if (pid == -1)
        {

        }
        else if (!pid) 
        {
            status = WorkProc();

            exit(status);
        }
        else
        {
            sigwaitinfo(&sigset, &siginfo);

            if (siginfo.si_signo == SIGCHLD)
            {
                wait(&status);

               status = WEXITSTATUS(status);

                 if (status == CHILD_NEED_TERMINATE)
                 {
                     Write("[MONITOR] Child stopped");
                     break;
                 }
                 else if (status == CHILD_NEED_WORK)
                 {
                     Write("[MONITOR] Child restart");
                 }
             }
             else if (siginfo.si_signo == SIGUSR1)
             {
                 kill(pid, SIGUSR1); 
                 need_start = 0;
             }
             else if (siginfo.si_signo == 0) 
             {
                need_start = 0;
                continue;
             }
             else
             {
                 Write("[MONITOR] Signal ", strsignal(siginfo.si_signo));
                 kill(pid, SIGTERM);
                 status = 0;
                 break;
             }
         }
     }

     Write("[MONITOR] Stop");

     unlink(PID_FILE);

     return status;
}

//Work process
int WorkProc()
{
    struct sigaction sigact;
    sigset_t         sigset;
    int             signo;
    int             status;

    sigact.sa_flags = SA_SIGINFO;

    sigact.sa_sigaction = signal_error_for_backtrace;

    sigemptyset(&sigact.sa_mask);


    sigaction(SIGFPE, &sigact, 0);
    sigaction(SIGILL, &sigact, 0);
    sigaction(SIGSEGV, &sigact, 0);
    sigaction(SIGBUS, &sigact, 0);

    sigemptyset(&sigset);

    sigaddset(&sigset, SIGQUIT);

    sigaddset(&sigset, SIGINT);

    sigaddset(&sigset, SIGTERM);

    sigaddset(&sigset, SIGUSR1);
    sigprocmask(SIG_BLOCK, &sigset, NULL);

    SetFdLimit(FD_LIMIT);


    status = InitWorkThread();


    if (!status)
    {
        for (;;)
        {
            sigwait(&sigset, &signo);

            if (signo == SIGUSR1)
            {
                status = ReloadConfig();
                if (status)
                {
                    Write("[DAEMON] Reload config failed");
                }
                else
                {
                    Write("[DAEMON] Reload config OK");
                }
            }
            else
            {
                break;
            }
        }

        DestroyWorkThread();
    }
    else
    {
        Write("[DAEMON] Create work thread failed");
    }

    Write("[DAEMON] Stopped");


    return CHILD_NEED_TERMINATE;
}


int main(int argc , char *argv[])
{

    if (argc != 2)
    {
        printf("Usage: ./test_daemon.conf failed!\n");
        return -1;
    }

    status = LoadConfig(argv[1]);
    if (status) 
    {
        printf("Error: Load config failed\n");
        return -1;
    }

    if (CheckForAnotherInstance())
    {
    printf("Daemon is already running!\n");
    return 1;
    }

    pid = fork();
    if (pid == -1)
    {
        printf("Error: Start Daemon failed (%s)\n", strerror(errno));
        return -1;
    }
    else if (!pid)
    {
        umask(0);
        setsid();

        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
        //Monitor process startup
        status = MonitorProc();
        return status;
    }
    else
    {
        return 0;
    }
    return 0;
}

我使用两个过程:工作过程,它产生主要工作和监视过程,等待来自工作过程的信号,并在它收到所需信号时重新启动它们。当我尝试向父进程发送信号 - 监视进程 - 使用命令 kill -s SIGCHLD 时,它会收到此信号。

当我尝试终止子进程时,父进程没有收到SIGCHLD信号 - 它等待信号的连接,子进程转换为僵尸。

但是当我使用实用程序 strace 和父进程时,一切正常 - 子进程成功终止,父进程收到SIGCHLD信号。

我读到了函数waitpid(),它用于接收SIGCHLD信号,但我也希望在父进程中接收另一个信号。

有什么想法吗?

2 个答案:

答案 0 :(得分:0)

我的猜测,在第一次分叉之前没有安装信号处理程序?

答案 1 :(得分:0)

您SIG_BLOCK SIGCHLD所以不会收到任何信号。但是当你继续使用sigwaitinfo()但是在执行wait()时你没有使用siginfo.si_pid时,你应该使用waitpid()来清理由于通过sigwaitinfo同步接收信号的PID ()。

您首先使用WEXITSTATUS()而不检查WIFEXITED(状态)。请参阅wait()手册页。

您的监视器和工作进程似乎使用与执行fork()之后的exec()相同的可执行文件。所以要小心,因为你可能会恢复子进程中的信号处理程序状态,以使子进程中的代码正常运行。 例如,监控进程是父进程?所以为了得到一个孩子它做fork()然后调用WorkProc()。在WorkProc()内部,它会阻止一堆信号(但不是SIGCLD)。但是执行是来自MonitorProc()的sigprocmask(SIG_BLOCK,...)仍然在WorkProc()内部处于活动状态。

我不确定“if(siginfo.si_signo == 0)”是什么。

所以另一个你的主要查询是什么SIGCHLD没有从运行WorkProc()函数的进程传递的原因,是因为你已经在MonitorProc()中阻止了那个信号。所以修复这个问题使用sigprocmask()的第三个参数来保存MonitorProc()中的原始块/解除阻塞掩码,当你fork()并跳转到WorkProc()之前恢复阻塞/解除阻塞掩码。