Differences

This shows you the differences between two versions of the page.

Link to this comparison view

sde:laboratoare:05 [2019/03/20 08:51]
iuliana.marin
— (current)
Line 1: Line 1:
- ​====== Laboratoire 5 - IPC - Communication entre processus ====== 
-===== Documents d'aide ===== 
- 
-     * [[[[http: //​elf.cs.pub.ro/​so/​res/​laboratoare/​lab04-refcard.pdf | lab05-refcard.pdf]] 
-===== Liens vers des sections utiles ===== 
-   * [[#Pipes anonymes sous Linux | Pipes anonymes sous Linux]] 
-   * [[#Pipes sous Linux | Pipes sous Linux]] 
-  * [[#​Génération de signaux | Génération de signaux]] 
-  * [[#​Soumission et recevoir des signaux | Soumission et recevoir des signaux]] 
-  * [[#Types standard de signaux | Types standard de signaux]] 
-  * [[#Les messages décrivant des signaux | Les messages décrivant des signaux]] 
-  * [[#Masques de signalisation. Verrouillage du signal | Masques de signalisation. Verrouillage du signal]] 
-  * [[#​Traitement du signal | Traitement du signal]] 
-  * [[#Rapport de processus | Rapport de processus]] 
-  * [[#En attente d'un signal | En attente d'un signal]] 
-  * [[#Timers sous Linux | Timers sous Linux]] 
- 
-===== Pipes sous Linux ===== 
- 
-==== Pipes anonymes sous Linux ==== 
- 
-Le tuyau est un mécanisme de communication à sens unique entre deux processus. Dans la plupart des implémentations UNIX, un canal apparaît sous la forme d'un espace mémoire d'une certaine taille dans l'​espace du noyau. Les processus qui communiquent via un tuyau anonyme doivent avoir un degré de parenté; généralement,​ un processus qui crée un tuyau appelle ensuite fork et le tuyau est utilisé pour la communication parent-fils. Dans tous les cas, les processus qui communiquent via des canaux anonymes ne peuvent pas être créés par différents utilisateurs du système. 
- 
- 
-L'​appel système à créer est [[http://​linux.die.net/​man/​2/​pipe|pipe]]:​ 
- 
-{{so:​laboratoare:​pipe.png?​370| Exemple d'​utilisation - Le processus parent transmet les données enfant au processus enfant par pipeline}} 
-<code c> 
-int pipe(int filedes[2]);​ 
-</​code>​ 
-Le vecteur filedesktop contient deux descripteurs de fichier après l'​exécution de la fonction: 
-  * ''​ filedes [0] '',​ ouvert à la lecture; 
-  * ''​ filedes [1] '',​ ouvert à l'​écriture;​ 
- 
-/ * On considère que le résultat de ''​ filedes [1] ''​ est une entrée pour ''​ filedes [0] ''​. * / 
- 
-// Mnemotechnics //: ''​ STDIN_FILENO ''​ vaut 0 (lecture), ''​ STDOUT_FILENO ''​ vaut 1 (écriture). 
- 
-** Note **: 
-  * la lecture / écriture à partir de pipes est atomique à moins que vous ne lisiez / écriviez plus que ''​ PIPE_BUF ''​ ((limite globale définie par défaut pour Linux à 4096 octets)) octets. 
-  * La lecture / écriture à partir des pipes est faite en utilisant les fonctions '​read'​ / '​write'​. 
- 
-La plupart des applications de pipeline se ferment à chaque extrémité du tuyau de traitement ** non utilisées ** dans la communication unidirectionnelle. Si l'un des descripteurs est fermé, les règles suivantes s'​appliquent:​ 
-  * Une lecture dans un tube pour lequel ** write ** a été fermé après la lecture de toutes les données retournera «0», indiquant la fin du fichier. Le descripteur d'​écriture peut être dupliqué afin que plusieurs processus puissent écrire dans le canal. En règle générale, pour les canaux anonymes, il n'y a que deux processus, un qui en écrit un autre et un autre, alors que dans le cas des fichiers nommés du canal (FIFO), il peut y avoir davantage de processus d'​écriture de données. 
-  * une écriture dans un tube pour lequel le lecteur ** ** a été fermé provoque la génération du signal SIGPIPE. Si le signal est capturé et revient de la routine de traitement, la fonction write renvoie une erreur et la variable ''​ errno ''​ a la valeur ''​ EPIPE ''​. 
-<note important>​ Le défaut ** le plus fréquent **, relatif au travail avec des tuyaux, provient du fait qu’il n’est pas tenu compte du fait qu’aucun "​EOF"​ n’est envoyé dans le tuyau (la lecture du tuyau ne se termine pas) à moins qu’il soit fermé ** TOUTES ** têtes d'​écriture provenant de ** TOUS ** processus ayant ouvert le descripteur d'​écriture de tuyau (dans le cas d'un "​fork",​ n'​oubliez pas de fermer les extrémités du tuyau dans le processus parent). </​note>​ 
- 
-Autres fonctionnalités utiles: [[http://​linux.die.net/​man/​3/​popen|popen]],​ [[http://​linux.die.net/​man/​3/​pclose|pclose]]. 
-==== Pipes sous Linux ==== 
- 
-Élimine le besoin de relation entre les processus qui communiquent. Ainsi, chaque processus peut s'​ouvrir pour lire ou écrire le fichier de nom de canal (FIFO), un type de fichier spécial qui conserve les caractéristiques d'un canal. La communication est faite dans un sens ou les deux. Les fichiers FIFO peuvent être identifiés par la lettre ''​ p ''​ dans le premier champ de droits d'​accès (''​ ls -l ''​). 
- 
-L'​appel de bibliothèque pour créer des canaux FIFO est [[http://​linux.die.net/​man/​3/​mkfifo|mkfifo]]:​ 
-<code c> 
-int mkfifo(const char *pathname, mode_t mode); 
-</​code>​ 
-Une fois le tube FIFO créé, il peut être utilisé pour toutes les opérations sur les fichiers courants: ''​ ouvert '',​ ''​ fermer '',​ '​lire'​ ','​ écrire '. 
- 
-Le comportement d'un tuyau FIFO après ouverture est affecté par l'​indicateur ''​ O_NONBLOCK '':​ 
-<spoiler Détails du drapeau O_NONBLOCK>​ 
-   * Si ''​ O_NONBLOCK ''​ n'est pas spécifié (cas normal), alors un '​ouvert'​ pour la lecture sera verrouillé jusqu'​à ce qu'un autre processus ouvre la même FIFO pour l'​écriture. Par analogie, si l'​ouverture est destinée à l'​écriture,​ un verrouillage peut avoir lieu jusqu'​à ce qu'un autre processus effectue la lecture. 
-   * Si '​O_NONBLOCK'​ est spécifié, la lecture s'​ouvre immédiatement,​ mais une ouverture en écriture peut renvoyer une erreur avec ''​ errno ''​ avec la valeur '​ENXIO'​ s'il n'y a pas d'​autre processus ouvrant la même FIFO. pour lire. 
-</​spoiler>​ 
- 
- 
-Lors de la fermeture du dernier fichier descripteur d'​écriture pour une FIFO, un "fin de fichier"​ - "​EOF"​ est généré - pour le processus qui lit à partir de la FIFO. 
-=====  Les signaux dans Linux ===== 
-Dans le monde réel, un processus peut découvrir une variété de contingences qui affectent le flux normal de l'​exécution. Si le processus ne peut pas gérer, ils sont passés plus loin, le système d'​exploitation. Quel système d'​exploitation ne sait pas si le processus peut continuer à exécuter normalement sans effets secondaires indésirables est nécessaire pour terminer le processus de force. Une solution à ce problème est les signaux. 
-<​note>​ Un signal est un logiciel d'​interruption du flux normal de l'​exécution d'un processus. </​note>​ 
-Les signaux sont un concept spécifique des systèmes d'​exploitation UNIX. Le système d'​exploitation utilise pour signer la survenance de circonstances exceptionnelles processus donnant la possibilité de répondre. Chaque signal est associé à une classe d'​événements qui peuvent se produire qui répondent à certains critères. 
-Les processus peuvent traiter, bloquer, ignorer ou laisser le système d'​exploitation pour exécuter l'​action par défaut lors de la réception d'un signal: 
-  * En règle générale l'​action par défaut est processus **d'​achèvement**. 
- 
-  * Si un processus veut **ignorer** un signal, le système d'​exploitation ne sera pas envoyer ce processus de signal. 
- 
-  * Si un processus spécifique qui veut signal **de bloc**, le système d'​exploitation ne sera plus envoyer des signaux à traiter ce type en question, mais enregistrera seulement le premier signal de ce type, le reste est perdu. Lorsque le processus décide qu'il veut recevoir des signaux à nouveau de ce type, en cas de signal en attente, il sera envoyé. 
-L'​ensemble de ces signaux est terminé; OS conserve pour chaque processus, une **table des actions** choisie pour chaque type de signal. A chaque fois que ces actions sont bien définies. Lors du démarrage de la table de processus est initialisé avec l'​action par défaut. Le signal de traitement est déterminé par le processus de réception du signal, mais est choisi automatiquement de la table. 
-Les signaux sont flux d'​exécution synchrone / asynchrone du processus qui reçoit le signal si l'​événement provoquant le signal d'​envoi est exécution synchrone / asynchrone des flux de processus. ​ 
-  * Un événement est flux **synchrone** de l'​exécution du processus qui se produit chaque fois que le programme en cours d'​exécution au même point dans le flot d'​exécution. Des exemples tentent d'​accéder à un emplacement mémoire invalide ou permis, division par zéro, etc. 
- 
-  * Un événement est **asynchrone** si il est pas synchronisé. Des exemples d'​événements asynchrones:​ un signal envoyé par un autre processus (terminaison de signal d'un processus enfant), ou une demande de terminaison externe (utilisateur veut réinitialiser l'​ordinateur). 
- 
-Un signal reçu par un processus peut être généré: 
- 
-  * Soit directement **système d'​exploitation** - où il signale diverses erreurs; 
- 
-  * ou un **processus** - qui peut également envoyer ses propres signaux (le signal passera également par le système d'​exploitation). 
-<note important>​ Si deux signaux sont trop proches car ils pourraient être confondus en un seul. Ainsi, normalement,​ il n'y a pas de mécanisme pour faire en sorte que envoie le signal qu'il a atteint sa destination. </​note>​ 
- 
-Dans certains cas, il faut savoir, sûrement, qui a envoyé un signal pour atteindre sa destination et donc, le processus lui répondre (faire une des actions possibles). Le système d'​exploitation fournit un autre moyen d'​envoyer un signal, qui **garantit** si le signal a atteint sa destination,​ si cette action a échoué. Ceci est accompli en créant une pile de signaux d'une certaine capacité (il doit être fini, ne pas provoquer des situations de débordement). Envoi d'un signal, le système d'​exploitation vérifie si la pile est pleine. Dans ce cas, l'​application échoue, l'​autre signal est placé sur la pile et l'​opération se termine avec succès. 
- 
-Le signal terme est utilisé pour indiquer alternativement être une sorte d'​objets ou réels signaux de ce type.  
- 
-===== Génération de signaux =====  
-En général, les événements générant des signaux se répartissent en trois catégories principales:​ 
- 
-  * Une "​erreur"​ indique qu'un programme a effectué une opération non autorisée et ne peut pas continuer son exécution. Cependant, tous les types d'​erreur ne génèrent pas de signaux (en fait, la plupart ne le font pas). Par exemple, ouvrir un fichier inexistant est une erreur, mais cela ne génère pas de signal. Au lieu de cela, l'​appel système ouvert renvoie -1 indiquant que l'​appel s'est terminé avec une erreur. Généralement,​ les erreurs associées à certaines bibliothèques sont signalées en renvoyant une valeur spéciale. Les erreurs qui génèrent des signaux sont celles qui peuvent apparaître n'​importe où dans le programme, pas seulement dans les appels des bibliothèques. Ils incluent la remise à zéro et l'​accès invalide à la mémoire. 
- 
-  * Un "​événement externe"​ est généralement lié aux E / S et à d'​autres processus. Exemples: nouvelle entrée de données, expiration du temporisateur,​ achèvement d'un processus enfant. 
- 
-  * La ''​ demande explicite ''​ indique l'​utilisation d'un appel système, tel que kill, pour générer un signal. 
- 
-Les signaux peuvent être synchrones ou asynchrones:​ 
- 
-  * Un signal "​synchrone"​ fait référence à une action spécifique du programme et est envoyé (s'il n'est pas bloqué) au cours de cette action. La plupart des erreurs génèrent des signaux synchrones. Les signaux peuvent également être générés de manière synchrone et par des demandes explicites envoyées par un processus lui-même. Sur certains ordinateurs,​ certains types d’erreurs matérielles (généralement des exceptions en virgule flottante) ne sont pas signalés comme étant complètement synchroniques et ils peuvent obtenir certaines instructions ultérieurement. 
- 
-  * Les signaux asynchrones sont générés par des événements incontrôlables par le processus qui les reçoit. Ces signaux arrivent à des moments imprévisibles. Les événements externes génèrent des signaux asynchrones,​ ainsi que des demandes explicites envoyées par d'​autres processus. 
- 
-Un type de signal donné est synchrone ou asynchrone. Par exemple, les signaux d'​erreur sont généralement synchrones car les erreurs génèrent des signaux synchrones. Cependant, tout type de signal peut être généré de manière synchrone ou asynchrone avec une demande explicite. 
-===== Soumission et recevoir des signaux =====  
-Lorsqu'​un signal est généré, il entre dans un état en attente. Normalement,​ il reste dans cet état pendant très peu de temps et est ensuite envoyé au processus de destination. Toutefois, si ce type de signal est actuellement bloqué, il peut rester à l'​état indéfini jusqu'​à ce que les signaux de ce type soient déverrouillés. Une fois que ce type de signal est déverrouillé,​ il sera envoyé immédiatement. 
-Lorsque le signal a été reçu, immédiatement ou avec retard, l'​action spécifiée pour ce signal est exécutée. Pour certains signaux, tels que «SIGKILL» et «SIGSTOP»,​ l'​action est ** fixée ** (le processus est terminé), mais pour la plupart des signaux, le programme peut choisir de: 
-  * ignorer le signal 
-  * spécifie une fonction ** gestionnaire ** 
-  * accepte ** l'​action par défaut ** pour ce type de signal. 
-Le programme spécifie votre choix en utilisant des fonctionnalités telles que [[https://​linux.die.net/​man/​2/​signal|signal]] ou [[https://​linux.die.net/​man/​2/​sigaction|sigaction]] . Lorsque le gestionnaire est en cours d'​exécution,​ ce type de signal est normalement ** verrouillé ** (le déverrouillage sera effectué par une demande explicite du gestionnaire qui gère le signal). 
-<spoiler Exemple d'​utilisation ''​ signal ''>​ 
-Dans le code ci-dessous, nous visons à capturer les signaux "​SIGINT"​ et "​SIGUSR1"​ et à prendre des mesures si nous les recevons. "​SIGINT"​ est reçu à la fois en utilisant la commande "kill -SIGINT <​programme>"​ et en envoyant la frappe "CTRL + c" au programme. 
-<code c> 
-#include <​stdio.h>​ 
-#include <​stdlib.h>​ 
-#include <​signal.h>​ 
-#include <​unistd.h>​ 
-  
-pid_t child1, child2; 
-int child1_pid; 
-  
-  
-void signal_handler(int signum) 
-{ 
-  
-    switch(signum) { 
-        case SIGINT: 
-            printf("​CTRL+C received in %d Exiting\n",​ getpid()); 
-            exit(EXIT_SUCCESS);​ 
-        case SIGUSR1: 
-            printf("​SIGUSR1 received. Continuing execution\n"​);​ 
-    } 
-} 
-  
-int main(void) 
-{ 
-  
-    printf("​Process %d started\n",​ getpid()); 
-  
-    /* Semnale ca SIGKILL sau SIGSTOP nu pot fi prinse */ 
-    if (signal(SIGKILL,​ signal_handler) == SIG_ERR) 
-        printf("​\nYou shall not catch SIGKILL\n"​);​ 
-  
-    if(signal(SIGINT,​ signal_handler) == SIG_ERR) { 
-        printf("​Unable to catch SIGINT"​);​ 
-        exit(EXIT_FAILURE);​ 
-    } 
-  
-    if(signal(SIGUSR1,​ signal_handler) == SIG_ERR) { 
-        printf("​Unable to catch SIGUSR1"​);​ 
-        exit(EXIT_FAILURE);​ 
-    } 
-  
-  
-    printf("​Press CTRL+C to stop us\n"​);​ 
-  
-    while(1) { 
-        sleep(1); 
-    } 
-  
-    return 0; 
-} 
-</​code>​ 
-<note tip> Notez que le signal ''​SIGKILL''​ ne peut pas être traité (''​kill -9 <​program>'' ​ ou ''​kill -SIGKILL <​program>''​). 
-</​note> ​ 
-</​spoiler>​ 
-Si l'​action spécifiée pour un type de signal est de ** ignorer **, alors tout signal de ce type généré pour le processus en question est ignoré. La même chose se produit si le signal est bloqué à ce moment-là. Un signal négligé dans ce mode ne sera jamais reçu, ou si le programme spécifie ensuite une action différente pour ce type de signal, puis le déverrouille. 
-Si un signal est reçu pour lequel aucun type d'​action n'a été spécifié, ** l'​action par défaut ** est exécutée. Chaque type de signal a sa propre action par défaut. Pour la plupart des signaux, l'​action par défaut est ** mettant fin au processus **. Pour certains types de signaux, qui sont des événements sans conséquences majeures, l'​action par défaut consiste à ne rien faire. 
- 
-Lorsqu'​un signal force un processus à se terminer, le processus parent peut déterminer la cause de la terminaison en examinant le code de terminaison signalé par les fonctions '​wait'​ et '​waitpid'​. Les informations pouvant être obtenues incluent le fait que le processus s'est terminé par un signal ainsi que le type de signal. Si un programme que vous exécutez à partir de la ligne de commande est terminé par un signal, le shell affiche généralement des messages d'​erreur. 
-Les signaux qui représentent normalement des erreurs de programme ont une propriété spéciale: lorsqu'​un de ces signaux termine le processus, il écrit également un fichier ** core dump ** qui enregistre le statut du processus à la fin. Vous pouvez examiner le fichier avec un débogueur pour déterminer la cause de l'​erreur. 
-Si vous générez un signal, qui est une erreur de programme, par une requête explicite et que le processus se termine, le fichier est généré comme si le signal avait été généré par une erreur. 
-<note important>​ Si un signal est envoyé au processus alors qu'il exécute un appel système ** bloqueur **, le processus suspend l'​appel,​ exécute le gestionnaire de signaux défini à l'aide de ''​ signal ''​ et alors l'​opération échouera (avec "​errno"​ sur "​EINTR"​) ou l'​opération sera redémarrée. Les systèmes System V se comportent comme dans le premier cas, BSD comme dans le second. Depuis la glibc v2, le comportement est le même que sous BSD, tout dépend de la définition de _BSD_SOURCE mackerel. Le comportement peut être contrôlé par le programmeur en utilisant ''​ sigaction ''​ avec l'​indicateur ''​ SA_RESTART ''​. </​note>​ 
-===== Types standard de signaux ===== 
-Cette section répertorie les noms de différents types de signaux standard et décrit le type d’événements qu’il indique. 
-<​note>​ Chaque nom de signal est un ** macrodefined **, qui est en fait un nombre entier positif (le nombre correspondant à ce type de signal). </​note>​ 
-Un programme ne devrait jamais émettre d'​hypothèses sur le code numérique d'un type de signal particulier,​ mais plutôt s'y référer toujours par son nom. En effet, un numéro pour un type de signal peut ** varier ** d'un système à un autre, mais leurs noms sont standard. Pour obtenir la liste complète des signaux pris en charge par un système, vous pouvez exécuter la ligne de commande: 
-<code bash> 
-$ 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 ​    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 ​     33) SIGRTMIN ​   34) SIGRTMIN+1 
-    35) SIGRTMIN+2 ​ 36) SIGRTMIN+3 ​ 37) SIGRTMIN+4 ​ 38) SIGRTMIN+5 
-    39) SIGRTMIN+6 ​ 40) SIGRTMIN+7 ​ 41) SIGRTMIN+8 ​ 42) SIGRTMIN+9 
-    43) SIGRTMIN+10 44) SIGRTMIN+11 45) SIGRTMIN+12 46) SIGRTMIN+13 
-    47) SIGRTMIN+14 48) SIGRTMIN+15 49) SIGRTMAX-15 50) SIGRTMAX-14 
-    51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 
-    55) SIGRTMAX-9 ​ 56) SIGRTMAX-8 ​ 57) SIGRTMAX-7 ​ 58) SIGRTMAX-6 
-    59) SIGRTMAX-5 ​ 60) SIGRTMAX-4 ​ 61) SIGRTMAX-3 ​ 62) SIGRTMAX-2 
-    63) SIGRTMAX-1 ​ 64) SIGRTMAX 
-</​code>​ 
-Les noms des signaux sont définis dans l'​en-tête ''​ signal.h ''​. Généralement,​ les signaux ont des rôles prédéfinis,​ mais ils peuvent être écrasés par le programmeur. 
-Les plus communs sont les signaux suivants: 
-   * ''​ SIGINT ''​ - transmis au toucher de la combinaison ''​ CTRL + C '';​ 
-   * ''​ SIGQUIT ''​ - transmis en appuyant sur la combinaison de touches ''​ CTRL + \ '';​ 
-   * ''​ SIGSEGV ''​ - transmis lors de l'​accès à un emplacement mémoire invalide, etc. 
-   * ''​ SIGKILL ''​ - ne peut être ignoré ni écrasé. La transmission de ce signal a pour effet de mettre fin au processus, quel que soit le contexte. 
-===== Les messages décrivant des signaux ===== 
-Le meilleur moyen d’afficher une description d’un signal est d’utiliser le [[http://​man7.org/​linux/​man-pages/​man3/​strsignal.3.html|strsignal]] et le [[http: /​man7.org/​linux/​man-pages/​man3/​psignal.3.html|psignal]]. Ces fonctions utilisent un numéro de signal pour spécifier le type de signal à décrire. Vous trouverez ci-dessous un exemple d'​utilisation de ces fonctionnalités:​ 
-<code c msg_signal.c>​ 
-#include <​stdio.h>​ 
-#include <​stdlib.h>​ 
-  
-#define __USE_GNU 
-#include <​string.h>​ 
-  
-#include <​signal.h>​ 
-  
-int main(void) { 
-    char *sig_p = strsignal(SIGKILL);​ 
-  
-    printf("​signal %d is %s\n", SIGKILL, sig_p); 
-  
-    psignal(SIGKILL,​ "death and decay"​);​ 
-  
-    return 0; 
-} 
-</​code>​ 
-Pour compiler et exécuter la séquence, procédez comme suit: 
-<code bash> 
-so@spook$ gcc -Wall -g -o msg_signal msg_signal.c 
-so@spook$ ./​msg_signal ​ 
-signal 9 is Killed 
-death and decay: Killed 
- 
-</​code>​ 
-===== Masques de signalisation. Verrouillage du signal ===== 
-Afin de pouvoir effectuer des opérations de blocage / déblocage de signal, nous devons connaître l'​état de chaque signal à chaque étape du flux d'​exécution. Le système d’exploitation a également besoin de la même chose pour décider du signal à envoyer à un processus (il a besoin de ce type d’informations séparément pour chaque processus). À cette fin, un masque de signaux est utilisé pour chaque processus. 
-<​note>​ Le masque de signal a chaque bit associé à un type de signal. </​note>​ 
-Le masque binaire est utilisé par plusieurs fonctions, y compris la fonction [[http://​man7.org/​linux/​man-pages/​man2/​sigprocmask.2.html|sigprocmask]] utilisée pour modifier le masque de signal de processus. en cours. 
-<code c> 
-int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 
- 
-</​code>​ 
-Le type de données utilisé par les systèmes UNIX pour représenter les masques de signaux est ''​ sigset_t ''​. Les variables de ce type sont // 'non initialisées'​ //. Les opérations sur ce type de données sont: 
-   * démarrage 0; 
-   * initialisation de démarrage de 1; 
-   * bloquer un signal; 
-   * déverrouiller un signal; 
-   * détection du blocage du signal. 
-Les fonctions suivantes permettent de manipuler le masque de bits. Ils ne décident pas de l'​action de bloquer ou de déverrouiller un signal, mais placent simplement le signal dans le masque binaire (ajoutez le bit correspondant au signal à 1 et supprimez-le à 0), puis utilisez ''​ sigprocmask ''​ pour définir l’action de verrouillage / déverrouillage effectif. Vous pouvez trouver plus de détails sur ces fonctionnalités [[https://​linux.die.net/​man/​3/​sigemptyset|aici]]. 
-<code c> int sigemptyset(sigset_t *set); 
-int sigfillset(sigset_t *set); 
-int sigaddset(sigset_t *set, int signo); 
-int sigdelset(sigset_t *set, int signo); 
-int sigismember(sigset_t *set, int signo); 
-</​code>​ 
-<note warning> Avant d'​utiliser les fonctions '​sigaddset',​ '​sigdelset'​ et '​sigismember'​ sur un sigset_t, ce type doit être initialisé à l'aide de ''​ gemptset ''​ ou ''​ sigfillset ''​. Le comportement n'est pas défini autrement. </​note>​ 
-L'​équation ci-dessous est un cas d'​utilisation de la fonction du masque de signal, dans laquelle, toutes les 5 secondes, le signal SIGINT est verrouillé / déverrouillé:​ 
-<code c> 
-sigset_t set; 
-  
-sigemptyset(&​set);​ 
-sigaddset(&​set,​ SIGINT); 
-  
-while (1) { 
-    sleep(5); 
-    sigprocmask(SIG_BLOCK,​ &set, NULL); 
-    sleep(5); 
-    sigprocmask(SIG_UNBLOCK,​ &set, NULL); 
-} 
-</​code>​ 
-Une autre valeur que le premier paramètre de [[http://​man7.org/​linux/​man-pages/​man2/​sigprocmask.2.html|sigprocmask]] peut prendre est le ''​ SIG_SETMASK '',​ qui spécifie clairement simplement que l'​ancien masque (le troisième paramètre) est remplacé par le deuxième paramètre (le nouveau masque). Un exemple d'​utilisation est disponible à l'​adresse [[https://​support.sas.com/​documentation/​onlinedoc/​sasc/​doc750/​html/​lr1/​zlocking.htm "cette adresse]]. 
-===== Traitement du signal ===== 
-Le traitement des signaux se fait en associant une fonction (** gestionnaire **) à un signal. La fonction sera appelée lorsque le processus recevra le signal. 
-Traditionnellement,​ la fonction utilisée pour associer les gestionnaires pour traiter un signal était [[http://​man7.org/​linux/​man-pages/​man2/​signal.2.html|signal]]. Pour remédier aux faiblesses de cette fonctionnalité,​ le standard POSIX a défini la fonction [[https://​linux.die.net/​man/​2/​sigaction|sigaction]] pour associer un gestionnaire à un signal. La "​sigaction"​ offre plus de contrôle au prix d'un degré de complexité plus élevé. 
-<code c> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 
-</​code>​ 
-Le composant important de la fonction ''​ sigaction ''​ est la structure du même nom, décrite dans la page de la fonction manuelle: 
-<code c> 
-struct sigaction { 
-               ​void ​    ​(*sa_handler)(int);​ 
-               ​void ​    ​(*sa_sigaction)(int,​ siginfo_t *, void *); 
-               ​sigset_t ​  ​sa_mask;​ 
-               ​int ​       sa_flags; 
-}; 
-</​code>​ 
-Si le champ sa_flags spécifie l'​indicateur ''​ SA_SIGINFO '',​ le gestionnaire utilisé est celui spécifié par ''​ sa_sigaction ''​. Sinon, le gestionnaire utilisé est ''​ sa_handler ''​. Le masque des signaux qui doivent être bloqués lors de l'​exécution du gestionnaire est représenté par ''​ sa_mask ''​. 
- 
-Un exemple d'​association d'un gestionnaire de signal est présenté ci-dessous: 
-<code c> 
-#include <​signal.h>​ 
-... 
-  
-/* SIGUSR2 handler */ 
-static void usr2_handler(int signum) { 
-    /* actions that should be taken when the signal signum is received */ 
-    ... 
-} 
-  
-int main(void) { 
-    struct sigaction sa; 
-  
-    memset(&​sa,​ 0, sizeof(sa));​ 
-  
-    sa.sa_flags ​  = SA_RESETHAND; ​  /* restore handler to previous state */ 
-    sa.sa_handler = usr2_handler;​ 
-    sigaction(SIGUSR2,​ &sa, NULL); 
-  
-    return 0; 
-} 
-</​code>​ 
-<note warning> Vous pouvez choisir de configurer votre propre gestionnaire ou d’utiliser un gestionnaire prédéfini. Vous pouvez utiliser ''​ SIG_IGN ''​ pour ignorer le signal ou ''​ SIG_DFL ''​ pour exécuter l'​action par défaut (mettre fin au processus, ignorer le signal, etc.). </​note>​ 
-<spoiler Détails de la structure ''​ siginfo_t ''>​ 
-Si le drapeau ''​ SA_SIGINFO ''​ est défini, le ''​ sa_sigaction ''​ de la structure '​sigaction'​ est utilisé pour spécifier le gestionnaire associé au signal. Le gestionnaire utilisé dans ce cas reçoit trois paramètres et peut être utilisé pour transmettre des informations utiles avec le processus. Le troisième argument ('void *') est rarement utilisé. Le deuxième argument, de type siginfo_t, définit une structure contenant des informations utiles sur le contexte de l'​occurrence du signal et d'​autres informations pouvant être fournies par le programmeur. La définition de la structure se trouve dans la page de manuel de la fonction [[http://​man7.org/​linux/​man-pages/​man2/​sigaction.2.html|sigaction]]. 
-<code c> 
-siginfo_t { 
-               ​int ​     si_signo; ​   /* Signal number */ 
-               ​int ​     si_errno; ​   /* An errno value */ 
-               ​int ​     si_code; ​    /* Signal code */ 
-               ​int ​     si_trapno; ​  /* Trap number that caused 
-                                        hardware-generated signal 
-                                        (unused on most architectures) */ 
-               ​pid_t ​   si_pid; ​     /* Sending process ID */ 
-               ​uid_t ​   si_uid; ​     /* Real user ID of sending process */ 
-               ​int ​     si_status; ​  /* Exit value or signal */ 
-               ​clock_t ​ si_utime; ​   /* User time consumed */ 
-               ​clock_t ​ si_stime; ​   /* System time consumed */ 
-               ​sigval_t si_value; ​   /* Signal value */ 
-               ​int ​     si_int; ​     /* POSIX.1b signal */ 
-               ​void ​   *si_ptr; ​     /* POSIX.1b signal */ 
-               ​int ​     si_overrun; ​ /* Timer overrun count; POSIX.1b timers */ 
-               ​int ​     si_timerid; ​ /* Timer ID; POSIX.1b timers */ 
-               ​void ​   *si_addr; ​    /* Memory location which caused fault */ 
-               ​long ​    ​si_band; ​    /* Band event (was int in 
-                                        glibc 2.3.2 and earlier) */ 
-               ​int ​     si_fd; ​      /* File descriptor */ 
-               ​short ​   si_addr_lsb;​ /* Least significant bit of address 
-                                        (since kernel 2.6.32) */ 
-} 
-</​code>​ 
-Les membres de la structure ne sont initialisés que lorsque leurs valeurs sont utiles. Les membres ''​ si_signo '',​ ''​ si_errno ''​ et ''​ and_code ''​ sont toujours définis pour tous les signaux. Le reste de la structure peut être une union, de sorte que seuls les champs qui détectent le signal reçu doivent être lus. Par exemple, l'​appel système '​kill',​ les signaux POSIX.1b et ''​ SIGCHLD ''​complètent'​ '​si_pid'​ '​et'​ '​si_uid'​ ', et ''​SIGILL,​ SIGFPE, SIGSEGV ''​et''​ SIGBUS ''​complète'​ '​and_addr'​ 'avec l'​adresse à l'​origine de l'​erreur. 
-</​spoiler>​ 
-===== Rapport de processus ===== 
-Pour transmettre un signal, vous pouvez utiliser le [[http://​man7.org/​linux/​man-pages/​man2/​kill.2.html|kill]] ou le [[http://​man7.org/​linux/​ Man-pages / man2 / sigqueue.2.html | sigqueue]]. Le [[http://​man7.org/​linux/​man-pages/​man2/​kill.2.html|kill]] présente le désavantage de ne pas garantir le signal reçu par le processus de destination. S'il est nécessaire d'​envoyer un signal à un processus et de savoir avec certitude qu'il est recommandé d'​utiliser la fonction [[http://​man7.org/​linux/​man-pages/​man2/​sigqueue.2.html|sigqueue]]:​ 
-<code c>int sigqueue(pid_t pid, int signo, const union sigval value); 
-</​code>​ 
-La fonction envoie le signal ''​ signo ''​ avec les paramètres spécifiés par ''​ valeur ''​ au processus avec l'​identifiant '​pid'​. Si le signal est égal à zéro, les erreurs éventuelles sont vérifiées,​ mais aucun signal n'est envoyé. Le signal null peut être utilisé pour vérifier que le pid est valide. 
-La valeur pouvant être envoyée avec le signal est une union: 
-<code c>  
-union sigval { 
-     ​int ​  ​sival_int;​ 
-     void *sival_ptr; 
-}; 
-</​code>​ 
-Un paramètre envoyé ici apparaît dans la ''​ si_value ''​ de la structure ''​ siginfo_t '',​ reçue par le gestionnaire de signaux. Évidemment,​ ** ** n’a aucun sens de transmettre des pointeurs d’un processus à un autre. 
- 
-Les conditions requises pour qu'un processus soit autorisé à envoyer un signal à un autre processus sont les mêmes que dans le cas d'une suppression. Si le signal spécifié est bloqué à ce moment-là, la fonction quittera immédiatement et si l'​indicateur ''​ SA_SIGINFO ''​ est défini et qu'il y a des ressources nécessaires,​ le signal sera mis en file d'​attente à l'​état en attente (un processus peut avoir un maximum de '​SIGQUEUE_MAX'​ des signaux). De plus, lorsque le signal est reçu, le champ ''​ si_code '',​ transmis à la structure ''​ siginfo '',​ sera défini sur ''​ SI_QUEUE '',​ et ''​ and_value ''​ sur ''​ valeur ''​. 
- 
-Si le drapeau ''​ SA_SIGINFO ''​ n'est pas défini, alors ''​ signo '',​ mais pas nécessairement '​valeur',​ sera envoyé au moins une fois au processus qui devrait recevoir le signal. 
-===== En attente d'un signal ===== 
-<spoiler Approches en attente d'un signal> 
-Si des signaux de communication et / ou de synchronisation sont utilisés, il faut souvent s'​attendre à ce qu'un type de signal particulier parvienne au processus. Un moyen simple d'y parvenir est une boucle dont la condition de sortie serait le réglage correct d'une variable (la variable doit être du type '​sig_atomic_t'​). Par exemple: 
-<code c>while (!signal_has_arrived);​ 
-</​code>​ 
-Le principal inconvénient de l'​approche ci-dessus (type // '​occupation-attente'//​) est le temps que prend le processeur pour que le processus soit considéré comme une perte inutile. Une option serait d'​utiliser la fonction [[http://​man7.org/​linux/​man-pages/​man3/​sleep.3.html|sleep]]:​ 
-<code c> 
-while (!signal_has_arrived) { 
-    sleep(1); 
-} 
-</​code>​ 
-Une telle approche ne prendrait plus trop de temps au processeur, mais le temps de réponse en cas de signal est assez élevé. Une autre solution au problème est [[http://​man7.org/​linux/​man-pages/​man2/​pause.2.html|pause]] (qui bloque le temps d'​exécution jusqu'​à ce que le processus en cours soit interrompu par un signal). . Bien que cette approche semble très simple, elle introduit souvent des blocages bloquant l'​horaire indéfini. Un exemple de ceci est la pseudo-résolution ci-dessous, à la question de l'​attente d'un signal: 
-<code c> 
-while (!signal_has_arrived) { 
-    pause(); 
-} 
-</​code>​ 
-La boucle est nécessaire pour éviter que le processus ne soit interrompu par des signaux autres que ceux attendus. Il peut arriver que le signal soit ** après ** le test de la variable et ** avant ** l'​appel de la fonction de pause. Dans ce cas, le processus est bloqué et si aucun autre signal ne semble provoquer la sortie [[http://​man7.org/​linux/​man-pages/​man2/​pause.2.html|pause]],​ il sera reste bloqué indéfiniment. 
-</​spoiler>​ 
-La meilleure solution pour attendre un signal peut être faite en utilisant [[http://​man7.org/​linux/​man-pages/​man2/​sigsuspend.2.html|sigsuspend]]:​ 
-<code c> 
-int sigsuspend(const sigset_t *set); 
-</​code>​ 
-La fonction remplace le masque de signal de processus verrouillé par un ensemble et suspend le processus jusqu'​à la réception d'un signal qui n'est pas bloqué par le nouveau masque. A la sortie, la fonction restaure l'​ancien masque de signalisation. 
-Dans la séquence ci-dessous, [[http://​www.kernel.org/​doc/​man-pages/​online/​pages/​man2/​sigsuspend.2.html|sigsuspend]] est utilisé pour suspendre le processus en cours jusqu'​à sa réception. signal ''​ SIGINT ''​. Les signaux ''​ SIGKILL et SIGSTOP '',​ bien que présents dans le masque de signal, ne seront pas bloqués: 
-<code c> 
-sigset_t set; 
-  
-/* block all signals except SIGINT */ 
-sigfillset(&​set);​ 
-sigdelset(&​set,​ SIGINT); 
-  
-/* wait for SIGINT */ 
-sigsuspend(&​set);​ 
-</​code>​ 
-===== Timers sous Linux ===== 
- 
-Sous Linux, l'​utilisation de minuteries est liée à l'​utilisation de signaux. Cela se produit car la plupart des fonctions de la minuterie utilisent des signaux. 
- 
-Un ** timer ** est généralement un ensemble dont la valeur est décrémentée dans le temps. Lorsque le tout atteint 0, la minuterie expire. Sous Linux, l’expiration du minuteur entraîne généralement la transmission d’un signal. Définir un "​gestionnaire de minuterie"​ (routine appelée lorsque la minuterie expire) équivaut donc à définir un gestionnaire pour le signal associé. 
- 
-Enregistrer un minuteur sous Linux signifie spécifier un intervalle de temps après lequel un minuteur expire et configurer le gestionnaire pour qu'il s'​exécute. La configuration du gestionnaire peut être réalisée au moyen de la fonction "​sigaction"​ (à l'​expiration du délai imparti, un signal est généré, lequel génère à son tour l'​exécution du gestionnaire associé) ou directement via les paramètres du [ http://​www.kernel.org/​doc/​man-pages/​online/​pages/​man2/​timer_create.2.html|timer_create]]. 
-L'​utilisation d'une minuterie implique plusieurs étapes: 
-  * ** création d'une minuterie ** en utilisant [[http://​www.kernel.org/​doc/​man-pages/​online/​pages/​man2/​timer_create.2.html|timer_create]]:​ 
-<code c> 
-int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid) ​ 
-</​code>​ 
-Le timer créé est identifié par ''​ timerid ''​. Grâce à la structure '​sigevent',​ vous définissez la manière dont le minuteur interagira avec le processus / thread qu'il a lancé. Exemple d'​utilisation:​ 
-<code c> 
-timer_t timerid; 
-struct sigevent sev; 
-  
-sev.sigev_notify = SIGEV_SIGNAL; ​            /* notification method */ 
-sev.sigev_signo = SIGRTMIN; ​                 /* Timer expiration signal */ 
-sev.sigev_value.sival_ptr = &​timerid; ​       ​ 
-timer_create(CLOCK_REALTIME,​ &sev, &​timerid);​ 
-</​code>​ 
-Le premier argument peut mesurer le temps réel du système, le temps d'​exécution du processus ou le temps d'​exécution du processus dans l'​espace utilisateur et l'​espace noyau. À l'​expiration du délai, le minuteur transmettra le signal enregistré à ''​ sev.sigev_signo ''​. 
- 
-   * ** réinstallation d'un minuteur - ** à l'aide de la fonction [[http://​www.kernel.org/​doc/​man-pages/​online/​pages/​man2/​timer_settime.2.html|timer_settime]]:​ 
-<code c> 
-int timer_settime(timer_t timerid, int flags, ​ 
-                          const struct itimerspec *new_value, 
-                          struct itimerspec * old_value); ​ 
-</​code>​ 
-Le renforcement du minuteur implique de compléter la structure itimerspec en spécifiant l'​heure de début du minuteur et l'​intervalle d'​expiration du délai d'​attente (les plages sont mesurées en secondes et en nanosecondes). Exemple d'​utilisation:​ 
-<code c> 
-its.it_value.tv_sec = freq_nanosecs / 1000000000; /* Initial expiration in secs*/ 
-its.it_value.tv_nsec = freq_nanosecs % 1000000000;/​* Initial expiration in nsecs*/ 
-its.it_interval.tv_sec = its.it_value.tv_sec; ​    /* Timer interval in secs */ 
-its.it_interval.tv_nsec = its.it_value.tv_nsec; ​  /* Timer interval in nanosecs */  
-  
-timer_settime(timerid,​ 0, &its, NULL); 
-</​code>​ 
-  * ** suppression d'une minuterie - ** en utilisant la fonction [[http://​man7.org/​linux/​man-pages/​man2/​timer_delete.2.html|timer_delete]] 
-<code c> 
-int timer_delete(timer_t timerid); ​ 
-</​code>​ 
-<note warning> Pour utiliser les fonctions ci-dessus, le programme doit être compilé avec ''​ -lrt ''​ </​note>​ 
-Une des manières d'​utiliser les minuteries consiste à implémenter les [[http://​www.kernel.org/​doc/​man-pages/​online/​pages/​man3/​sleep.3.html|sleep] fonctionnalités de veille] ou [[http://​www.kernel.org/​doc/​man-pages/​online/​pages/​man2/​nanosleep.2.html|nanosleep]]. L’utilisation de [[http://​www.kernel.org/​doc/​man-pages/​online/​pages/​man3/​sleep.3.html|sleep]] réside dans la simplicité. Les inconvénients sont une faible résolution (secondes) et une interaction possible du signal (en particulier «SIGALRM»). [[http://​www.kernel.org/​doc/​man-pages/​online/​pages/​man2/​nanosleep.2.html|nanosleep]] a un appel plus complexe mais fournit une résolution au nanoseconde près et est //'​signal-safe'// ​ (n'​interagit pas avec les signaux). 
-===== Exercices ===== 
- 
-Pour résoudre le labo, veuillez cloner [[https://​www.github.com/​upb-fils/​sde|repository]]. si vous en avez déjà un, lancez svp ''​ git pull ''​. 
- 
-<note tip> Pour vous aider à mettre en œuvre les exercices de laboratoire,​ il existe un fichier ''​ utils.h ''​ avec des fonctions utiles dans le répertoire ''​ utils ''​ de l'​archive. </​note>​ 
- 
-<note important>​ Lorsque vous utilisez sigaction, vous initialiserez généralement la structure ''​ sa_flags ''​ de ''​ struct sigaction ''​ sur ''​ 0 ''​. </​note>​ 
-==== Exercice 1 - pipe (2p) ==== 
- 
-Allez dans le répertoire ''​ 1-pipe '',​ vous avez deux programmes: ''​pipe''​ et ''​reader''​. 
- 
-=== 1a - Tuyau et fourche (1p) === 
-Dans le fichier ''​ pipe.c '',​ créez un tuyau puis créez un fork. Dans le processus parent, fermez la fin de lecture du tuyau 
-et écrivez les données dans // buffer // dans le tube. 
- 
-Dans le processus enfant, fermez l'​écriture du canal, lisez les données de la mémoire tampon dans la mémoire tampon et affichez-les à l'​écran. 
- 
-Suivez les lignes TODO 1. 
- 
-=== 1b - Pipe et exec === 
-Dans le fichier ''​reader.c'',​ lisez le texte stocké dans la variable // buffer // et affichez-la. 
- 
-Dans le fichier ''​pipe.c'',​ modifiez le processus enfant afin qu'​après la fermeture de l'​extrémité du tuyau, 
-redirige // stdin // vers la fin de la lecture du tube et exécute (en utilisant l'une des fonctions // exec //) 
-le programme ''​lecteur''​. 
- 
-Regardez TODO 2 pendant des mois. 
- 
-==== Exercice 2 - hitme (2p) ==== 
- 
-Allez dans le dossier ''​ 2-hitme / ''​ et analysez le contenu du fichier hitme.c. Compiler et exécuter le programme. 
- 
-Utilisez la commande ''​kill -l''​ pour lister tous les signaux disponibles. Quelle est la valeur du signal SIGKILL? 
-Dans une autre console, envoyez le programme hitme avec des signaux compris entre 20 et 25 comme suit: <code bash> 
-kill -20 $(pidof hitme) 
-kill -21 $(pidof hitme) 
-kill -22 $(pidof hitme) 
-kill -23 $(pidof hitme) 
-kill -24 $(pidof hitme) 
-kill -25 $(pidof hitme) 
-</​code>​ 
-=== Répéter l'​envoi de signaux (1p) === 
- 
-Essayez d’envoyer deux fois le même signal et d’expliquer le comportement. 
-   * Que dois-je changer pour envoyer le même signal deux fois? 
-   * Analysez soigneusement ce qui est défini sur ''​ signaux.sa_flags ''​. 
-   * Vous pouvez revenir au [[#Starting Signal Processing]] ou rechercher la structure [[http://​www.kernel.org/​doc/​man-pages/​online/​pages/​man2/​sigaction.2.html|sigaction]] 
- 
-<note tip> Notez que la suppression de l'​indicateur ''​ SA_RESETHAND ''​ ne rétablira plus le gestionnaire à la valeur par défaut après la réception du premier signal. </​note>​ 
- 
-=== SIGSEGV (1p) === 
- 
-Suivez le commentaire ''​ TODO 2 ''​ et les autres index que vous trouvez dans le code pour provoquer un accès invalide à la mémoire. 
-   * ** Que se passe-t-il? ** 
- 
-Si vous êtes curieux de savoir ce qui se passe en détail, vous pouvez consulter [[so:​laboratoare-2013:​laborator-07| laboratoire de mémoire virtuelle]]. 
- 
-==== Exercice 3 - Signaux normaux / signaux en temps réel (1p) ==== 
- 
-Allez dans le répertoire ''​ 3-signaux ''​ et regardez le contenu du fichier ''​ signaux.c ''​. Le programme compte le nombre d'​appels du gestionnaire de signaux en cas d'​envoi de signaux ''​ SIGINT ''​ et ''​ SIGRTMIN ''​ 
- 
-Démarrez le programme "​signaux":​ <code bash> ./ signaux </​code>​ dans une console 
- 
-Pour le cas de signaux "​normaux",​ dans une autre console, exécutez le script "​send_normal.sh":​ <code bash> ./ send_normal.sh </​code>​ 
-Pour les signaux en temps réel, dans une autre console, exécutez le script send_rt.sh: <code bash> ./ send_rt.sh </​code>​ 
-Pour fermer l'​exécutable ''​signaux'',​ le signal SIGQUIT est envoyé. Où se situe la différence?​ 
-Lisez la section "Man 7 Signal"​ de la section "​Signaux en temps réel" et passez en revue la section [[# Types de signaux standard | Types de signaux standard]]. 
- 
-<note tip> La différence entre le nombre de signaux reçus est due au fait que les signaux dont l’indice est entre «SIGRTMIN» et «SIGRTMAX» sont des signaux en temps réel et qu’ils sont donc assurés d’atteindre leur destination. Voir [[http://​www.linuxprogrammingblog.com/​all-about-linux-signals?​page=9 | lien]]. </​note>​ 
-==== Exercice 4 - askexit (2p) ==== 
- 
-Allez dans le répertoire ''​ 4-askexit ''​ et regardez le code source. Le programme est occupé à attendre, en affichant des numéros consécutifs sur la console. 
- 
-Vous devez terminer le programme pour intercepter les signaux générés par ''​ CTRL + \ '',​ ''​ CTRL + C ''​ et ''​ SIGUSR1 ''​ (utilisez la commande '​kill'​). Le gestionnaire associé à chacun des signaux sera "​ask_handler"​. Pour chaque signal reçu, il sera demandé à l'​utilisateur s'il souhaite ou non arrêter l'​exécution. 
- 
-Testez la fonctionnalité du programme. 
-<note tip> Notez que l'​utilisation des fonctions ''​printf'',​ ''​ scanf ''​ dans le traitement du signal peut être problématique,​ car ces fonctions ne sont pas sécurisées. </​note>​ 
- 
-  * Reportez-vous à la section [[#​Traitement du signal]]. 
- 
-==== Exercice 5 - nohup (1p) ==== 
- 
-Entrez le répertoire "​5-nohup"​ et créez un programme appelé "​mynohup"​ qui simule la commande [[http://​linux.die.net/​man/​1/​nohup|nohup]]. Le programme reçoit, en tant que premier paramètre, le nom d'une commande à exécuter. Le reste des paramètres sont les arguments pour appeler la commande; la liste des arguments peut être nulle. 
- 
-Le programme exécuté par ''​ mynohup ''​ ne doit pas être averti de la fermeture du terminal auquel il était connecté. Vous devrez ignorer le signal ''​SIGHUP''​ délivré par le processus à la fin de la session en cours. 
-   * Passez en revue la section sur [[#​Traitement du signal]]. 
- 
-Si le fichier de sortie standard était lié à un terminal, il devrait être redirigé vers un fichier défini par la macro ''​ NOHUP_OUT_FILE ''​. 
-   * Utilisez l'​appel [[http://​www.kernel.org/​doc/​man-pages/​online/​pages/​man3/​isatty.3.html|isatty]]. 
- 
-Pour tester, exécutez <code bash> ./ mynohup sleep 120 & </​code>​ 
-Après le lancement, fermez la session shell en cours: soit en envoyant un signal SIGHUP, soit en utilisant l’icône «X» située à droite de la fenêtre. 
- 
-Depuis une autre console, exécutez <code bash> ps -ef | grep sleep </​code>​ 
-Qui est le nouveau parent du processus? 
-   * Reportez-vous à la section [[#​Traitement du signal]] et à la section [[sde: labs: 04_en # Remplacer une image de processus sous Linux]] et à la section [[sde: laboratoires:​ 03_en # Redirects]] des laboratoires précédents. 
-L'​utilisation de la commande ''​exit''​ ou de la combinaison de touches ''​Ctrl-d''​ n'​enverra pas un signal SIGHUP au processus de veille; vous pouvez tester en utilisant ''​ sleep 120 & '',​ fermez le shell actuel en utilisant l'une des deux méthodes, puis vérifiez que le processus est toujours en cours d'​exécution. 
-==== Exercice 6 - zombie (2p) ==== 
- 
-Allez dans le répertoire ''​ 6-zombie ''​ et regardez le contenu de ''​ mkzombie.c ''​ et de ''​nozombie.c''​. Chaque programme créera un nouveau processus enfant qui appellera uniquement ''​ exit ''​. 
- 
-Déployez ''​mkzombie''​ sans attendre la fin de l'​enfant. Le processus parent attendra ''​ TIMEOUT ''​ secondes et quittera (suivez // TODO //). 
-Depuis une autre console, lancez: <code bash> 
-ps -eF | grep zombie 
-</​code>​ 
-Notez que le processus enfant, bien qu’il ne soit pas en cours d’exécution,​ apparaît dans la liste des processus sous la forme ''​defunct''​ et comporte un pid (unique pour le système à ce moment-là). Vous remarquez également qu’après la mort du processus parent, le processus zombie disparaît. 
- 
-Déployez ''​ nozombie ''​ sans utiliser les fonctions '​wait'​ wait afin que le processus bébé ne passe pas à l'​état de zombie. ''​ nozombie ''​ attendra ''​ TIMEOUT ''​ quelques secondes et sortira. Utilisez le signal ''​ SIGCHLD ''​ (des informations sont disponibles dans [[http://​linux.die.net/​man/​2/​sigaction|sigaction(2)]] et [[http://​linux.die.net/​man/​ 3 / wait | attente (2)]]). Voir également les sections [[#​Traitement du signal]] et [[sde:​laboratoare:​04_ro#​ Création d'un processus Linux]]. 
-<note tip> 
-Si le parent ignore explicitement le signal SIGCHLD en définissant le gestionnaire sur SIG_IGN (au lieu d'​ignorer le signal par défaut), l'​état de sortie des enfants sera ignoré et les enfants ne deviendront pas des processus zombies. 
-</​note>​ 
  
sde/laboratoare/05.1553064692.txt.gz · Last modified: 2019/03/20 08:51 by iuliana.marin
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0