IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

POSIX C Discussion :

Fork et signaux


Sujet :

POSIX C

  1. #1
    Membre régulier
    Inscrit en
    Avril 2005
    Messages
    156
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 156
    Points : 76
    Points
    76
    Par défaut Fork et signaux
    Bonjour à tous,

    J'ai écrit un programme qui créé un processus fils, puis qui envoie des signaux (père vers fils) aussi vite que possible pendant 5 secondes, le but étant de compter le nombre de signaux reçus par le fils :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
     
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <signal.h>
     
    #define SIG_TICK SIGUSR1
     
    static int g_end = 0;
    static unsigned long g_counter = 0UL;
     
    void count( int signo )
    {
        ( void )signo;
        ++g_counter;
    }
     
    void end( int signo )
    {
        ( void )signo;
        g_end = 1;
    }
     
    void process_child( void )
    {
     
        struct sigaction sa;
     
        sa.sa_handler = count;
        sigemptyset( &sa.sa_mask );
        sa.sa_flags = 0;
        if ( sigaction( SIG_TICK , &sa , NULL ) < 0 )
        {
            perror( "sigaction" );
            exit( EXIT_FAILURE );
        }
     
        sa.sa_handler = end;
        if ( sigaction( SIGALRM , &sa , NULL ) < 0 )
        {
            perror( "sigaction" );
            exit( EXIT_FAILURE );
        }
     
        while ( !g_end )
            sigsuspend( &sa.sa_mask );
     
        printf( "Fils : %lu signaux recus.\n" , g_counter );
     
    }
     
    void process_parent( pid_t child )
    {
     
        struct sigaction sa;
        int status;
     
        sa.sa_handler = end;
        sigemptyset( &sa.sa_mask );
        sa.sa_flags = 0;
        if ( sigaction( SIGALRM , &sa , NULL ) < 0 )
        {
            perror( "sigaction" );
            kill( child , SIGKILL );
            wait( NULL );
            exit( EXIT_FAILURE );
        }
     
        alarm( 5 );
     
        while ( !g_end )
            kill( child , SIG_TICK );
     
        kill( child , SIGALRM );
        wait( &status );
        if ( !WIFEXITED( status ) )
            exit( EXIT_FAILURE );
     
    }
     
    int main( void )
    {
     
        pid_t pid = fork();
     
        switch ( pid )
        {
     
            /* Error */
            case -1: perror( "fork" ); return EXIT_FAILURE;
     
            /* Child */
            case 0: process_child(); break;
     
            /* Parent */
            default: /*sleep( 1 );*/ process_parent( pid ); break;
     
        }
     
        return EXIT_SUCCESS;
     
    }
    Le problème est que certaines fois tout fonctionne bien, et d'autres fois aucun résultat n'est affiché (après avoir testé, il semblerait que la fonction process_child ne soit jamais exécutée). En décommentant l'appel à sleep juste avant d'appeler la fonction process_parent, tout fonctionne très bien à chaque fois...

    Quelqu'un pourrait-il m'expliquer la cause du problème ? Merci !

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 370
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 370
    Points : 23 625
    Points
    23 625
    Par défaut
    Bonsoir,

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $ ./sign ; echo $?
    1

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $ ./sign ; echo $?
    Fils : 1700959 signaux recus.
    0

    Donc, lorsque ton programme n'affiche rien, le status de retour du père vaut 1, donc EXIT_FAILURE. Ce code n'est renvoyé que lorsque le fils a mal fini, si l'on en croit ton code. Donc, il faut passer en revue chaque macro pour interpréter le status de retour. Ça tombe bien, la man page de wait() nous donne un code tout fait :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
                   if (WIFEXITED(status)) {
                           printf("exited, status=%d\n", WEXITSTATUS(status));
                       } else if (WIFSIGNALED(status)) {
                           printf("killed by signal %d\n", WTERMSIG(status));
                       } else if (WIFSTOPPED(status)) {
                           printf("stopped by signal %d\n", WSTOPSIG(status));
                       } else if (WIFCONTINUED(status)) {
                           printf("continued\n");
                       }

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $ ./sign ; echo $?
    killed by signal 10
    1
    $ kill -l
     1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
     5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
     9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
    13) SIGPIPE     14) SIGALRM     15) SIGTERM     16) SIGSTKFLT
    17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
    21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU
    25) SIGXFSZ     26) SIGVTALRM   27) SIGPROF     28) SIGWINCH
    29) SIGIO       30) SIGPWR      31) SIGSYS      34) SIGRTMIN
    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3  38) SIGRTMIN+4
    …

    Le programme a donc été tué par le signal que tu lui envoies pour incrémenter son compteur. Est-ce possible ? Voyons ce qu'en dit man 7 signal :

    Code Text : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
           First the signals described in the original POSIX.1-1990 standard.
    
           Signal     Value     Action   Comment
           ----------------------------------------------------------------------
           SIGHUP        1       Term    Hangup detected on controlling terminal
                                         or death of controlling process
           SIGINT        2       Term    Interrupt from keyboard
           SIGQUIT       3       Core    Quit from keyboard
           SIGILL        4       Core    Illegal Instruction
           SIGABRT       6       Core    Abort signal from abort(3)
           SIGFPE        8       Core    Floating point exception
           SIGKILL       9       Term    Kill signal
           SIGSEGV      11       Core    Invalid memory reference
           SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                         readers
           SIGALRM      14       Term    Timer signal from alarm(2)
           SIGTERM      15       Term    Termination signal
           SIGUSR1   30,10,16    Term    User-defined signal 1
           SIGUSR2   31,12,17    Term    User-defined signal 2
           SIGCHLD   20,17,18    Ign     Child stopped or terminated
           SIGCONT   19,18,25    Cont    Continue if stopped
           SIGSTOP   17,19,23    Stop    Stop process
           SIGTSTP   18,20,24    Stop    Stop typed at tty
           SIGTTIN   21,21,26    Stop    tty input for background process
           SIGTTOU   22,22,27    Stop    tty output for background process

    Ce qui me surprend, d'ailleurs. J'étais persuadé que les signaux utilisateurs étaient ignorés par défaut.

    En tout état de cause, cela dépend du premier processus qui prend la main au lancement de ton programme. Si c'est le fils, il aura le temps d'installer son gestionnaire de signal et tout se déroulera correctement. Si c'est le père, alors un signal SIGUSR1 sera envoyé au fils et le tuera avant qu'il y ait eu le temps d'exécuter la moindre instruction.

  3. #3
    Membre régulier
    Inscrit en
    Avril 2005
    Messages
    156
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 156
    Points : 76
    Points
    76
    Par défaut
    Super, merci beaucoup pour ta réponse très instructive !

    J'ai modifié le programme pour qu'au lieu d'appeler sleep chez le père (ce que je trouve pas terrible), il bloque le signal SIGUSR1 avant le fork. Ainsi le fils hérite de ce comportement et n'est pas tué trop tôt

  4. #4
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Ce qui me surprend, d'ailleurs. J'étais persuadé que les signaux utilisateurs étaient ignorés par défaut.
    Non. Ils sont offerts aux programmeurs avec l'assurance qu'aucun élément du noyau ne les enverra (ce qui pourrait générer alors un conflit avec le programme en cours) mais un processus qui reçoit ce signal se comporte comme avec les autres signaux.

    Citation Envoyé par j0o0 Voir le message
    J'ai modifié le programme pour qu'au lieu d'appeler sleep chez le père (ce que je trouve pas terrible), il bloque le signal SIGUSR1 avant le fork. Ainsi le fils hérite de ce comportement et n'est pas tué trop tôt
    T'avais aussi la solution de mettre le père en pause() et le fils qui lui envoie un signal quand il est prêt à recevoir
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  5. #5
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 370
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 370
    Points : 23 625
    Points
    23 625
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Non. Ils sont offerts aux programmeurs avec l'assurance qu'aucun élément du noyau ne les enverra (ce qui pourrait générer alors un conflit avec le programme en cours) mais un processus qui reçoit ce signal se comporte comme avec les autres signaux.
    C'est-à-dire qu'il y a au moins quatre comportements différents face aux signaux du système : ignorer le signal, mettre fin au programme, y mettre fin et produire un fichier core, et suspendre l'exécution (STOP). On peut dire cinq avec SIGCONT.

    Le fait même qu'il ne s'agisse pas d'un signal envoyé par le système m'aurait justement poussé à les ignorer par défaut. Mais, dit comme ça, c'est vrai que ça se tient. Un signal imprévu doit conduire à un arrêt.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Fork() et Signaux
    Par michaelvd dans le forum Linux
    Réponses: 1
    Dernier message: 18/04/2012, 10h01
  2. Fork() et signaux
    Par kaso54 dans le forum Linux
    Réponses: 3
    Dernier message: 31/05/2009, 19h52
  3. [Amstrad] Signaux à gérer port E/S pour lire ROM
    Par Masterglob dans le forum Autres architectures
    Réponses: 7
    Dernier message: 12/01/2005, 12h03
  4. [langage] [Fork] Détecter un fichier
    Par GLDavid dans le forum Langage
    Réponses: 11
    Dernier message: 08/07/2004, 01h05
  5. Pas de fork sous Windows?
    Par chezjm dans le forum POSIX
    Réponses: 8
    Dernier message: 11/06/2002, 12h15

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo