2012-04-02 5 views
1

Ich arbeite an einem Shell-Labor für einen Systems-Kurs, und ich habe einige wirklich seltsame Probleme mit Rennbedingungen gehabt, die ich seit Freitagabend zu lösen versuche und die ich nicht festhalten kann.Rennbedingungen in meinen Signalhandlern? (C)

Mein aktueller Code: http://buu700.com/tsh

Alles vor START OF MY CODE und nach END OF MY CODE durch den Kursleiter zur Verfügung gestellt, so nichts davon sollte die Quelle der Probleme sein.

Wir haben auch ein Testskript; hier ist die Ausgabe meiner aktuellen Testergebnisse: 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

Ich glaube Ihr Vertrauen in Professor-Code _adorable_ geliefert. :) – sarnold

+0

Haha, es sieht für mich wie ein ziemlich solider Code aus, und die meisten Studenten haben das Labor schon so fertig gemacht, dass der Kurs-Code funktioniert. (Soweit ich weiß, wurde nichts davon persönlich von einem meiner Professoren geschrieben.) –

+0

Können Sie bitte eine bessere Beschreibung des Fehlers geben? Und erklären, warum Sie denken, dass es eine Race Condition ist? –

Antwort

1

ich den Verdacht haben, dass der Bug aus dem Rennen gegen andere Signale in der Signal-Handler kommt:

sigint_handler(int sig) 
{ 
    sigset_t s; 

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

    sigprocmask(SIG_BLOCK, &s, NULL); 

Wenn Sie sich registrieren Mit dem Signalhandler sigaction(2) können Sie eine sa_mask bereitstellen, die der Kernel verwendet, um Signale zu blockieren, während Ihr Signalhandler läuft. Dies geschieht atomar und erfordert keine zusätzliche Arbeit von Ihnen. Füllen Sie diese Maske einfach einmal auf, wenn Sie den Signalhandler registrieren.

Eine andere Möglichkeit kommt vom Signal SIGTSTP; Seite 350 meiner Kopie von APUE, 2nd edition sagt unter anderem:

Nur ein Job-Steuerungs-Shell, die die Anordnung von [SIGTSTP, SIGTTIN, SIGTTOU] zu SIG_DFL zurückgedreht werden.

Was ungesagt in diesen Absätzen noch übrig ist, aber ich denke, fair, anzunehmen ist, dass die Shell das Signal Disposition zu SIG_DFL für das Kind Prozesse setzen soll - es muss noch etwas tun, um die Signale zu verarbeiten selbst.

Haben Sie die Signalverteilungen bei den Kindern richtig gehandhabt?

+0

Danke für die Antwort. Der vom Kurs bereitgestellte Code hat tatsächlich einen Wrapper um sigaction herum, der die Blockierung so behandelt, wie Sie es beschrieben haben; Ich habe das nur in die Handler geworfen als eines dieser zufälligen, sinnlosen Dinge, mit denen man herumhackt, während man ziellos debuggt (es hat die Tester-Ausgabe überhaupt nicht beeinflusst). Ich werde es jetzt entfernen, da es definitiv nicht korrekt ist. --- Ich habe auch etwas wie das, was Sie mit SIG_DFL vorschlagen, während ich Debugging war; Sieht dies irgendwo annähernd korrekt aus? http://buu700.com/runcommand –

+1

Etwas, das mir sofort Angst machte, war: 'sprintf (cmd,"% s% s ", cmd, argv [i]);'. Beachten Sie die Warnung in 'sprintf (3)': _C99 und POSIX.1-2001 geben an, dass die Ergebnisse nicht definiert sind, wenn ein Aufruf von 'sprintf()', 'snprintf()', 'vsprintf()' oder 'vsnprintf () 'würde dazu führen, dass ein Kopieren zwischen Objekten stattfindet, die sich überlappen (z. B. wenn sich das Ziel-String-Array und eines der übergebenen Eingabeargumente auf den gleichen Puffer beziehen)._ – sarnold

+0

Danke, gerade behoben, dass (es fühlte sich gefährlich an, als ich es schrieb). Ich bekomme immer noch die gleiche Ausgabe aus dem Testskript, aber dieser Teil sollte jetzt besser sein. –

Verwandte Themen