我的信号处理程序中的竞争条件? (C)

时间:2012-04-02 01:21:54

标签: c shell system signals race-condition

我正在为一个系统课程的shell实验室工作,我一直有一些非常奇怪的竞争条件错误,我一直试图解决,因为星期五晚上,似乎无法确定。

我目前的代码:http://buu700.com/tsh

课程讲师提供START OF MY CODE之前和END OF MY CODE之后的所有内容,因此这些都不应成为问题的根源。

我们也有一个测试脚本;这是我当前测试结果的输出:http://buu700.com/sdriver


/*****************
 * Signal handlers
 *****************/

/* 
 * sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
 *     a child job terminates (becomes a zombie), or stops because it
 *     received a SIGSTOP, SIGTSTP, SIGTTIN or SIGTTOU signal. The 
 *     handler reaps all available zombie children, but doesn't wait 
 *     for any other currently running children to terminate.  
 */
void 
sigchld_handler(int sig) 
{
    pid_t pid;
    int status, termsig;
    struct job_t *job;


    sigset_t s;

    sigemptyset(&s);
    sigaddset(&s, SIGCHLD);
    sigaddset(&s, SIGINT);
    sigaddset(&s, SIGTSTP);

    sigprocmask(SIG_BLOCK, &s, NULL);


    while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
        if (WIFEXITED(status)) {
            deletejob(job_list, pid);
        }
        if ((termsig = WTERMSIG(status))) {
            deletejob(job_list, pid);
            safe_printf("Job [%i] (%i) %s by signal %i\n",
                            pid2jid(pid), pid, "terminated", termsig);
        }
        if (WIFSTOPPED(status)) {
            job = getjobpid(job_list, pid);
            job->state = ST;
            safe_printf("Job [%i] (%i) %s by signal %i\n",
                            pid2jid(pid), pid, "stopped", SIGTSTP);
        }
    }

    if (errno != ECHILD)
        unix_error("waitpid error");

    sigprocmask(SIG_UNBLOCK, &s, NULL);

    return;
}



/* 
 * sigint_handler - The kernel sends a SIGINT to the shell whenver the
 *    user types ctrl-c at the keyboard.  Catch it and send it along
 *    to the foreground job.  
 */
void 
sigint_handler(int sig) 
{
    sigset_t s;

    sigemptyset(&s);
    sigaddset(&s, SIGCHLD);
    sigaddset(&s, SIGINT);
    sigaddset(&s, SIGTSTP);

    sigprocmask(SIG_BLOCK, &s, NULL);

    kill(-1, sig);

    sigprocmask(SIG_UNBLOCK, &s, NULL);

    return;
}



/*
 * sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever
 *     the user types ctrl-z at the keyboard. Catch it and suspend the
 *     foreground job by sending it a SIGTSTP.  
 */
void 
sigtstp_handler(int sig) 
{
    sigset_t s;

    sigemptyset(&s);
    sigaddset(&s, SIGCHLD);
    sigaddset(&s, SIGINT);
    sigaddset(&s, SIGTSTP);

    sigprocmask(SIG_BLOCK, &s, NULL);

    kill(-1, sig);

    sigprocmask(SIG_UNBLOCK, &s, NULL);

    return;
}

1 个答案:

答案 0 :(得分:1)

我怀疑这个错误来自你与信号处理程序中其他信号的竞争:

sigint_handler(int sig) 
{
    sigset_t s;

    sigemptyset(&s);
    sigaddset(&s, SIGCHLD);
    sigaddset(&s, SIGINT);
    sigaddset(&s, SIGTSTP);

    sigprocmask(SIG_BLOCK, &s, NULL);

当您使用sigaction(2)注册信号处理程序时,您可以提供一个sa_mask,在您的信号处理程序运行时,内核将使用该SIGTSTP来阻止信号。这是以原子方式完成的,不需要您做额外的工作。注册信号处理程序时,只需填充一次这个掩码。

另一种可能性来自SIGTSTP信号;我的APUE, 2nd edition副本的第350页在部分内容中说:

  

只有作业控制外壳应将[SIGTTINSIGTTOUSIG_DFL]的处置方式重置为SIG_DFL

在这些段落中没有说明什么,但我认为可以公平地说,shell应该为子进程设置信号处理{{1}} - 它仍然需要做一些事情来处理信号本身。

您是否正确处理了孩子们的信号处理?