2012-04-02 26 views
1

我正在系统课程的shell实验室工作,并且自从周五晚上以来我一直试图解决一些非常奇怪的竞争条件错误,并且似乎无法确定。我的信号处理程序中的竞态条件? (C)

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

之前的所有START OF MY CODEEND 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; 
} 
+5

我发现你对教授提供的代码_adorable_有信心。 :) – sarnold 2012-04-02 01:25:21

+0

哈哈,它看起来对我来说是非常可靠的代码,并且大多数学生已经完成了实验室,清楚了课程提供的代码的工作原理。 (据我所知,虽然没有一个是我的教授亲自写的)。 – 2012-04-02 01:28:02

+0

你能否给出一个更好的故障描述。并解释你为什么认为这是一种竞争条件? – 2012-04-02 03:43:01

回答

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页说,部分:

只有一个作业控制外壳应的配置[SIGTSTPSIGTTINSIGTTOU]重置为SIG_DFL

什么没说的那些段落,但我认为这是公平的假设,就是外壳应的信号配置设置为SIG_DFL子进程 - 它仍然需要做一些事情来处理信号本身。

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

+0

感谢您的回复。课程提供的代码实际上有一个sigaction封装器,它处理你描述的阻塞问题;实际上,我只是把它作为处理程序中的一个随机无意义的东西,而这些东西往往会在无目标地进行调试(它根本不会影响测试仪的输出)的情况​​下进行攻击。我现在将它删除,因为它绝对不正确。 --- 我在做调试时也做了类似于SIG_DFL的建议;这看起来是否接近正确? http://buu700.com/runco​​mmand – 2012-04-02 02:02:52

+1

马上让我感到害怕的是这样的:'sprintf(cmd,“%s%s”,cmd,argv [i]);'。请注意'sprintf(3)'中的警告:_C99和POSIX.1-2001指定如果调用'sprintf()','snprintf()','vsprintf()'或'vsnprintf( )会导致在重叠对象之间发生复制(例如,如果目标字符串数组和一个提供的输入参数指向同一个缓冲区)。_ – sarnold 2012-04-02 02:10:44

+0

谢谢,只是修正了这个问题(当我编写它时感觉很危险)。我仍然从测试脚本中获得相同的输出结果,但现在该部分应该会更好。 – 2012-04-02 02:26:49