2012-04-02 4 views
1

Je travaille sur un shell stage pour un cours sur les systèmes, et j'ai eu des bugs de condition de course vraiment bizarres que j'ai essayé de résoudre depuis vendredi soir et que je n'arrive pas à cerner.Les conditions de course dans mes gestionnaires de signaux? (C)

Mon code actuel: http://buu700.com/tsh

Tout avant et après START OF MY CODEEND OF MY CODE est fourni par les instructeurs de cours, donc rien de tout cela devrait être la source des problèmes.

Nous avons également un script de test; voici la sortie de mes résultats actuels: 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

Je trouve votre confiance dans le code fourni par le professeur _adorable_. :) – sarnold

+0

Haha, ça me semble un code assez solide, et la plupart des étudiants ont déjà fini le labo donc le code fourni par le cours fonctionne bien. (Pour autant que je sache, rien de tout cela n'a été écrit personnellement par aucun de mes professeurs.) –

+0

Pouvez-vous donner une meilleure description de la faute? Et expliquez pourquoi vous pensez que c'est une condition de course? –

Répondre

1

Je soupçonne que le bug vient de votre course contre d'autres signaux dans vos gestionnaires de signal:

sigint_handler(int sig) 
{ 
    sigset_t s; 

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

    sigprocmask(SIG_BLOCK, &s, NULL); 

Lorsque vous vous inscrivez le gestionnaire de signal avec sigaction(2), vous pouvez fournir un sa_mask que le noyau utilisera pour bloquer les signaux pendant que votre gestionnaire de signal est en cours d'exécution. Ceci est fait atomiquement et ne nécessite aucun travail supplémentaire de votre part. Il suffit de remplir ce masque une fois lors de l'enregistrement du gestionnaire de signal. Une autre possibilité vient du signal SIGTSTP; la page 350 de ma copie de APUE, 2nd edition dit, en partie:

Seule une coquille contrôle du travail devrait remettre la disposition de [SIGTSTP, SIGTTIN, SIGTTOU] à SIG_DFL.

Ce qui est non-dit dans ces paragraphes, mais je pense est juste de supposer, est que la coque doit définir la disposition du signal à SIG_DFL pour l'enfant processus - il doit encore faire quelque chose pour traiter les signaux lui-même.

Avez-vous géré correctement les dispositions du signal chez les enfants?

+0

Merci pour la réponse. Le code fourni par le cours a en fait une enveloppe autour de sigaction qui gère le blocage comme vous l'avez décrit; En fait, je l'ai juste jeté dans les manipulateurs comme l'une de ces choses insensées et aléatoires avec lesquelles on a tendance à bidouiller tout en déboguant sans but (cela n'affecte en rien la sortie du testeur). Je vais l'enlever maintenant car ce n'est certainement pas correct. --- J'ai aussi fait quelque chose comme ce que vous suggérez avec SIG_DFL pendant le débogage; cela ressemble-t-il à peu près à corriger? http://buu700.com/runcommand –

+1

Quelque chose qui m'a effrayé tout de suite était: 'sprintf (cmd,"% s% s ", cmd, argv [i]);'. Notez l'avertissement dans 'sprintf (3)': _C99 et POSIX.1-2001 spécifient que les résultats sont indéfinis si un appel à 'sprintf()', 'snprintf()', 'vsprintf()' ou 'vsnprintf () 'provoquerait une copie entre les objets qui se chevauchent (par exemple, si le tableau de chaînes cible et l'un des arguments d'entrée fournis font référence au même tampon)._ – sarnold

+0

Merci, juste fixé ça (ça me semblait dangereux quand je l'ai écrit). Je reçois toujours la même sortie du script de test, mais cette partie devrait être mieux maintenant. –

Questions connexes