0% ont trouvé ce document utile (0 vote)
16 vues105 pages

Introduction à la Programmation Système

Le document présente un cours sur la programmation système, principalement sous Linux et Unix, en abordant des concepts tels que les fichiers, les processus, la communication, les threads et la synchronisation. Il couvre également l'historique des systèmes d'exploitation, en se concentrant sur l'évolution d'Unix et de Windows, ainsi que sur les distinctions entre l'espace noyau et l'espace utilisateur. L'objectif est d'explorer les services de bas niveau fournis par un système d'exploitation et de comprendre leur fonctionnement.
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
16 vues105 pages

Introduction à la Programmation Système

Le document présente un cours sur la programmation système, principalement sous Linux et Unix, en abordant des concepts tels que les fichiers, les processus, la communication, les threads et la synchronisation. Il couvre également l'historique des systèmes d'exploitation, en se concentrant sur l'évolution d'Unix et de Windows, ainsi que sur les distinctions entre l'espace noyau et l'espace utilisateur. L'objectif est d'explorer les services de bas niveau fournis par un système d'exploitation et de comprendre leur fonctionnement.
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Programmation Système

Merci

• Alexandre Dang : Programmation système sous Linux et


Windows (électif 2A, Rennes)

• Frédéric Tronel et Pierre Wilke : Systèmes d'exploitation


(électif 2A, Rennes)

• Idir Ait Sadoune : Systèmes d'exploitation et virtualisation


(Mastère spécialisé ASI, Saclay)

2 ©2024 CentraleSupélec - Dominique Marcadet


Plan
• Présentation

• Introduction

• Historique

• Programmation sous Unix

• Fichier

• Processus

• Communication

• Threads

• Synchronisation
3 ©2024 CentraleSupélec - Dominique Marcadet
Plan
• Présentation

• Introduction

• Historique

• Programmation sous Unix

• Fichier

• Processus

• Communication

• Threads

• Synchronisation
4 ©2024 CentraleSupélec - Dominique Marcadet
Objectifs
• La programmation système est un type de programmation qui
vise au développement de programmes qui font partie du
système d’exploitation d’un ordinateur ou qui en réalisent les
fonctions [...]
Wikipédia

• Dans le cadre de ce cours, l'objectif est de découvrir et


d'utiliser quelques services de bas niveau fournis pas un
système d'exploitation.

• Linux est pris comme exemple pour les services retenus.

• Connaissances supposées : les mêmes que celles du cours


« Concepts des langages de programmation - Mise en œuvre
en C/C++ ».
5 ©2024 CentraleSupélec - Dominique Marcadet
Système d'exploitation

• Un système d’exploitation (ou OS, Operating System) est


une couche logicielle conceptuellement située entre le
matériel et les applications.

• Il o re aux programmeurs et aux utilisateurs un ensemble


d’abstractions qui permettent de ne pas avoir à gérer les
spéci cités du matériel.

• Les applications font des appels systèmes à des services


proposés par le système d’exploitation qui, seul, gère les
implications matérielles et retourne le résultat de l’appel.

6 ©2024 CentraleSupélec - Dominique Marcadet


ff
fi
Services

• Lancement et gestion des applications.

• Gestion de la mémoire.

• Gestion des E/S et des chiers.

• Gestion des périphériques.

• Coopération inter-tâches.

• Communications via le réseau.

7 ©2024 CentraleSupélec - Dominique Marcadet


fi
Espaces noyau et utilisateur
• Dans les systèmes d’exploitation modernes, il existe une distinction
nette entre l’espace noyau et l’espace utilisateur.

• Le noyau s’exécute dans une zone mémoire particulière et avec des


privilèges élevés.

• Il peut accéder à toute la mémoire, à tous les périphériques, et à


toutes les instructions du microprocesseur.

• Les applications s’exécutent pour leur part dans l’espace utilisateur.

• Elles ne peuvent accéder qu’à la partie de la mémoire que le noyau


leur a attribuée et ne peuvent utiliser qu’un sous-ensemble des
instructions du microprocesseur.

• Le noyau contrôle leurs accès aux périphériques via les pilotes.


8 ©2024 CentraleSupélec - Dominique Marcadet
Liens entre couches
• Appels syst mes : demandes
de services

• Noyau (kernel) : partie du


Appels Applications système d'exploitation
systèmes rendant des services en lien
Noyau
Pilotes avec le matériel

Matériel • Interruptions : v nements


produits par le mat riel
Exceptions Interruptions
• Exceptions : v nements
g n r s par le processeur

• Pilotes (drivers) : applications


contr lant les p riph riques
9 ©2024 CentraleSupélec - Dominique Marcadet












Plan
• Présentation

• Introduction

• Historique

• Programmation sous Unix

• Fichier

• Processus

• Communication

• Threads

• Synchronisation
10 ©2024 CentraleSupélec - Dominique Marcadet
Multics
• Multiplexed Information and Computing Service
(Wikipédia)

• Projet commun entre le MIT, les Bell Labs et General


Electric (GE), démarré en 1964.

• Les Bell Labs se sont retirés en 1969.

• C’est un système d’exploitation extrêmement novateur


mais qui a connu un succès commercial en demi-teinte.

• Les chercheurs des Bell Labs impliqués dans Multics


s’inspireront de certaines des idées présentes dans
Multics pour concevoir un système plus simple : Unix.
11 ©2024 CentraleSupélec - Dominique Marcadet
Unix : l'origine (1969)
• Les Bell Labs trouvent le projet Multics trop complexe et
ambitieux.

• Ils se retirent progressivement du projet.

• Les derniers chercheurs à s’être retirés, reconsidèrent la


construction d’un système d’exploitation multitâche de
conception plus simple que Multics.

• Les concepteurs : Ken Thompson, Brian Kernighan,


Dennis Ritchie

• Une première version est développée sur un PDP-7, dans


l'optique de faire tourner le jeu vidéo Space Travel.
12 ©2024 CentraleSupélec - Dominique Marcadet
Unix : l'expansion (1)
• Les Bell Labs fournissent un soutien aux trois chercheurs, à la
condition qu’ils écrivent un éditeur de texte et un système de mise en
page pour le nouvel OS.

• Celui-ci est porté sur PDP-11.

• Brian Kernighan suggère le nom d’Unics (Uniplexed Information and


Computing Service) qui est un jeu de mot à propos de Multics.

• Le terme UNIX remplacera rapidement celui d’Unics.

• Le logiciel de mise en page TROFF est développé et sera utilisé par


la suite pour la rédaction de l’ensemble des brevets de la compagnie.

• En 1972, UNIX est réécrit en C. Ceci permit de le porter plus


facilement vers d’autres processeurs.

13 ©2024 CentraleSupélec - Dominique Marcadet


Unix : l'expansion (2)
• En 1956, une condamnation dans un procès antitrust interdit à AT&T
de se lancer dans le commerce lié aux ordinateurs.

• Les Bell Labs qui sont une division de AT&T ne peuvent donc pas
commercialiser UNIX.

• Celui-ci est donc distribué de manière gratuite (avec le code source).

• UNIX devient de plus en plus utilisé et populaire dans les universités


américaines notamment pour l’enseignement des systèmes
d’exploitation.

• La septième version d’UNIX est produite en 1979.

• Cette version marque un changement de licence : il n’est plus


possible d’étudier le code source du système d’exploitation.

14 ©2024 CentraleSupélec - Dominique Marcadet


Unix : le début de la division
• En 1983, le département de la Justice américain (DOJ) entame
un second procès antitrust contre AT&T.

• Ce procès a pour résultat le découpage de la compagnie, mais


la relève de son interdiction de se lancer dans le commerce des
ordinateurs.

• AT&T en pro te pour commercialiser UNIX.

• Comme les conditions de distribution sont devenues moins


favorables pour les universités, l’université de Berkeley (UCB)
continue à développer sa propre version (Berkeley Software
Distribution).

• La branche BSD continue d'exister de nos jours, elle s’est


subdivisée en de nombreuses variantes.
15 ©2024 CentraleSupélec - Dominique Marcadet
fi
Unix : la division

• C’est aussi durant les années 80 que de nombreuses


compagnies privées se mettent à produire leur propres
versions d’UNIX, c’est ce qu’on a appelé la guerre des
UNIX.

• Malgré des tentatives de normalisation (dont POSIX), les


stratégies commerciales des di érentes compagnies ont
nalement nuit au succès commercial d’UNIX.

• Timeline

16 ©2024 CentraleSupélec - Dominique Marcadet


fi
ff
Unix : familles

17 ©2024 CentraleSupélec - Dominique Marcadet


GNU/Hurd

• Richard Stallman : projet de développer un système


d’exploitation libre et compatible Unix (1983).

• GNU « GNU’s Not UNIX ».

• Au début des années 1990, le projet GNU possède une


version utilisable de tous les éléments nécessaires à la
construction d’un système d’exploitation à l’exception du
plus central : le noyau.

• Projet GNU/Hurd : micro-noyau

18 ©2024 CentraleSupélec - Dominique Marcadet


Linux
• Linus Torvalds

• [Link]
(message, minix)

• Noyau monolithique

• Historique des versions


Crédit : [Link]
Les composants du noyau

• Nombreuses distributions

19 ©2024 CentraleSupélec - Dominique Marcadet


PDP-11 et shell

• Interface utilisateur de type


« ligne de commande »

• shell qui entoure le noyau


(kernel)

• Le premier (sh) pour les


versions 1 à 6 d'Unix

• De nombreuses variantes
existent maintenant
Crédit : Stefan_Kögl / CC BY-SA

20 ©2024 CentraleSupélec - Dominique Marcadet


De MS-DOS à MS-Windows 3
• MS-DOS 1.0, sorti en 1981, était un système d’exploitation 16
bits mono-utilisateur en ligne de commande. Il occupait 8 Ko
en mémoire.

• Deux versions de MS-Windows sortent respectivement en


1985 et 1987. Elles ne rencontrent pas un grand succès.

• Windows 3, sorti en 1990 pour l’Intel 386, se vend à plus d’un


million d’exemplaires en 6 mois. Il s’agit avant tout d’une
surcouche graphique à MS-DOS, pas d’un vrai système
d’exploitation.

• Même s’ils apportent de nombreuses innovations


technologiques (Multiprogrammation, gestion des processus,
mémoire virtuelle), MS-Windows 95, 98 et Me sont en fait
construits en interne sur la base MS-DOS.
21 ©2024 CentraleSupélec - Dominique Marcadet
De Windows NT 3.1 à Windows XP

• Parallèlement à Windows 3, Microsoft (après l'arrêt de sa


collaboration avec IBM sur OS/2) conçoit un système d’exploitation
entièrement 32 bits dédié aux serveurs, Windows NT, qui introduit
l’API Win32. La première version, Windows NT 3.1, sort en 1993.

• Windows NT 4, sorti en 1996, propose la même API Win32 mais


exhibe la même interface graphique que MS-Windows 95. 4
familles de processeurs sont supportés.

• Son successeur, Windows 2000, ne supportera plus de manière


active que les processeurs Intel x86.

• L’arrivée de Windows XP (2001) marque un tournant important dans


l’évolution des gammes d’OS Microsoft : pour la première fois, les
systèmes orientés professionnels et les systèmes orientés grand-
public sont basés sur le même noyau et l’API Win32.
22 ©2024 CentraleSupélec - Dominique Marcadet
De Windows Vista à Windows 11

• Windows Vista (2007), plus de 5 ans après Windows XP, a


reçu beaucoup de critiques.

• Windows 7 (2009) a été lui très bien accueilli

• Windows RT (2012) à destination des processeurs ARM


(Surface RT)

• Windows 10 en 2015, Windows 11 en 2021

• Timeline

23 ©2024 CentraleSupélec - Dominique Marcadet


macOS

• Mac OS Classic de 1984 à 2001(Système 1 à Mac OS 9)

• macOS (Mac OS X jusqu'en 2012, OS X jusqu'en 2015)

• Basé sur un noyau XNU et l'implémentation BSD d'Unix


(Darwin)

• Interface graphique propriétaire issue de Next

• OS dérivés : iOS, iPadOS, watchOS, tvOS, visionOS,


audioOS

24 ©2024 CentraleSupélec - Dominique Marcadet


Plan
• Présentation

• Introduction

• Historique

• Programmation sous Unix

• Fichier

• Processus

• Communication

• Threads

• Synchronisation
25 ©2024 CentraleSupélec - Dominique Marcadet
Unix : tout est chier
• chier normal

• r pertoire

• lien symbolique : une r f rence vers un autre chier

• named pipe ou FIFO : un outil de communication


unidirectionnel entre processus locaux

• socket : un outil de communication bidirectionnel entre


processus distants

• p riph riques ( cran, imprimante, clavier, disque, clef USB...)

• processus (Linux)
26 ©2024 CentraleSupélec - Dominique Marcadet
fi






fi
fi
Descripteur de chier
• Tout ces chiers sont manipulables par des descripteurs de
chier

• En C ils sont repr sent s par des entiers non n gatifs (int)
→ STDIN_FILENO (0) : entrée standard
→ STDOUT_FILENO (1) : sortie standard
→ STDERR_FILENO (2) : sortie d'erreurs standard

• Avantage/d savantage:

+ API commune pour pouvoir g rer toutes les ressources


avec des descripteurs de chiers
→ open(), creat(), read(), write(), fcntl() et close()

- Fonctions g n riques et de bas niveau, ne permettant pas


une utilisation simple de ressources sp ci ques
27 ©2024 CentraleSupélec - Dominique Marcadet
fi
fi





fi


fi
fi

Flux de donn es
• Surcouche aux descripteurs de chiers pour les chiers
normaux

• Dans la bibliothèque standard C (#include <stdio.h>)


→ Repr sentation : FILE *
→ fopen(), fclose(), fread(), fwrite(),
fscanf(), fprintf()...

• Avantages
+ Portabilit , appartient au standard C
+ Performance, gestion plus optimis e des lectures/ critures
+ Simplicit , fonctions sp cialis es dans la gestion de chiers
normaux

• Trois ux standards pour chaque processus


→ stdin, stdout et stderr
28 ©2024 CentraleSupélec - Dominique Marcadet
fl





fi


fi

fi
Plan
• Présentation

• Introduction

• Historique

• Programmation sous Unix

• Fichier

• Processus

• Communication

• Threads

• Synchronisation
29 ©2024 CentraleSupélec - Dominique Marcadet
Programme et processus
• Un programme est l’ensemble des instructions et données
décrivant l’apparence et le comportement d’une application

• Un programme est stocké dans un chier

• Un programme est statique

• Un programme est exécutable

• Un processus est l’ensemble des informations décrivant l’état d’un


programme en cours d’exécution

• Un processus n’existe qu’en mémoire

• Un processus est dynamique

• Plusieurs processus peuvent être associés à un même


programme
30 ©2024 CentraleSupélec - Dominique Marcadet
fi
Analogie culinaire classique

• Recette = programme

• Cuisinier = processeur

• Plat en cours de préparation = processus

• Un cuisinier peut préparer simultanément plusieurs plats


(certains pouvant correspondre à la même recette)

31 ©2024 CentraleSupélec - Dominique Marcadet


Gestion des processus
• Le système d’exploitation a en charge :

• La création de processus

• La répartition de l’utilisation du processeur par les


di érents processus (ordonnanceur, scheduler)

• La communication entre processus (copier/coller,


mémoire partagée…)

• La gestion de l’utilisation des ressources par les


processus

• mémoire

• entrées/sorties
32 ©2024 CentraleSupélec - Dominique Marcadet
ff
États d'un processus
Terminaison

Actif
Fin du quantum Demande d’entrée/sortie
ou de ressource

Suppression

Élection
Prêt Bloqué

Naissance Fin d’entrée/sortie


ou ressource disponible
33 ©2024 CentraleSupélec - Dominique Marcadet
Mémoire vue d'un processus

• Chaque processus pense


Pile être le seul à s'exécuter sur
l'ordinateur (hors aspects
liés aux communications).

• Un processus voit un
Tas espace mémoire illimité (de
Données globales
l'adresse 0 à l'adresse ...).

Code • Les adresses manipulées


par un processus sont dites
logiques.

34 ©2024 CentraleSupélec - Dominique Marcadet


Mémoire vue du SE
• Le système d'exploitation a accès à la mémoire réelle, le
noyau utilise des adresses physiques.

• Il alloue à chaque processus une partie de cette mémoire


réelle.

• Il con gure le composant matériel (MMU) chargé de


convertir les adresses logiques du processus actif en
adresses physiques.

• Les ordinateurs actuels (processeur et système


d'exploitation) utilisent le mécanisme de pagination pour
cela (ils o rent aussi la mémoire virtuelle).

35 ©2024 CentraleSupélec - Dominique Marcadet


fi
ff
Bloc de contexte

• Lors du changement de processus actif, l'ordonnateur doit


sauvegarder l'état du processus courant :
• registres du processeur,
• ressources utilisées :
• espace mémoire utilisé (con guration de la
pagination),
• chiers ouverts,
• canaux de communications utilisés,
• ...
• et restaurer celui du processus qui va (re)devenir actif

36 ©2024 CentraleSupélec - Dominique Marcadet


fi
fi
Types de processus

• Di érents types de processus

• Processus contrôlé par le shell


./[Link]

• Processus en tâche de fond


gedit &

• Services (daemon)
httpd, sshd…

37 ©2024 CentraleSupélec - Dominique Marcadet


ff
Création d'un processus
• La seule fa on de cr er un processus est de dupliquer le
processus courant avec fork()

pid_t fork();

• retourne 0 si on se trouve dans le processus ls

• retourne le PID du ls si on se trouve dans le processus


p re

• Un processus possède un identi ant unique appelé PID

pid_t getpid();

• Tous les processus ont un anc tre commun : le processus


lanc au d marrage (PID = 1).
38 ©2024 CentraleSupélec - Dominique Marcadet




fi


fi
fi
Changement de programme

• Pour changer le programme d'un processus (en g n ral le


ls), il faut appeler une fonction de la famille exec() :

int execl( const char * path,


const char * arg, ..., NULL );

int execv( const char * path,


char * const argv[] );

39 ©2024 CentraleSupélec - Dominique Marcadet


fi


Démo

GitLab

cours/progsyst/Fork.c

5- 40 ©2024 CentraleSupélec - Dominique Marcadet


Terminaison d'un processus
• Terminaison normale

return depuis la fonction main()

void exit( int status );

• Terminaison anormale

void abort();

void assert( expression );

• Attente de la terminaison d'un processus ls

pid_t wait( int * status );


41 ©2024 CentraleSupélec - Dominique Marcadet
fi
Gestion de la mémoire
• Gestion de bas niveau

void * mmap(
void * addr, size_t length, int prot,
int flags, int fd, off_t offset );

int munmap( void * addr, size_t length );

• Gestion via libc

void * malloc( size_t size );

void free( void * ptr );

void * calloc( size_t nmemb, size_t size );

void * realloc( void * ptr, size_t size );


42 ©2024 CentraleSupélec - Dominique Marcadet
Plan
• Présentation

• Introduction

• Historique

• Programmation sous Unix

• Fichier

• Processus

• Communication

• Threads

• Synchronisation
43 ©2024 CentraleSupélec - Dominique Marcadet
IPC
• Douglas McIlroy (1978) et Peter H. Salus (1994) :

• Philosophie Unix

• Write programs that do one thing and do it well.

• Write programs to work together.

• Write programs to handle text streams, because that


is a universal interface.

• Les outils de communication entre processus sont


commun ment appel s IPC (Inter-Process
Communication)

44 ©2024 CentraleSupélec - Dominique Marcadet




Différents IPC

• Signaux

• Pipes ou tubes

• Sockets

• Fichiers partag s

• M moire partag e

• (Files d’attente de message)

45 ©2024 CentraleSupélec - Dominique Marcadet





Signaux
• Message asynchrone d’un processus un autre

• Envoi des signaux avec

int kill( pid_t pid, int sig );

• Action sur réception d'un signal :

int sigaction( int signum,


const struct sigaction * act,
struct sigaction * oldact);

• Signaux standards

SIGINT (Ctrl+C), SIGQUIT (Ctrl+D) …


46 ©2024 CentraleSupélec - Dominique Marcadet

Table des descripteurs
• Le noyau alloue des ressources I/O pour chaque
processus sous forme de descripteurs de chiers. La
plupart des IPC sont repr sent s par des descripteurs de
chiers.

47 ©2024 CentraleSupélec - Dominique Marcadet


fi


fi
Pipes
• Les pipes sont des IPC unidirectionnels

48 ©2024 CentraleSupélec - Dominique Marcadet


Exemple pipe (1)
• On veut un pipe dans lequel un processus ls peut crire
des messages au processus p re

// Tableau pour 2 descripteurs de fichier


int fds[2];
// Cr ation d'un pipe, les bouts sont stock s dans fd
pipe( fds );
pid_t pid = fork();
if( pid == 0 ) { // Processus fils
close( fds[0] ); // Fermeture du c t lecture
// ...
} else { // Processus père
close( fds[1] ); // Fermeture du c t écriture
// On raccroche stdin au c t lecture du pipe
dup2( fds[0], STDIN_FILENO );
// ...
}
49 ©2024 CentraleSupélec - Dominique Marcadet




fi






Exemple pipe (2)
dup2( close(
fd[0], fd[1])
stdin ) État initial
fork()
pipe( fds ) close( fd[0])

stdin = 0 stdin = 0

stdout = 1 stdout = 1

stderr = 2 stderr = 2

fd[0] fd[0]

fd[1] fd[1]

50 ©2024 CentraleSupélec - Dominique Marcadet


Démo

GitLab

cours/progsyst/Pipe.c

5- 51 ©2024 CentraleSupélec - Dominique Marcadet


Sockets
• Les sockets sont des IPC bidirectionnels pouvant utiliser
les protocoles réseaux (UDP, TCP…)

52 ©2024 CentraleSupélec - Dominique Marcadet


Fonctions sockets (1)
• Création d'un socket

int socket( int domain, int type, int protocol );

• Liaison de la socket une adresse IP et un port

int bind( int fd, const struct sockaddr * addr,


socklen_t addrlen );

• Description de l'adresse

struct sockaddr_in address;


address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY; //localhost
address.sin_port = htons( PORT );
53 ©2024 CentraleSupélec - Dominique Marcadet

Fonctions sockets (2)
• Attente de connexion par le serveur

int listen( int sockfd, int backlog );

• Ouverture de la connexion par le client

int connect( int fd, const struct sockaddr * addr,


socklen_t addrlen );

• Acceptation par le serveur de la connexion

int accept( int fd, const struct sockaddr * addr,


socklen_t addrlen );

54 ©2024 CentraleSupélec - Dominique Marcadet


Ordre des octets
• Le nombre sur 32 bits [Link]
int i = 0x12345678; [Link]

• peut être stocké en gros-boutiste (big-endian)


12 34 56 78

&i &i+1
• ou en petit-boutiste (little-endian)
78 56 34 12

&i &i+1
• Des ordinateurs qui échangent des données doivent se
mettre d'accord sur l'ordre des octets utilisé : fonctions
htonl(), htons(), ntohl(), ntohs()
55 ©2024 CentraleSupélec - Dominique Marcadet
Fichiers partagés
• Ouverture du même chier par plusieurs processus

int open( const char * name, int flags, mode_t mode );

• Mappage possible en mémoire

void * mmap(
void * addr, size_t length, int prot,
int flags, int fd, off_t offset );

56 ©2024 CentraleSupélec - Dominique Marcadet


fi
Mémoire partagée
• Une mémoire partagée est nommée, comme un chier

int shm_open( const char * name, int oflag, mode_t mode );

• Mappage en mémoire

57 ©2024 CentraleSupélec - Dominique Marcadet


fi
Plan
• Présentation

• Introduction

• Historique

• Programmation sous Unix

• Fichier

• Processus

• Communication

• Threads

• Synchronisation
58 ©2024 CentraleSupélec - Dominique Marcadet
Processus légers (threads)
• Le temps de création (et de changement) d’un processus
est élevé (… mémoire virtuelle … mémoire cache …).

• Un thread a son propre compteur ordinal, sa propre pile ...


mais n’existe qu’à l’intérieur d’un processus et utilise ses
ressources (espace mémoire en particulier).

• Un thread a aussi un état (actif, prêt, bloqué), un bloc de


contexte (plus petit !).

• C'est aussi le système d'exploitation qui gère les threads.

59 ©2024 CentraleSupélec - Dominique Marcadet


Threads POSIX (1)
• Creation

int pthread_create(
pthread_t * thread_handle,
const pthread_attr_t * attribute,
void * (*thread_function)( void * ),
void * arg
);

• Terminaison

void pthread_exit(
void * ptr
);

60 ©2024 CentraleSupélec - Dominique Marcadet


Threads POSIX (2)
• Obtenir son identi ant de thread

pthread_t pthread_self();

• Attente de terminaison (sinon le thread continue d'exister)

int pthread_join(
pthread_t thread,
void ** ptr
);

• Détachement (il n'est plus possible de l'attendre)

int pthread_detach(
pthread_t thread
);

61 ©2024 CentraleSupélec - Dominique Marcadet


fi
C++ : std::thread
class thread {
public:
template< class F > explicit thread( F f );

void join();
void detach();
id get_id() const;
// ... C++20 : std::jthread
};

namespace this_thread {
id get_id();
void sleep_until( /* ... */ );
void sleep_for( /* ... */ );
// ...
}

62 ©2024 CentraleSupélec - Dominique Marcadet


Utilisation std::thread

void f1( int n ) { /* ... */ }


void f2( int & n ) { /* ... */ }
struct F3 {
void operator()() { /* ... */ }
// ...
};

int main() {
int n1{ 5 }, n2{ -2 };
std::thread t1{ f1, n1 };
std::thread t2{ f2, std::ref( n2 )};
F3 f3;
std::thread t3{ f3 };
[Link](); [Link](); [Link]();
}

63 ©2024 CentraleSupélec - Dominique Marcadet


Démo

GitLab

cours/progsyst/[Link]

5- 64 ©2024 CentraleSupélec - Dominique Marcadet


std::async et std::future

int foo()
{
std::this_thread::sleep_for( 10ms );
return 42;
}

int main()
{
std::future< int > f{ std::async( foo )};
while( f.wait_for( 1ms ) ==
std::future_status::timeout ) {
std::cout << "waiting\n";
}
std::cout << "got " << [Link]() << "\n";
}

65 ©2024 CentraleSupélec - Dominique Marcadet


Démo

GitLab

cours/progsyst/[Link]

5- 66 ©2024 CentraleSupélec - Dominique Marcadet


Plan
• Présentation

• Introduction

• Historique

• Programmation sous Unix

• Fichier

• Processus

• Communication

• Threads

• Synchronisation
67 ©2024 CentraleSupélec - Dominique Marcadet
Plan
• Présentation

• Programmation sous Unix

• Fichier

• Processus et threads

• Communication

• Synchronisation

• Problématique

• Synchronisation de threads

• Synchronisation de processus
68 ©2024 CentraleSupélec - Dominique Marcadet
Problème de la variable partagée

DAB Compte Employeur

solde

credit()
retrait() debit() paiement()

[Link](...); [Link](...);

69 ©2024 CentraleSupélec - Dominique Marcadet


Version incorrecte

1. Chargement de
class Compte {
public: l'attribut solde (en
void credit( double montant ) {mémoire) dans un
solde += montant; registre du processeur
}
2. Ajout de montant au
void debit( double montant ) { registre
solde -= montant;
} 3. Transfert du contenu
private:
du registre en
double solde; mémoire (dans
} l'attribut solde)

70 ©2024 CentraleSupélec - Dominique Marcadet


Exécution correcte
Thread Thread
DAB Employeur

‣ LDR solde ‣ LDR solde


‣ SUB 20 ‣ ADD 50
‣ STR solde ‣ STR solde

100
80 100
130
80 130
80

Registre Registre
71 ©2024 CentraleSupélec - Dominique Marcadet
Exécution incorrecte
Thread Go
Stop Go
Stop
Thread
DAB Employeur

‣ LDR solde ‣ LDR solde


‣ SUB 20 ‣ ADD 50
‣ STR solde ‣ STR solde

100
80 150
100
80 150
100

Registre Registre
72 ©2024 CentraleSupélec - Dominique Marcadet
Le dîner des philosophes

• Interblocage

• Famine

73 ©2024 CentraleSupélec - Dominique Marcadet


Dé nitions

• Ressource critique : ressource qui ne doit pas être


accédée par plusieurs processus simultanément

• Section critique : partie(s) de code accédant à une


ressource critique

• Exclusion mutuelle : mécanisme permettant l’accès à une


ressource critique

74 ©2024 CentraleSupélec - Dominique Marcadet


fi
Propriétés

• Sûreté : au plus un seul processus utilise la ressource à un


moment donné
• Vivacité :
• Un processus obtient l’accès immédiat à une ressource
disponible
• Un processus obtient l’accès à la ressource au bout
d’un temps ni
• E cacité : un processus en attente d’une ressource
n’utilise pas le processeur (il est bloqué)

75 ©2024 CentraleSupélec - Dominique Marcadet


ffi
fi
Solutions

• Il existe des algorithmes (algorithme de Peterson,


algorithme de Dekker), mais ceux-ci imposent une attente
active (e cacité non respectée)

• C'est le système d'exploitation qui peut mettre un thread


ou un processus à l'état bloqué, donc c'est lui qui doit
fournir le service

• Une implémentation e cace impose l'existence d'une


transaction de lecture/écriture au niveau du bus et
l'existence d'une instruction correspondante sur le
processeur (test-and-set, compare-and-swap…)

76 ©2024 CentraleSupélec - Dominique Marcadet


ffi
ffi
Plan
• Présentation

• Programmation sous Unix

• Fichier

• Processus et threads

• Communication

• Synchronisation

• Problématique

• Synchronisation de threads

• Synchronisation de processus
77 ©2024 CentraleSupélec - Dominique Marcadet
Mutex POSIX
• Création
int pthread_mutex_init(
pthread_mutex_t * mutex_lock,
const pthread_mutexattr_t * lock_attr
);

• Acquisition du mutex

int pthread_mutex_lock(
pthread_mutex_t * mutex_lock
);

• Libération du mutex

int pthread_mutex_unlock(
pthread_mutex_t * mutex_lock
);
78 ©2024 CentraleSupélec - Dominique Marcadet
Compte avec mutex
class Compte {
public:
Compte() { pthread_mutex_init( &lock, NULL ); }

void credit( double montant ) {


pthread_mutex_lock( &lock );
solde += montant;
pthread_mutex_unlock( &lock );
}

void debit( double montant ) {


pthread_mutex_lock( &lock );
solde -= montant;
pthread_mutex_unlock( &lock );
}

private:
double solde;
pthread_mutex_t lock;
}
79 ©2024 CentraleSupélec - Dominique Marcadet
Producteur - Consommateur

• Le producteur dépose des données dans une boîte à


lettres de taille xe.
• Le consommateur les retire.
• Synchronisation :
• le consommateur doit attendre qu'une donnée soit
présente dans la boîte à lettres ;
• le producteur doit attendre d'avoir de la place dans la
boîte à lettres.
• Variantes : plusieurs producteurs et consommateurs.

80 ©2024 CentraleSupélec - Dominique Marcadet


fi
Moniteur
• Hansen (1973), Hoare (1974)

• Un moniteur est un module constitué de :

• fonctions d'accès à la ressource critique en exclusion


mutuelle (un verrou)

• condition booléenne et services permettant le blocage


d'un processus/thread (avec libération du verrou) si la
condition n'est pas remplie et réactivation par un autre
processus/thread quand la condition peut être remplie
(l'accès à la ressource critique reste soumise au verrou)

• Mécanisme de base de synchronisation en Java (classe


Object)
81 ©2024 CentraleSupélec - Dominique Marcadet
Variable de condition POSIX (1)

• Création

int pthread_cond_init(
pthread_cond_t * cond,
const pthread_condattr_t * attr
);

• Destruction

int pthread_cond_destroy(
pthread_cond_t * cond
);

82 ©2024 CentraleSupélec - Dominique Marcadet


Variable de condition POSIX (2)

• Attente

int pthread_cond_wait(
pthread_cond_t * cond,
pthread_mutex_t * mutex
);

• Signalisation

int pthread_cond_signal(
pthread_cond_t * cond
);

83 ©2024 CentraleSupélec - Dominique Marcadet


Exemple POSIX (1)
pthread_cond_t data_place_cond;
pthread_mutex_t data_place_mutex;
volatile int data_available = 0;

int main() {
pthread_t c, p;

pthread_cond_init( &data_place_cond, NULL );


pthread_mutex_init( &data_place_mutex, NULL );

pthread_create( &c, NULL, &consumer, NULL );


pthread_create( &p, NULL, &producer, NULL );
pthread_join( c, NULL );
pthread_join( p, NULL );
}

84 ©2024 CentraleSupélec - Dominique Marcadet


Exemple POSIX (2)
void * producer( void * producer_thread_data ) {
while( !done() ) {
create_data();

pthread_mutex_lock( &data_place_mutex );
if( data_available == 1 ) {
pthread_cond_wait(
&data_place_cond,
&data_place_mutex );
}
put_data_into_place();
data_available = 1;
pthread_cond_signal( &data_place_cond );
pthread_mutex_unlock( &data_place_mutex );
}
}

85 ©2024 CentraleSupélec - Dominique Marcadet


Exemple POSIX (3)
void * consumer( void * consumer_thread_data ) {
while( !done() ) {
pthread_mutex_lock( &data_place_mutex );
if( data_available == 0 ) {
pthread_cond_wait(
&data_place_cond,
&data_place_mutex );
}
get_data_from_place();
data_available = 0;
pthread_cond_signal( &data_place_cond );
pthread_mutex_unlock( &data_place_mutex );

process_data();
}
}

86 ©2024 CentraleSupélec - Dominique Marcadet


C++ : std::mutex
class mutex {
public:
void lock();
bool try_lock();
void unlock();
// ...
};
class recursive_mutex { /* ... */ };

class timed_mutex {
public:
bool try_lock_for( /* ... */ );
bool try_lock_until( /* ... */ );
// ...
};
class recursive_timed_mutex { /* ... */ };

87 ©2024 CentraleSupélec - Dominique Marcadet


C++ : std::lock_guard
template< typename Mutex >
class lock_guard {
public:
explicit lock_guard( Mutex & m );
~lock_guard();
// ...
};
template< typename Mutex >
class unique_lock {
public:
void lock();
bool try_lock();
void unlock();
bool try_lock_for( /* ... */ );
bool try_lock_until( /* ... */ );
// ...
};
88 ©2024 CentraleSupélec - Dominique Marcadet
C++ : std::condition_variable
class condition_variable {
public:
void notify_one();
void notify_all();

void wait( unique_lock< mutex > & lock );


bool wait_until( /* ... */ );
bool wait_for( /* ... */ );
// ...
};

class condition_variable_any { /* ... */ };

89 ©2024 CentraleSupélec - Dominique Marcadet


C++ : exemple (1)

std::condition_variable data_place_cond;
std::mutex data_place_mutex;

volatile bool data_available{ false };

int main() {
std::thread c{ consumer{} }, p{ producer{} };

[Link]();
[Link]();
}

90 ©2024 CentraleSupélec - Dominique Marcadet


C++ : exemple (2)
struct producer {
void operator()() {
while( !done() ) {
create_data();
{
std::unique_lock lock{ data_place_mutex };
if( data_available ) {
cond_place_empty.wait( lock );
}
put_data_into_place();
data_available = true;
cond_place_full.notify_one();
}
}
}
};
91 ©2024 CentraleSupélec - Dominique Marcadet
C++ : exemple (3)
struct consumer {
void operator()() {
while( !done() ) {
{
std::unique_lock lock{ data_place_mutex };
if( ! data_available ) {
cond_place_full.wait( lock );
}
get_data_from_place();
data_available = false;
cond_place_empty.notify_one();
}
process_data();
}
}
};
92 ©2024 CentraleSupélec - Dominique Marcadet
Démo

GitLab

cours/progsyst/[Link]

5- 93 ©2024 CentraleSupélec - Dominique Marcadet


Variable partagée (KO)

void f( int & x ) { /*...*/ x += 3; }

int main() {
int n{ 2 };
std::thread t1{ f, std::ref( n )};
std::thread t2{ f, std::ref( n )};
[Link]();
[Link]();
}

94 ©2024 CentraleSupélec - Dominique Marcadet


Variable partagée (OK)
class ProtectedInt {
public:
ProtectedInt( int n ) : n_{ n } {}
void operator+=( int i ) {
std::lock_guard< std::mutex > l( mutex_ );
n_ += i;
}
private:
int n_; std::mutex mutex_;
};

void f( ProtectedInt & x ) { /*...*/ x += 3; }

int main() {
ProtectedInt n{ 2 };
std::thread t1{ f, std::ref( n )};
std::thread t2{ f, std::ref( n )};
[Link](); [Link]();
}

95 ©2024 CentraleSupélec - Dominique Marcadet


std::atomic

void f( std::atomic_int & x ) {


/*...*/
x += 3;
}

int main() {
std::atomic_int n{ 2 };
std::thread t1{ f, std::ref( n )};
std::thread t2{ f, std::ref( n )};
[Link]();
[Link]();
}

96 ©2024 CentraleSupélec - Dominique Marcadet


Plan
• Présentation

• Programmation sous Unix

• Fichier

• Processus et threads

• Communication

• Synchronisation

• Problématique

• Synchronisation de threads

• Synchronisation de processus
97 ©2024 CentraleSupélec - Dominique Marcadet
Différent des threads ?

• La synchronisation entre processus ne peut être que gérée


par le système d'exploitation

• La frontière d'un processus (espace mémoire) est aussi


une frontière pour les langages

• C'est aussi le cas pour la communication entre


processus !

• Les systèmes d'exploitation sont trop variés (parce que les


besoins sont multiples) pour imposer une solution unique

98 ©2024 CentraleSupélec - Dominique Marcadet


Solutions déjà vues

• Certains mécanismes de communication intègrent un


mécanisme de synchronisation

• pipe

• socket

• D'autres mécanismes de communication o rent des


solutions spéci ques

• chiers partagés : un blocage (avec fcntl()) peut être


utilisé pour interdire l'accès à un chier

99 ©2024 CentraleSupélec - Dominique Marcadet


fi
fi
fi
ff
Solutions pour les autres cas
• Par exemple, comment gérer la synchronisation dans le cas
d'une communication par mémoire partagée ?

• Plusieurs solutions

• Se limiter à un seul système d'exploitation

• il faut quand même gérer les di érentes variantes et


versions

• Écrire du code spéci que à chaque système d'exploitation

• di culté de maintenance

• Utiliser une bibliothèque qui o re une interface uni ée

• l'ensemble des possibilités ne sera pas disponible


100 ©2024 CentraleSupélec - Dominique Marcadet
ffi
fi
ff
ff
fi
Sémaphore (1)

• Djikstra (1965)

• Un sémaphore est constitué :

• D’un compteur

• D’une file d’attente de processus (de blocs de


contexte)

• De 3 opérations atomiques (non interruptibles)

101 ©2024 CentraleSupélec - Dominique Marcadet


Sémaphore (2)
• init( val )

• initialise le compteur à val et la file à vide

• wait()
• décrémente le compteur ; si celui-ci est strictement
négatif, bloque le processus et le met dans la file
d’attente

• signal()
• incrémente le compteur ; si celui-ci est négatif ou nul,
libère un processus de la file d’attente

102 ©2024 CentraleSupélec - Dominique Marcadet


Linux : sémaphore anonyme
• Les sémaphores anonymes sont accessibles uniquement
entre processus père- ls et les threads

• Création

int sem_init(
sem_t * sem,
// pshared == 0 ? threads : processus
int pshared,
unsigned int value
);

• Destruction

int sem_destroy( sem_t * sem );

103 ©2024 CentraleSupélec - Dominique Marcadet


fi
Linux : sémaphore nommé
• Les sémaphores nommés sont implémentés par un chier
et sont accessibles par tout les processus/threads

• Création

sem_t * sem_open(
char * name,
int oflag,
mode_t mode,
int val
);

• Destruction

int sem_destroy( sem_t * sem );

104 ©2024 CentraleSupélec - Dominique Marcadet


fi
Linux : opérations sur sémaphores

• Ces deux types de sémaphores o rent les services


suivants :

• Demande d'un jeton

int sem_wait( sem_t * sem );

• Libération d'un jeton

int sem_post( sem_t * sem );

105 ©2024 CentraleSupélec - Dominique Marcadet


ff

Vous aimerez peut-être aussi