Le carnet On est
Cours 2 · encre verte

Signaux & sémaphores

Synchroniser et faire dialoguer des processus Unix. Des interruptions logicielles asynchrones aux primitives POSIX.4 d'exclusion mutuelle.

~ 35 min de lecture Programmation système & réseau 14 flashcards

📄 Ressources du cours

📥 Télécharger les slides  ·  🔗 Ouvrir dans un nouvel onglet

🎥 Vidéos recommandées

1. Besoins de communication entre processus

Étanchéité des espaces mémoire Deux processus peuvent éventuellement partager le même code (ceci est sous le contrôle du système) mais les espaces mémoires réservés aux variables sont complètement étanches : un processus n'a aucun accès aux variables d'un autre processus.

On peut néanmoins créer une application multitâche et avoir besoin de synchroniser ces tâches ou d'échanger des informations entre elles. Les systèmes d'exploitation sont donc munis de mécanismes appropriés.

Synchronisation
par signaux ou sémaphores.
Communication de données
par fichiers, tubes (pipes), files de messages (queues, boîtes à lettres), variables implantées dans un segment de mémoire partagée.

2. Qu'est-ce qu'un signal ?

Définition Un signal est une interruption logicielle asynchrone d'un processus. Le signal peut être envoyé par un processus ou par le noyau.
  • Les signaux sont identifiés par un numéro entier et un nom symbolique décrit dans signal.h.
  • Comme pour une interruption matérielle, la réception d'un signal interrompt le traitement en cours et exécute automatiquement la fonction associée au signal (programmation événementielle).
  • En langage C, l'association entre le numéro du signal et la fonction est réalisée par un appel à la fonction système signal().
  • La fonction sigaction() peut être utilisée à la place de signal().
Émetteur
Envoyé par un processus.
Destinataire
Reçu par un autre processus (éventuellement le même).
Véhicule
Acheminé par le noyau.

Comment réagit un processus qui reçoit un signal ?

  1. Il interrompt le traitement en cours.
  2. Il exécute la fonction de traitement du signal.
  3. Il reprend l'exécution du traitement interrompu.
Remarques sur le réveil Si le processus était endormi, il est réveillé par le signal. Après l'exécution de la fonction associée :
  • s'il est réveillé avant la fin d'une temporisation, il ne se rendort pas ;
  • par contre s'il attendait la fin d'une entrée-sortie, il continue à attendre.

Comportement associé à la réception

Comportement par défaut Le plus souvent : terminaison anormale du processus (ex. violation de mémoire, division par zéro).
Personnalisations Le processus peut ignorer le signal, ou redéfinir par une fonction spécifique son comportement à la réception.

3. Origine des signaux

Frappe clavier
CTRL-C, CTRL-\, CTRL-Z
Erreurs programme (transmis par le noyau)
Violation mémoire : SIGSEGV
Division par zéro : SIGFPE
Primitives système
kill(), alarm().
Commande shell
$ kill -KILL 2400
$ kill -9 2400
La commande ci-dessus envoie le signal SIGKILL au processus d'identité 2400.

4. Liste des signaux les plus fréquents

NomDescription
1SIGHUPTerminaison du processus leader
2SIGINTFrappe d'interruption (CTRL-C)
3SIGQUITFrappe de quit (CTRL-\)
4SIGILLInstruction illégale
6SIGABRTProblème matériel ou appel de la fonction abort()
7SIGBUSUne erreur liée à l'accès à la mémoire
8SIGFPEErreur arithmétique
9SIGKILLSignal de terminaison — la réaction à ce signal ne peut être redéfinie
10SIGUSR1Signal utilisateur
11SIGSEGVViolation écriture mémoire
12SIGUSR2Signal utilisateur
13SIGPIPEÉcriture sur un tube non ouvert en lecture
14SIGALRMFin de temporisateur
15SIGTERMTerminaison normale d'un processus
17SIGCHLDTerminaison (arrêt) d'un fils
18SIGCONTContinuation d'un processus arrêté par SIGSTOP
19SIGSTOPSuspension d'exécution — réaction non redéfinissable
20SIGTSTPFrappe du caractère de suspension (CTRL-Z)
Non déroutables SIGKILL (9) et SIGSTOP (19) ne peuvent ni être ignorés, ni être redéfinis.

5. Envoi de signaux en C — kill()

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

La primitive kill permet d'émettre le signal sig (désigné par son nom symbolique ou son numéro) à :

  • le processus d'identité pid, si pid > 0 ;
  • tous les processus du même groupe que le processus appelant, si pid = 0 ;
  • tous les processus du groupe dont le gid est la valeur absolue de pid, si pid < -1.
Droits Les processus émetteur et destinataire(s) doivent être du même propriétaire, ou que le premier soit le super-utilisateur (root).
Valeur de retour Renvoie 0 en cas de succès et -1 sinon.

6. Les trois manières de répondre à un signal

1. Exécution de l'action par défaut

Cinq traitements par défaut sont possibles :

ActionEffetExemples
exitProvoque la terminaison du processusSIGINT, SIGKILL, SIGALRM
coreSauvegarde l'état de la mémoire et termine le processusSIGFPE, SIGBUS, SIGSEGV
stopSuspend l'exécution du processusSIGSTOP
ignoreLe signal est ignoréSIGCHLD
continueLe processus suspendu reprend son exécution ou le signal est ignoréSIGCONT

2. Ignorance du signal

On peut ignorer un certain nombre de signaux — sauf par exemple SIGKILL et SIGSTOP.

3. Interception puis invocation d'une fonction (handler)

Le signal peut être rattrapé par le processus destinataire, ce qui provoque un déroutement et le lancement d'une routine spécifique de traitement (handler). Après le traitement, le processus reprend où il a été interrompu.

Remarque Un signal peut être dérouté soit à travers les appels systèmes signal() et sigaction() à partir d'un programme C/C++ ; ou en utilisant la commande trap à partir du shell.
Exemple : trap 'commande' signal1 signal2 …

7. Pipeline d'un signal — vue d'ensemble

Émetteur kill(pid, sig) Noyau masque sa_mask file en attente Handler sa_handler(sig) Reprise contexte restauré Destinataire processus cible

Le noyau filtre via le masque, met le signal en attente si bloqué, sinon déroute vers le handler ; puis le contexte interrompu est restauré.

8. Préparer la réception — signal()

#include <signal.h>

void (*signal(int signum, void (*handler)(int)))(int);

L'appel système signal() permet d'installer handler comme fonction de traitement lors de la réception du signal signum. Cette fonction ne pourra prendre qu'un argument de type entier : le numéro du signal qui l'aura appelée. La fonction renvoie SIG_ERR en cas d'échec.

signum
Le signal à intercepter.
handler
Pointeur sur une fonction qui gère le signal — c'est simplement en C l'identificateur de cette fonction — ou une constante :
SIG_DFL : comportement par défaut (terminaison)
SIG_IGN : ignorer le signal

Récupérer le nom symbolique

#include <string.h>

char *strsignal(int sig);

La fonction strsignal permet de renvoyer le nom symbolique du signal correspondant au numéro sig.

Exemple complet

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>

/* fonction de déroutement */
void handler(int sig) {
    printf("Bien reçu %d %s\n", sig, strsignal(sig));
    if (sig == SIGINT) {
        printf("Fin volontaire\n");
        exit(1);
    }
}

void main(void) {
    signal(SIGINT,  handler); /* Ctrl-c  : signal 2  */
    signal(SIGQUIT, handler); /* Ctrl-\  : signal 3  */
    signal(SIGTSTP, handler); /* Ctrl-z  : signal 20 */
    /* SIGKILL est non déroutable */
    if (signal(SIGKILL, handler) == SIG_ERR) perror("SIGKILL");
    for (;;);
}

Constantes SIG_IGN et SIG_DFL

La fonction de traitement peut être remplacée par une des constantes suivantes :

  • SIG_IGN — indique que le signal doit être ignoré.
  • SIG_DFL — indique de rétablir l'action par défaut.
/* Rendre Ctrl-C (SIGINT) inopérant
   l'arrêt de ce programme doit se faire avec kill -9 pid */
#include <signal.h>

void main(void) {
    signal(SIGINT, SIG_IGN);
    for (;;);
}

9. L'appel système sigaction()

La primitive sigaction() associe un signal à un contexte de déroutement, représenté par une structure sigaction.

struct sigaction {
    void     (*sa_handler)();  /* SIG_DFL ou SIG_IGN ou ptr sur handler */
    sigset_t   sa_mask;        /* signaux supplémentaires à bloquer
                                  pendant l'exécution du gestionnaire   */
    int        sa_flags;       /* indicateurs optionnels pour modifier
                                  le comportement du signal             */
};
Remarque Le champ sa_handler est le seul obligatoire ; c'est un pointeur sur la fonction qui servira de handler.

Exemple

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>

void handler(int sig) {
    printf("Bien reçu %d %s\n", sig, strsignal(sig));
    if (sig == SIGINT) {
        printf("Fin volontaire\n");
        exit(1);
    }
}

void main(void) {
    struct sigaction act;
    act.sa_handler = handler;
    sigaction(SIGINT,  &act, NULL); /* Ctrl-c  : signal 2  */
    sigaction(SIGQUIT, &act, NULL); /* Ctrl-\  : signal 3  */
    sigaction(SIGTSTP, &act, NULL); /* Ctrl-z  : signal 20 */
    /* SIGKILL est non déroutable */
    if (sigaction(SIGKILL, &act, NULL) == -1) perror("SIGKILL");
    for (;;);
}
Masque sa_mask Permet de définir des signaux supplémentaires à bloquer pendant que le handler s'exécute — utile pour éviter qu'un signal en interrompe le traitement d'un autre.

10. Sémaphores — outils de coordination

But Protéger l'accès à des variables partagées.

Exemple : gestion d'un stock

  1. Un processus p1 lit dans un registre la valeur courante V du stock et veut la décrémenter.
  2. Un autre processus p2 préempte le premier avant ; il lit la valeur courante (toujours V), la décrémente et met à jour le registre qui contient alors V-1.
  3. p1 reprend son exécution et met à jour le registre en écrivant V-1, au lieu de V-2.
Problème — incohérence des données.
Solution — utilisation des mécanismes de synchronisation entre les processus pour protéger les variables partagées, comme les sémaphores.

11. Pourquoi utiliser un sémaphore ?

Un sémaphore sert à la synchronisation entre les processus. Un sémaphore utilisé pour l'exclusion mutuelle (garantir qu'un seul processus accède à un moment donné à une variable partagée) utilisera 2 primitives :

Attendre l'accès
Si la ressource est déjà utilisée, le processus est endormi et mis dans une file d'attente.
Libérer l'accès
Si la file d'attente est non vide, le processus qui a dormi le plus longtemps est réveillé.
Section critique La section de programme située entre les deux primitives « attendre la ressource » et « libérer la ressource » s'appelle une section critique.

Remarque : aux sémaphores sont associés des droits d'accès de type Unix.

12. Le mécanisme des sémaphores

Un sémaphore est un objet composé :

  • une variable (la valeur du sémaphore) ;
  • une file d'attente (les processus bloqués attendant la ressource).

Primitives associées

Initialisation
Avec une valeur positive ou nulle.
P / Wait — Prendre
Demande d'autorisation.
Si valeur > 0 → accord, sinon blocage.
V / Signal — Vendre
Fin d'utilisation, restitution.
Éventuel déblocage d'un processus en file.

Algorithmes des primitives P et V

/* P(sémaphore) */
si (valeur[sémaphore] > 0) alors
    valeur[sémaphore] = valeur[sémaphore] - 1
sinon
    si (valeur[sémaphore] == 0)
        étatProcessus = Bloqué
        mettre processus en file d'attente
    fsi
finSi
invoquer l'ordonnanceur
/* V(sémaphore) */
valeur[sémaphore] = valeur[sémaphore] + 1
si (valeur[sémaphore] > 0) alors
    extraire processus de file d'attente
    étatProcessus = Prêt
finSi
invoquer l'ordonnanceur
Initialisation Initialisation(sémaphore, n) : valeur[sémaphore] = n.

13. Les différents types de sémaphores

Sémaphores à compte
  • Valeur initiale positive ou nulle.
  • Décrémentée à chaque fois que l'acquisition est accordée.
  • Incrémentée quand le sémaphore est relâché.
  • Quand la valeur devient nulle, la demande d'acquisition est bloquante.
  • Ressource globale.

14. Sémaphores POSIX.4

Les sémaphores POSIX.4 existent sous deux formes :

Sémaphores nommés
Mécanisme de gestion analogue à un système de fichiers — accessible par tous les processus (selon les droits).
Sémaphores anonymes
Basés sur la mémoire.

Sémaphores nommés — création

sem_t *sem_open(const char *sem_name,
                int oflags,
                mode_t creation_mode,
                unsigned int initial_val);
  • sem_name : nom du sémaphore. Il est recommandé de fournir un nom « compatible » avec un nom de fichier, commençant par un / (barre oblique) et pouvant contenir jusqu'à 255 caractères (ex. "/semaphore1").
  • oflags :
    • O_CREAT pour créer un sémaphore ;
    • O_CREAT | O_EXCL erreur si existe déjà.
  • initial_val : valeur initiale.
  • creation_mode : permissions (mode de création) du sémaphore.
Retour Adresse du nouveau sémaphore ; en cas d'échec : SEM_FAILED + errno.

Ouverture d'un sémaphore existant

sem_t *sem_open(const char *sem_name, int oflags);
  • Accède à un sémaphore déjà créé.
  • oflags :
    • O_CREAT : crée un sémaphore nommé s'il n'existe pas déjà ;
    • O_EXCL : si O_CREAT est spécifié et que le sémaphore existe déjà, l'appel échoue ;
    • O_RDWR : ouvre le sémaphore en mode lecture/écriture (habituellement non utilisé pour les sémaphores).

Fermeture et suppression

int sem_close(sem_t *sem_id);

Ferme le sémaphore. Retour : renvoie 0, en cas d'erreur -1 + errno.

int sem_unlink(const char *sem_name);

Supprime le sémaphore. Permet de supprimer un sémaphore nommé du système, précédemment créé avec sem_open.

Suppression différée sem_unlink ne supprime pas immédiatement le sémaphore si des processus l'utilisent encore. Le sémaphore sera effectivement supprimé lorsque toutes les références à celui-ci seront fermées.

Retour : renvoie 0, en cas d'erreur -1 + errno.

Sémaphores anonymes

Sont des sémaphores qui n'ont pas de nom associé dans le système de fichiers. Contrairement aux sémaphores nommés, ils sont basés sur la mémoire.

int sem_init(sem_t *sem_location,
             int    pshared,
             unsigned int initial_value);
  • sem_location : zone mémoire, éventuellement en zone partagée.
  • pshared : visibilité du sémaphore :
    • pshared == 0 → local au processus courant ;
    • pshared != 0 → partagé entre plusieurs processus.

Retour : renvoie 0, en cas d'erreur -1 + errno.

int sem_destroy(sem_t *sem_location);

Détruit le sémaphore. Retour : renvoie 0, en cas d'erreur -1 + errno.

Opérations P() et V()

/* P() — verrouille (décrémente) */
int sem_wait(sem_t *sem_id);

/* V() — déverrouille (incrémente) */
int sem_post(sem_t *sem_id);
sem_wait Décrémente (verrouille) le sémaphore pointé par sem_id.
• Si sem_id > 0 : décrémentation et retour immédiat.
• Si sem_id = 0 : le processus appelant est mis en attente jusqu'à ce qu'une autre entité incrémente la valeur (généralement avec sem_post), ce qui libère la ressource et réveille le processus.
Retour : 0, ou -1 + errno.
sem_post Incrémente le compteur et, s'il devient positif, réveille une tâche en attente.
Retour : 0, ou -1 + errno.

Tableau récapitulatif POSIX.4

PrimitiveFormeRôleRetour
sem_opennomméCrée / ouvre un sémaphore nomméadresse · SEM_FAILED
sem_closenomméFerme le sémaphore0 · -1
sem_unlinknomméSupprime (différé tant que des références existent)0 · -1
sem_initanonymeInitialise un sémaphore en mémoire0 · -1
sem_destroyanonymeDétruit le sémaphore0 · -1
sem_waitPVerrouille (bloque si valeur = 0)0 · -1
sem_postVDéverrouille / réveille un attendant0 · -1

15. Application — producteur / consommateur

Un classique : un buffer circulaire de taille N partagé entre un producteur (qui dépose) et un consommateur (qui retire). Trois sémaphores coordonnent l'accès :

empty = N
compte les cases vides.
full = 0
compte les cases pleines.
mutex = 1
garantit l'exclusion mutuelle sur le buffer.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

#define N 8

int       buffer[N];
int       in = 0, out = 0;
sem_t     empty, full, mutex;

void *producteur(void *arg) {
    int item = 0;
    for (;;) {
        item++;                       /* produire un item */
        sem_wait(&empty);             /* P(empty) — attend une case vide */
        sem_wait(&mutex);             /* P(mutex) — entrée section critique */
        buffer[in] = item;
        in = (in + 1) % N;
        sem_post(&mutex);             /* V(mutex) — sortie SC */
        sem_post(&full);              /* V(full)  — une case pleine de plus */
    }
    return NULL;
}

void *consommateur(void *arg) {
    int item;
    for (;;) {
        sem_wait(&full);              /* P(full)  — attend une case pleine */
        sem_wait(&mutex);             /* P(mutex) */
        item = buffer[out];
        out = (out + 1) % N;
        sem_post(&mutex);             /* V(mutex) */
        sem_post(&empty);             /* V(empty) — libère une case vide */
        printf("consommé %d\n", item);
    }
    return NULL;
}

int main(void) {
    pthread_t tp, tc;
    sem_init(&empty, 0, N);           /* N cases vides au départ */
    sem_init(&full,  0, 0);           /* 0 case pleine          */
    sem_init(&mutex, 0, 1);           /* mutex binaire          */

    pthread_create(&tp, NULL, producteur,   NULL);
    pthread_create(&tc, NULL, consommateur, NULL);
    pthread_join(tp, NULL);
    pthread_join(tc, NULL);

    sem_destroy(&empty);
    sem_destroy(&full);
    sem_destroy(&mutex);
    return 0;
}
Ordre des P() — règle d'or Toujours faire P(empty) ou P(full) avant P(mutex).
Inverser l'ordre conduit à un interblocage : un producteur tient le mutex et attend une case vide qu'un consommateur ne peut libérer (le consommateur attend lui-même le mutex).

16. Réviser

❓ Q/R

Qu'est-ce qu'un signal ?

Une interruption logicielle asynchrone d'un processus, envoyée par un autre processus ou par le noyau, identifiée par un numéro entier et un nom symbolique dans signal.h.

Quels signaux ne peuvent pas être déroutés ni ignorés ?

SIGKILL (9) et SIGSTOP (19). Toute tentative de redéfinir leur comportement échoue (signal() renvoie SIG_ERR, sigaction() renvoie -1).

Quelle est la différence entre SIG_IGN et SIG_DFL ?

SIG_IGN = ignorer le signal. SIG_DFL = rétablir l'action par défaut du signal (souvent terminaison).

Quelles sont les 5 actions par défaut possibles ?

exit (terminer), core (dump mémoire + terminer), stop (suspendre), ignore (ne rien faire), continue (reprendre).

Que renvoie kill() et quelles sont les conditions pour qu'il réussisse ?

Renvoie 0 en cas de succès, -1 sinon. L'émetteur et le destinataire doivent être du même propriétaire, ou bien l'émetteur doit être root.

Que se passe-t-il si pid < -1 dans kill(pid, sig) ?

Le signal est envoyé à tous les processus du groupe dont le gid est égal à la valeur absolue de pid.

À quoi sert le champ sa_mask de struct sigaction ?

À définir des signaux supplémentaires qui doivent être bloqués pendant l'exécution du gestionnaire, pour éviter qu'ils interrompent le handler.

Quelle différence entre sémaphore nommé et anonyme ?

Un sémaphore nommé a un nom dans le système de fichiers ("/sem1") et est accessible par n'importe quel processus selon les droits Unix (sem_open, sem_close, sem_unlink). Un sémaphore anonyme est en mémoire (sem_init, sem_destroy) et la portée dépend du paramètre pshared.

Qu'est-ce qu'une section critique ?

La portion de programme située entre sem_wait et sem_post — c'est-à-dire entre les primitives « attendre » et « libérer » la ressource. Un seul processus à la fois peut y être présent quand on l'utilise pour l'exclusion mutuelle.

Que fait sem_wait quand la valeur du sémaphore vaut 0 ?

Le processus appelant est mis en attente jusqu'à ce qu'une autre entité fasse sem_post, ce qui libère la ressource et réveille le processus en attente.

🃏 Flashcards

Signal · définition

En une phrase ?

tourne →
Interruption logicielle asynchrone d'un processus, identifiée par un n° + nom dans signal.h.

SIGINT

N° + origine ?

tourne →
2 — frappe d'interruption CTRL-C.

SIGKILL / SIGSTOP

Particularité ?

tourne →
9 et 19 — leur réaction ne peut être redéfinie.

SIGSEGV / SIGFPE

N° + action par défaut ?

tourne →
11 (violation mémoire) et 8 (erreur arithmétique) — action core (dump + termine).

signal()

Prototype ?

tourne →
void (*signal(int signum,
  void (*handler)(int)))(int);

SIG_IGN vs SIG_DFL

Différence ?

tourne →
SIG_IGN : ignorer le signal · SIG_DFL : rétablir l'action par défaut.

struct sigaction

3 champs ?

tourne →
sa_handler (obligatoire), sa_mask (signaux bloqués), sa_flags (indicateurs).

kill(pid, sig)

Cas pid = 0 ?

tourne →
Envoie sig à tous les processus du même groupe que l'appelant.

Sémaphore — composition

De quoi est-il composé ?

tourne →
Une variable (la valeur) + une file d'attente des processus bloqués.

P() et V()

Autre nom + rôle ?

tourne →
P = Wait (prendre, demande d'autorisation) · V = Signal (vendre, fin d'utilisation).

sem_open · 4 args

Lesquels ?

tourne →
sem_open(name, oflags,
  mode, initial_val)
→ sem_t* ou SEM_FAILED

sem_init · pshared

0 vs ≠0 ?

tourne →
0 : local au processus courant. ≠0 : partagé entre plusieurs processus.

sem_unlink

Effet si encore utilisé ?

tourne →
Suppression différée : le sémaphore est effectivement supprimé quand toutes les références sont fermées.

Producteur / consommateur

Combien de sémaphores et lesquels ?

tourne →
3 : empty=N, full=0, mutex=1.

✎ Quiz éclair

test rapide · 7 questions
0 / 7
1.Un signal est :
  • Un tube de communication
  • Une variable partagée
  • Une interruption logicielle asynchrone
  • Une primitive de lecture/écriture
Asynchrone : peut survenir à tout moment dans l'exécution du processus.
2.Quel signal ne peut pas être redéfini ?
  • SIGINT
  • SIGKILL
  • SIGUSR1
  • SIGTSTP
SIGKILL (9) et SIGSTOP (19) ne peuvent ni être ignorés ni être interceptés.
3.signal() retourne en cas d'échec :
  • NULL
  • -1
  • 0
  • SIG_ERR
SIG_ERR est la valeur conventionnelle d'échec pour signal() (sigaction renvoie -1).
4.Que fait kill(0, SIGUSR1) ?
  • Envoie SIGUSR1 à tous les processus du même groupe que l'appelant
  • Tue le processus 0 (init)
  • Ignore le signal
  • Renvoie immédiatement -1
Avec pid = 0, le signal est diffusé à tout le groupe de l'appelant.
5.Le champ obligatoire de struct sigaction est :
  • sa_mask
  • sa_flags
  • sa_handler
  • sa_restorer
C'est le pointeur sur la fonction de traitement (handler).
6.sem_wait sur un sémaphore à 0 :
  • Retourne immédiatement -1
  • Met le processus en attente jusqu'à un sem_post
  • Décrémente quand même
  • Détruit le sémaphore
Quand la valeur est 0, l'appelant est endormi jusqu'à libération de la ressource.
7.Dans le producteur / consommateur, dans quel ordre les P() doivent-elles être faites côté producteur ?
  • P(mutex) puis P(empty)
  • P(full) puis P(mutex)
  • P(mutex) puis P(full)
  • P(empty) puis P(mutex)
L'ordre inverse provoque un interblocage : tenir le mutex en attendant une case vide bloque le consommateur qui doit libérer une case.

📌 À retenir

  • Signal = interruption logicielle asynchrone · identifié par n° + nom dans signal.h
  • Cycle : interrompt → exécute handler → reprend où il en était
  • 3 réponses : action par défaut · ignorer · intercepter (handler)
  • 5 actions par défaut : exit, core, stop, ignore, continue
  • Non déroutables : SIGKILL (9), SIGSTOP (19)
  • kill(pid, sig)0/-1 · pid>0 proc · =0 groupe · <-1 |pid|
  • signal(sig, handler) renvoie SIG_ERR en cas d'échec ; SIG_IGN / SIG_DFL
  • sigaction : sa_handler (obligatoire), sa_mask, sa_flags
  • Sémaphore = variable + file d'attente · primitives P (Wait) et V (Signal)
  • Section critique = code entre P() et V()
  • POSIX.4 : nommés (sem_open, sem_close, sem_unlink) · anonymes (sem_init, sem_destroy)
  • sem_wait bloque si valeur = 0 · sem_post réveille un attendant
  • Nom de sémaphore : commence par /, jusqu'à 255 car. (ex. "/sem1")
  • sem_init(sem, pshared, init) · pshared=0 local · ≠0 partagé
  • Producteur/consommateur : empty=N, full=0, mutex=1 · P(empty/full) avant P(mutex)