【问题标题】:Race conditions in my signal handlers? (C)我的信号处理程序中的竞争条件? (C)
【发布时间】:2012-04-15 17:27:22
【问题描述】:

我正在为系统课程的 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;
}

【问题讨论】:

  • 我发现你对教授提供的代码的信心可爱。 :)
  • 哈哈,对我来说,这看起来是相当可靠的代码,而且大多数学生已经完成了实验,因此课程提供的代码可以正常工作。 (据我所知,实际上没有一篇是我的两位教授亲自写的。)
  • 您能否更好地描述故障。并解释为什么你认为这是一种竞争条件?

标签: c shell system signals race-condition


【解决方案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信号;我的APUE, 2nd edition 副本的第 350 页部分说:

只有作业控制 shell 才能将 [SIGTSTP, SIGTTIN, SIGTTOU] 的配置重置为 SIG_DFL

那些段落中没有说的,但我认为可以公平地假设是,shell 应该为 子进程将信号处置设置为SIG_DFL——它仍然需要做一些事情自己处理信号。

您是否妥善处理了孩子们的信号处置?

【讨论】:

  • 感谢您的回复。课程提供的代码实际上有一个围绕 sigaction 的包装器,它按照您描述的方式处理阻塞;实际上,我只是将它作为一种随机无意义的东西扔到处理程序中,人们在漫无目的地调试时往往会随意乱搞(它根本不会影响测试器的输出)。我现在将其删除,因为它绝对不正确。 ---我在调试时也做了一些你对 SIG_DFL 建议的事情;这看起来接近正确吗? buu700.com/runcommand
  • 让我立即感到害怕的是:sprintf(cmd, "%s%s ", cmd, argv[i]);。注意sprintf(3) 中的警告:C99 和 POSIX.1-2001 指定如果调用 sprintf()snprintf()vsprintf()vsnprintf() 会导致复制发生,则结果未定义重叠的对象之间(例如,如果目标字符串数组和提供的输入参数之一引用相同的缓冲区)。
  • 谢谢,刚刚解决了这个问题(我写的时候感觉很危险)。我仍然从测试脚本中得到相同的输出,但那部分现在应该更好了。
猜你喜欢
  • 1970-01-01
  • 2011-09-23
  • 1970-01-01
  • 2014-09-23
  • 1970-01-01
  • 2011-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多