Le carnet On est
Cours 1 · encre bleue

Introduction · appels système

Le système d'exploitation comme machine étendue. Mode noyau, mode utilisateur, et la grande famille POSIX : open, read, write, lseek, close, dup, dup2, fork, wait, waitpid, exec*, system.

~ 45 min de lecture TP — fichiers & processus en C 14 flashcards · 8 quiz

📄 Ressources du cours

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

🎥 Vidéos recommandées

  • 🇫🇷 fonction fork programmation c linux en darija

    programmer0MAROC · 12:29 · 43k vues — link for download exemple.txt http://www.mediafire.com/?iu6giktc7dgb2uo

  • 🇫🇷 GNU/Linux #22 - processus

    FormationVidéo · 48:17 · 34k vues — Cette vidéo aborde les processus du système, de quoi sont ils composés et comment le processeur (via son ordonnanceur) partage les ressources entre chacun d'...

  • 🇬🇧 Linux fork() Introduction

    Brian Fraser · 29:40 · 248k vues — Demo the use of the fork() function in Linux/Unix to spawn a new process. Cover the basics of what it does, how to do work in parent and child processes, and...

0. Plan du cours

Sommaire Préambule · Systèmes d'exploitation (définitions et fonctions de base) · Appels système : gestion des fichiers, gestion des processus, signaux, threads, communications inter-machines.
Objectifs du cours
• Décrire le rôle et le fonctionnement d'un système d'exploitation monoprocesseur et multi-tâches.
• Apprendre à manipuler certains concepts de base.
Ces concepts seront essentiellement illustrés au travers du système UNIX / Linux qui est très homogène, très riche, très souple.

1. Systèmes d'exploitation · définitions

Définition Le système d'exploitation (SE, en anglais Operating System ou OS) est un ensemble de programmes responsables de la liaison entre les ressources matérielles d'un ordinateur et les applications de l'utilisateur (traitement de texte, jeu vidéo, …). Il fournit aux programmes applicatifs des points d'entrée génériques pour les périphériques.

Architecture en couches d'un ordinateur

Un ordinateur est constitué :

  • du matériel — dispositifs physiques + langage machine ;
  • d'un système d'exploitation ;
  • de programmes — programmes système + programmes d'applications.
Utilisateur 1 · Utilisateur 2 · … · Utilisateur N Compilateur · Éditeur · Base de données Applications & programmes système Système d'exploitation noyau (kernel) + outils système · appels système Matériel processeur · mémoire · disques · imprimantes · réseau

Les quatre couches d'un système : du matériel jusqu'aux utilisateurs, en passant par le SE et les applications.

Composition du SE

Noyau (kernel)
Partie la plus critique d'un OS. Il permet aux éléments matériel et logiciel de communiquer entre eux, de fonctionner ensemble et de former un tout. C'est le premier logiciel chargé en mémoire.
Outils système
Partie permettant à l'utilisateur de tirer profit de l'OS, de gérer les périphériques, les configurer. Bref, ils fournissent une interface d'accès au système.
Exemple de tâche « LIRE UN BLOC DU FICHIER » — c'est typiquement un service rendu par le SE.

Le matériel (hardware)

Les dispositifs physiques constituent la couche la plus basse :

  • le processeur,
  • la mémoire principale,
  • des disques,
  • des imprimantes,
  • des interfaces de connexion réseau,

Les programmes (software, applications)

Ils sont écrits par les utilisateurs ou par les éditeurs de logiciels. But : résoudre des problèmes spécifiques (traitement des problèmes commerciaux, calculs scientifiques, etc.).

2. La double fonction du SE

Un SE répond à deux besoins qui ne sont pas forcément liés :

  • le SE en tant que machine étendue (ou « machine virtuelle »),
  • le SE en tant que gestionnaire de ressources.
Exemples de SE Linux, Mac OS, Windows 9X, Me, 2000, XP, MS-DOS, MINIX, etc.

Le SE en tant que machine étendue

Interface entre applications et matériel De ce point de vue, le SE peut être assimilé à une machine étendue ou virtuelle plus facile à programmer ou à utiliser que le matériel.
Côté programmeur
Un programmeur va utiliser le SE par l'intermédiaire des « appels système ».
Côté utilisateur
Un utilisateur peut lui aussi — dans une certaine mesure — manipuler un SE, sans pour autant avoir à créer un programme : via les commandes shell.

Le SE en tant que gestionnaire de ressources

Les différents composants d'un ordinateur doivent coopérer et partager des ressources. Le travail du SE consiste à :

  • ordonnancer,
  • contrôler l'allocation des ressources : processeurs, mémoires, périphériques d'E/S, entre les différents programmes qui y font appel.
Exemple — gestion des impressions Deux éditions (Fic1, Fic2) sont envoyées au SPOOL, qui les sérialise puis les transmet l'une après l'autre à l'imprimante.

Pour chacune des ressources, le SE doit :

  • connaître à tout moment l'utilisateur de la ressource,
  • en accorder l'usage de manière équitable,
  • éviter les conflits d'accès entre les différents programmes ou utilisateurs.
Les deux tâches essentielles
① Le partage des ressources.
② La protection de l'accès aux ressources.

3. Fonctions de base d'un SE

Un SE, à quoi ça sert ? Six grandes fonctions :

#FonctionRôle
1Gestion des processusCorrespond à l'exécution des programmes.
2Gestion de la mémoireGérer les transferts entre la mémoire principale et secondaire.
3Système de fichiersOffre à l'utilisateur une vision homogène et structurée des données et des ressources (disques, périphériques).
4Entrées-sortiesMécanismes qu'utilisent les processus pour communiquer avec l'extérieur.
5Réseaux d'ordinateursProtocoles de communication, d'interconnexion et d'application.
6Systèmes répartisProtocoles d'appels de procédures à distance (RPC) ou objets distribués.

4. Les appels système · principe

Qu'est-ce qu'un appel système ? Pour accéder aux services du SE, un programme utilisateur doit effectuer un appel système qui consiste à :
Basculer en mode noyau
Invoquer le SE
Revenir en mode utilisateur
Retourner le contrôle au programme utilisateur.

Exemple : lecture ou écriture sur le disque dur.

Un exemple détaillé avec le compilateur C

Soit l'appel cpt = read(df, tampon, nboctets). Voici les 10 étapes du déroulement :

// Côté utilisateur (mode user)
cpt = read(df, tampon, nboctets);
  1. Empiler nboctets
  2. Empiler &tampon
  3. Empiler df
  4. Appeler read()
  5. Placement du code du read dans un registre
  6. Déroutement vers le noyau — basculer en mode noyau
  7. Le code noyau examine le numéro d'appel système, utilise une table de pointeurs indexée sur les numéros d'appels système
  8. Exécution de l'appel système
  9. Rendre le contrôle à la fonction read de bibliothèque
  10. Rendre le contrôle au programme utilisateur
MODE UTILISATEUR programme libc : read() — frontière user / kernel · déroutement (trap) — MODE NOYAU table des appels système exécution du service ①→④→⑥ ⑨→⑩ Aller-retour user → kernel → user pour chaque appel système.

Anatomie d'un appel système : on quitte le mode utilisateur, on transite par la table indexée du noyau, on exécute, et on revient.

5. Les appels système POSIX · panorama

À quoi ça sert ? Trois grandes familles :

FamilleAppels système
Gestion des fichiersopen, close, read, write, dup, dup2, stat, …
Gestion des processusfork, pipe, exec, execve, execlp, exit, …
Communications inter-machinessocket(), bind(), listen(), connect(), accept(), send(), recv(), write(), read(), sendto(), recvfrom(), close(), gethostbyname(), gethostbyaddr(), …

6. Gestion des fichiers

Ouvrir un fichier · open

Ouvre un fichier, qui restera ouvert jusqu'à la fin du processus ou jusqu'à close :

int open(const char *pathname, int typeOpen);
int open(const char *pathname, int typeOpen, mode_t droitsFic);
  • pathname (chemin) = chaîne de caractères correspondant au chemin d'accès du fichier à ouvrir.
  • typeOpen = mode d'ouverture.
ModeSignification
O_RDONLYlecture seule
O_WRONLYécriture seule
O_RDWRlecture et écriture

Ces modes peuvent se combiner avec un OU binaire avec un attribut de création :

AttributEffet
O_CREATcréer si nécessaire le fichier.
O_APPENDouvrir en mode ajout.
O_TRUNCsi le fichier existe, est un fichier régulier, et est ouvert en écriture (O_RDWR ou O_WRONLY), il sera tronqué à une longueur nulle (vider avant de pouvoir y écrire de nouvelles données). Si le fichier n'existe pas, il ne sera pas créé sauf si O_CREAT est spécifié aussi.
O_EXCLs'assurer que cet appel crée le fichier : si cet attribut est spécifié en conjonction avec O_CREAT et si le fichier existe déjà, open() échouera.
droitsFic
Droits d'accès à utiliser si le fichier doit être créé grâce à O_CREAT.
• Mêmes valeurs octales que la commande chmod (0777 = tous les droits pour tous).
• Se combinent avec le umask.
• Si on oublie, open donnera des résultats bizarres.
Valeur de retour de open open retourne -1 si le fichier n'a pas pu être ouvert, et un entier positif ou nul sinon. Ce nombre correspond à un index dans le tableau des fichiers ouverts, où chaque case contient le descripteur d'un fichier ouvert.

Les étapes d'un open

  1. Trouver l'inode du fichier (dans la table des inodes) — charger l'inode en mémoire si elle n'y est pas.
  2. Vérifier les droits d'accès — la vérification n'est faite qu'à l'ouverture ; si on enlève les droits sur un fichier ouvert, ça ne bloque pas le processus qui utilise le fichier !
  3. Allouer une entrée dans la table des fichiers ouverts.
  4. Positionner l'offset courant (0 ou fin de fichier en cas d'ouverture en ajout).
  5. Allouer une place dans la table des descripteurs de fichiers du processus.
  6. Renvoyer au processus appelant le descripteur obtenu.

Les 3 tables liées à open

Table des descripteurs (par processus) 0 stdin 1 stdout 2 stderr 3 → Table des fichiers ouverts (globale au noyau) offset, mode offset, mode Table des inodes inode #42 inode #99 Le descripteur (fd) pointe vers une entrée de la table globale, qui pointe vers l'inode.

Trois niveaux d'indirection : fd processusentrée globale (offset, mode)inode.

Fermer un fichier ouvert · close

Ferme le fichier ouvert et libère le descripteur :

int close(int fd);
Si fd était le dernier descripteur sur le fichier (à l'intérieur du processus) • le compteur associé à l'inode en mémoire est décrémenté ; s'il vaut 0, l'inode est déchargée.
• les ressources associées à l'ouverture du fichier sont libérées.

Lire depuis un fichier ouvert · read

int read(int fd, void *buf, size_t count);
Peut retourner moins que demandé en cas de :
fin de fichier
lecture sur un tube ou un terminal
interruption par un signal
Contrôles effectués
• Vérification de la validité du descripteur et du bon mode d'ouverture.
Les droits d'accès de l'utilisateur ne sont PAS testés.

Écrire dans un fichier ouvert · write

ssize_t write(int fd, const void *buf, size_t count);
Peut écrire moins que demandé en cas de :
• plus de place sur le périphérique,
• quota disque du processus dépassé,
• interruption par un signal.
Atomicité Dans un fichier, la modification de la position et l'écriture sont faites de façon atomique.

Se positionner dans un fichier ouvert · lseek

off_t lseek(int fd, off_t offset, int whence);
whenceOrigine du déplacement
SEEK_SETdébut du fichier
SEEK_CURposition courante
SEEK_ENDfin du fichier
Particularité lseek peut aller au-delà de la taille du fichier (sans la modifier) ; une écriture modifiera alors la taille réelle.

Dupliquer un descripteur · dup

int dup(int oldfd);
  • Duplique un descripteur de fichier valide.
  • La copie reçoit le plus petit numéro de descripteur disponible.
  • Pénible quand on veut un numéro précis.

Dupliquer avec numéro choisi · dup2

int dup2(int oldfd, int newfd);
  • Duplique un descripteur de fichier valide.
  • La copie reçoit le numéro newfd.
  • Si newfd était ouvert, il est fermé automatiquement.
  • Très utile pour les redirections.

dup, dup2 et lseek

Partage de position Le descripteur et sa copie partagent la position modifiable par lseek.
Risque de concurrence.
→ Après une duplication, il faut fermer deux fois le descripteur pour fermer complètement.

Descripteurs standards

Par défaut, un processus dispose de :

NomRôlefd
stdinentrée standard0
stdoutsortie standard1
stderrsortie d'erreur2

7. Gestion des processus

Création d'un processus

  • Lorsqu'on entre une commande, le shell lance un processus pour l'exécuter.
  • Le shell attend la fin du processus, puis attend la commande suivante.
  • Un programme qui s'exécute avec un jeu de données particulier est un processus.

Arborescence de processus

Définitions essentielles • Chaque processus a un père, celui qui l'a lancé.
• Un processus a un numéro permettant son identification : PID.
• Le numéro du processus du père est le PPID.
• Un processus père peut avoir plusieurs processus fils.
• Sous UNIX, un premier processus init (ou systemd) est créé avec un PID égal à 1. C'est l'ancêtre de tous les processus.

Duplication d'un processus · fork

pid_t fork(void);

L'appel système fork est le seul moyen de créer un processus à bas niveau :

  • Crée un processus fils de ce processus, et le lance.
  • L'exécution continue dans les deux processus, à l'instruction qui suit le fork.
  • Les PID et PPID sont les seules informations différentes entre les deux processus.
Valeur retournée par fork() • Dans le processus père : retourne le PID du fils.
• Dans le processus fils : retourne 0.
• Si le fork échoue : retourne -1 (seul le père existe alors).

La manière la plus courante d'utiliser fork

L'appel système fork duplique / clone le processus appelant :

void main() {
    pid_t n = fork();
    if (n != 0) {
        if (n < 0) {
            // gestion des erreurs
        } else {
            // programme du papa
        }
    } else {
        // programme du fils
    }
}

Récupération du PID

pid_t getpid(void);
pid_t getppid(void);
pid_t getpgrp(void);
  • getpid retourne le PID du processus courant.
  • getppid retourne le PID du processus père.
  • getpgrp retourne le PID du groupe du processus courant.

Exemple — clonage, indépendance

Le clonage est une recopie complète et indépendante. Exemple de sortie :

je suis le fils, mon PID est 10271 ; mon pere est 10270
pour le pid 10271, i = 12
je suis le pere, mon PID est 10270
Pour le pid 10270, i = 7
Père (PID p) avant fork() fork() clonage Père · n = PID(fils) n > 0 → branche papa Fils · n = 0 n == 0 → branche fils en cas d'erreur : n = -1 (seul le père existe)

Le branchement classique post-fork : n > 0 côté père, n == 0 côté fils, n < 0 en cas d'erreur.

Attendre la mort d'un fils · wait

pid_t wait(int *status);

La primitive wait permet à un processus d'attendre la fin d'un de ses fils :

  • S'il n'y a pas de fils ou erreur, retourne -1.
  • Sinon, retourne le PID du fils qui s'est terminé.
Comportementwait suspend l'exécution du processus courant jusqu'à ce que l'un de ses fils se termine (ou jusqu'à la réception d'un signal).
• Si un processus fils est déjà mort, la fonction revient immédiatement.
• Toutes les ressources utilisées par le fils sont libérées.

Inspecter status

Si status n'est pas NULL, les informations sur la terminaison du fils y sont stockées.

MacroSignification
WIFEXITED(status)vrai si le fils s'est terminé normalement (exit) ; sinon 0.
WEXITSTATUS(status)code de retour du fils (si normal).
WIFSIGNALED(status)vrai si le fils a été tué par un signal.
WTERMSIG(status)numéro du signal qui a tué le fils.

Terminer un processus · exit

void exit(int status);
  • status permet d'indiquer au processus père qu'une erreur s'est produite.
  • status = 0 si pas d'erreur.

Attendre un fils particulier · waitpid

pid_t waitpid(pid_t pid, int *status, int options);

La fonction waitpid suspend l'exécution du processus courant jusqu'à ce que le processus fils de numéro pid se termine (ou jusqu'à réception d'un signal).

Valeur de pidComportement
< -1attendre la fin de n'importe quel processus fils appartenant au groupe d'ID |pid| (n'importe quel fils dont GID = |pid|).
-1attendre n'importe quel fils — identique à wait.
0attendre la fin de n'importe quel fils du même groupe que l'appelant.
> 0attendre la fin du processus de numéro pid.
Option utile
Il existe différentes options ; la plus utile : WNOHANG qui permet de ne pas bloquer si aucun fils n'est mort.

Temporisation · sleep

int sleep(int seconds);

Similaire à la commande shell sleep — le processus qui appelle sleep est bloqué pendant le nombre de secondes spécifiées.

Différence avec wait() wait bloque le processus jusqu'à la fin d'un fils, alors que sleep() bloque pour un temps spécifié.

8. Lancer un autre programme · exec* & system

La famille exec*

Principe Le système UNIX offre une famille d'appels système exec* qui permettent à un processus de remplacer son code exécutable par un autre programme spécifié par path ou file, sans changer de numéro de processus (PID).
Valeur de retour En cas de succès : AUCUNE — le code ayant fait l'appel exec* n'existe plus.
En cas d'échec : -1.

Les 6 prototypes

int execl (char *path, char *arg0, char *arg1, ..., NULL);
int execv (char *path, char *arg[]);
int execle(char *path, char *arg0, ..., NULL, char *envp[]);
int execve(char *path, char *arg[], char *envp[]);
int execlp(char *file, char *arg0, char *arg1, ..., NULL);
int execvp(char *file, char *arg[]);

execl — liste d'arguments

La fonction execl prend en paramètre une liste des arguments à passer au programme (liste terminée par NULL). Le premier paramètre est une chaîne qui doit contenir le chemin d'accès complet au fichier exécutable ou au script shell à exécuter.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    /* dernier élément NULL, OBLIGATOIRE */
    execl("/usr/bin/emacs", "emacs", "fichier.c", "fichier.h", NULL);
    perror("Problème : cette partie du code ne doit jamais être exécutée");
    return 0;
}

execv — tableau d'arguments

La différence avec execl est que l'on n'a pas besoin de connaître la liste des arguments à l'avance (ni même leur nombre). Prototype :

int execv(const char *application, const char *argv[]);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    char *argv[] = {"emacs", "fichier.c", "fichier.h", NULL};
    /* dernier élément NULL, obligatoire */
    execv("/usr/bin/emacs", argv);
    puts("Problème : cette partie du code ne doit jamais être exécutée");
    return 0;
}

Récapitulatif des 6 variantes

PrimitiveFormat d'argumentPassage d'environnementRecherche avec PATH
execlListeAutomatiqueNon
execvTableauAutomatiqueNon
execleListeManuelNon
execveTableauManuelNon
execlpListeAutomatiqueOui
execvpTableauAutomatiqueOui
Mnémoniques
l : liste d'arguments
v : arguments sous forme de vecteur
p : recherche du fichier avec la variable d'environnement PATH
e : transmission d'un environnement en dernier paramètre, en remplacement de l'environnement courant

Lancement d'une commande · system

int system(const char *commande);
  • La fonction system de la bibliothèque stdlib.h permet directement de lancer un programme dans un programme C sans utiliser fork et exec.
  • Crée un nouveau processus /bin/sh qui exécute la commande (/bin/sh -c commande).
  • Le processus appelant cette fonction reste bloqué jusqu'à la fin de l'exécution du processus.
system("clear");

Réviser le cours

❓ Q/R

Que sont les deux fonctions principales d'un système d'exploitation ?

① Le SE en tant que machine étendue (ou « machine virtuelle ») : interface entre les applications et le matériel, plus facile à programmer/utiliser que le matériel. ② Le SE en tant que gestionnaire de ressources : ordonnance et contrôle l'allocation des ressources (processeurs, mémoires, périphériques d'E/S). Les deux tâches essentielles ici sont le partage et la protection.

Qu'est-ce qu'un appel système et quelles sont ses 4 étapes ?

C'est le mécanisme par lequel un programme utilisateur accède aux services du SE. Les 4 étapes : ① basculer en mode noyau, ② invoquer le SE, ③ revenir en mode utilisateur, ④ retourner le contrôle au programme utilisateur. Le noyau utilise une table de pointeurs indexée sur les numéros d'appels système pour dispatcher.

Que retourne open ?

open retourne -1 en cas d'échec, et un entier positif ou nul sinon, correspondant à un index dans le tableau des fichiers ouverts (le descripteur).

Pourquoi read peut-il retourner moins d'octets que demandé ?

Trois cas : ① fin de fichier, ② lecture sur un tube ou un terminal, ③ interruption par un signal. read vérifie la validité du descripteur et le mode d'ouverture, mais ne teste pas les droits d'accès de l'utilisateur (cela est fait à l'open).

Différence entre dup et dup2 ?

dup(oldfd) duplique oldfd et renvoie le plus petit numéro de descripteur disponible. dup2(oldfd, newfd) oblige la copie à recevoir le numéro newfd (si newfd était ouvert, il est fermé automatiquement) — très utile pour les redirections. Dans les deux cas, l'original et la copie partagent la position modifiée par lseek.

Que retourne fork() ?

Dans le père : le PID du fils (entier positif).
Dans le fils : 0.
En cas d'échec : -1 (seul le père existe alors). Les deux processus continuent à l'instruction qui suit le fork ; seules les valeurs de PID et PPID diffèrent.

Quelle est la différence entre wait et waitpid ?

wait attend la fin de n'importe lequel des fils. waitpid(pid, status, options) permet de cibler un fils particulier (pid > 0), un groupe (pid < -1), n'importe quel fils du même groupe (pid == 0) ou n'importe quel fils (pid == -1, équivalent à wait). Option utile : WNOHANG pour ne pas bloquer.

Que retourne un appel exec* en cas de succès ?

RIEN. Le code ayant fait l'appel exec* n'existe plus — il a été remplacé par le nouveau programme. En cas d'échec, retourne -1. Le PID, lui, est conservé.

Que signifient les lettres l, v, p, e dans les variantes d'exec ?

l : arguments en liste (terminée par NULL). v : arguments en vecteur (tableau). p : recherche du fichier via la variable d'environnement PATH. e : environnement passé manuellement en dernier paramètre.

À quoi sert system() ? Différence avec fork + exec ?

system(commande) de stdlib.h lance directement un programme dans un programme C sans avoir à écrire le couple fork/exec*. Elle crée un nouveau processus /bin/sh qui exécute /bin/sh -c commande. Particularité : le processus appelant reste bloqué jusqu'à la fin de l'exécution.

🃏 Flashcards

Clique pour retourner.

Machine étendue

Quel est le rôle du SE vu comme machine étendue ?

tourne →
Interface entre les applications et le matériel, plus facile à programmer que le matériel. Le programmeur l'utilise via les appels système, l'utilisateur via les commandes shell.

4 étapes d'un appel système

Ordre exact.

tourne →
① Basculer en mode noyau
Invoquer le SE
③ Revenir en mode utilisateur
Retourner le contrôle au programme

Prototype de open

Les deux signatures.

tourne →
int open(const char *pathname,
         int typeOpen);
int open(const char *pathname,
         int typeOpen,
         mode_t droitsFic);

Valeur de retour de open

Échec et succès.

tourne →
-1 si échec, sinon un entier ≥ 0 = index dans le tableau des fichiers ouverts (le descripteur).

Les 3 tables d'open

Lesquelles ?

tourne →
① Table des descripteurs (par processus)
② Table des fichiers ouverts (globale)
③ Table des inodes

read — retour partiel

3 cas.

tourne →
Fin de fichier
② Lecture sur tube ou terminal
Interruption par un signal

lseek · whence

3 valeurs possibles.

tourne →
SEEK_SET (début) · SEEK_CUR (courant) · SEEK_END (fin). Peut aller au-delà de la taille du fichier.

dup vs dup2

Différence principale.

tourne →
dup → plus petit fd disponible.
dup2(old, new) → fd imposé = new (ferme new s'il était ouvert). Utile pour les redirections.

stdin / stdout / stderr

Quels fd ?

tourne →
stdin = 0 · stdout = 1 · stderr = 2

Valeurs de retour de fork

3 cas.

tourne →
Père : PID du fils (> 0)
Fils : 0
Erreur : -1 (seul le père existe)

PID 1

Quel est-ce ?

tourne →
Le processus init (ou systemd) — ancêtre de tous les processus.

Macros de wait

4 macros sur status.

tourne →
WIFEXITED · terminé normalement
WEXITSTATUS · code de retour
WIFSIGNALED · tué par signal
WTERMSIG · n° du signal

waitpid · valeurs de pid

4 cas.

tourne →
< -1 : fils du groupe |pid|
-1 : n'importe quel fils (= wait)
0 : fils du même groupe
> 0 : le fils de PID pid

Famille exec* — lettres

Que veulent dire l v p e ?

tourne →
l = liste · v = vecteur
p = PATH · e = environnement manuel

Retour d'exec*

Succès et échec.

tourne →
Succès : AUCUN retour (le code appelant n'existe plus).
Échec : -1. Le PID est conservé.

✎ Quiz éclair

test rapide · 8 questions
0 / 8
1.Le SE vu comme « machine étendue » signifie qu'il :
  • Ajoute de la mémoire physique
  • Émule un autre OS
  • Sert d'interface plus facile à utiliser que le matériel
  • Étend la durée de vie des processus
Le SE est assimilé à une machine étendue/virtuelle plus facile à programmer ou utiliser que le matériel.
2.Que retourne open en cas d'échec ?
  • -1
  • 0
  • NULL
  • errno
open retourne -1 en cas d'échec, sinon un entier positif ou nul (le descripteur).
3.Pour ouvrir un fichier de manière garantie créative (échec si déjà existant), on combine :
  • O_RDONLY | O_APPEND
  • O_TRUNC seul
  • O_CREAT seul
  • O_CREAT | O_EXCL
O_EXCL avec O_CREAT garantit que l'appel crée le fichier ; si le fichier existe déjà, open échoue.
4.À propos de dup et dup2 :
  • Les deux retournent toujours le même numéro
  • Original et copie partagent la position modifiable par lseek
  • dup2 ne ferme jamais newfd
  • Une seule fermeture suffit après duplication
Le descripteur et sa copie partagent la position. Il faut donc fermer deux fois pour libérer complètement.
5.Dans le processus fils, fork() retourne :
  • Le PID du père
  • Le PID du fils
  • 0
  • -1
Dans le fils : 0. Dans le père : PID du fils. En cas d'échec : -1.
6.Pour ne pas bloquer si aucun fils n'est mort avec waitpid, on utilise :
  • pid = 0
  • pid = -1
  • WIFEXITED
  • WNOHANG
WNOHANG est l'option qui rend waitpid non-bloquant.
7.En cas de succès, execl retourne :
  • Le PID du programme lancé
  • Rien — le code appelant n'existe plus
  • 0
  • Le code de retour du programme lancé
exec* remplace le code du processus. En cas de succès, il n'y a aucun retour. En cas d'échec : -1.
8.Parmi les variantes exec*, lesquelles cherchent dans la variable PATH ?
  • execlp et execvp
  • execl et execv
  • execle et execve
  • Toutes
La lettre p indique la recherche via la variable d'environnement PATH : execlp et execvp.

📌 À retenir

  • SE = liaison matériel ↔ applications, points d'entrée génériques pour les périphériques
  • Double fonction : machine étendue + gestionnaire de ressources
  • Composition : noyau (kernel, 1er chargé en mémoire) + outils système
  • 6 fonctions de base : processus · mémoire · système de fichiers · E/S · réseaux · systèmes répartis
  • Appel système : 4 étapes → basculer noyau → invoquer SE → revenir user → rendre la main
  • POSIX : fichiers (open/close/read/write/dup/dup2/stat), processus (fork/pipe/exec*/exit), réseaux (socket/bind/…)
  • open : retourne -1 si échec, sinon un fd ≥ 0
  • open manipule 3 tables : descripteurs (par proc) → fichiers ouverts (global) → inodes
  • read/write peuvent renvoyer moins que demandé (EOF, tube/terminal, signal)
  • lseek(fd, offset, whence) avec SEEK_SET/SEEK_CUR/SEEK_END — peut dépasser la taille
  • dup = plus petit fd libre ; dup2 = fd imposé (ferme l'ancien) → redirections
  • fork : père reçoit PID(fils), fils reçoit 0, erreur = -1 ; seuls PID/PPID diffèrent
  • wait bloque jusqu'à mort d'un fils ; waitpid + WNOHANG = non-bloquant
  • exec* : remplace le code, PID conservé ; succès = aucun retour, échec = -1 ; l/v/p/e = liste/vecteur/PATH/env
  • system("cmd") = fork + exec de /bin/sh -c cmd, bloque jusqu'à la fin