Cours systèmes
d’exploitation
Chapitre 1: Programmes, Processus,
et Threads
• Contenu
–Programmes
–Processus
–Changement de contexte
–Threads
2
Execution d’un code dynamique
• Une des fonctionnalités les plus basiques d’un
OS est l’exécution et la gestion d’un code
dynamiquement, ex:
– Une ligne de commande sur le terminal
– Une icone doublement cliques sur le bureau
– Des taches exécutées en arrière plan
• Un processus est l’unité de base d’un
programme en exécution
3
Programmes et Processus
Processus
Instance d’un
programme en cours
d’execution stockée
en RAM
Programme
Un fichier
executable sur
une mémoire Relation 1-n entre
secondaire programme et
processus
4
Comment s’execute un programme?
• Quand vous double cliquer sur un *.exe ,
qu’arrive-t-il?
• Quelles informations doit contenir un fichier
*.exe pour lancer un programme ?
5
Formats de programmes
• Les programmes obéissent à un format
spécifique :
– DOS: COM executables (*.com)
– DOS: MZ executables (*.exe)
• Mark Zbikowski exutables, developpeur DOS
– Windows Portable Executable (*.exe)
– Unix/Linux: Executable and Linkable Format (ELF)
– Mac OSX: Mach object file format (Mach-O)
6
test.c
#include <stdio.h>
int big_big_array[10 * 1024 * 1024];
char *a_string = "Hello, World!";
int a_var_with_value = 100;
int main(void) {
big_big_array[0] = 100;
printf("%s\n", a_string);
a_var_with_value += 20;
printf("main is : %p\n", &main);
return 0;
}
7
Format de fichier ELF
• ELF Header
– Contient des informations de
compatibilité
– Point d’entrée du code executable
• Program header table
– Liste tous les segments dans le fichier
– Utilisé pour charger et executer le
programme
• Section header table
– Utilisée par le linker
8
Format de fichier ELF
typedef struct {
1 unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type; ISA ( instruction set
5 Elf32_Half e_machine; architecture)
Elf32_Word e_version;
Elf32_Addr e_entry; Offset du program headers
Elf32_Off e_phoff; • Point d’entrée d’un
Elf32_Off e_shoff; code executable
Offset du section headers
10 Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
De program headers
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
# de section headers
15 Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr; 9
ELF Header
$ gcc –g –o test test.c
$ readelf --header test
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400460
Start of program headers: 64 (bytes into file)
Start of section headers: 5216 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 36
Section header string table index: 33 10
Point d’entrée
int main(void) {
…
printf("main is : %p\n", &main);
return 0;
}
$ gcc -g -o test test.c
$ readelf --headers ./test | grep Entry point'
Entry point address: 0x400460
$ ./test
Hello World!
main is : 0x400544
11
Point d’entrée!= &main
$ ./test • La plupart des compilateurs
Hello World!
main is : 0x400544 inserent des programmes
$ readelf --headers ./test | grep Entry point' avant et après le code
Entry point address: 0x400460 • Ce code s’executent avant et
$ objdump --disassemble –M intel ./test
… après le main()
0000000000400460 <_start>:
400460: 31 ed xor ebp,ebp
400462: 49 89 d1 mov r9,rdx
400465: 5e pop rsi
400466: 48 89 e2 mov rdx,rsp
400469: 48 83 e4 f0 and rsp,0xfffffffffffffff0
40046d: 50 push rax
40046e: 54 push rsp
40046f: 49 c7 c0 20 06 40 00 mov r8,0x400620
400476: 48 c7 c1 90 05 40 00 mov rcx,0x400590
40047d: 48 c7 c7 44 05 40 00 mov rdi,0x400544
400484: e8 c7 ff ff ff call 400450 <__libc_start_main@plt>
…
12
Sections & Segments
sections multiples
• Sections : sont des bouts de dans un segment
codes et de données Segments
regroupés et liés par le
compilateur
• chaque segment contient une
ou plusieurs sections liés ( ex
: section de code )
13
Sections
• Sections : différentes parties de code et de
données qui composent un programme
• Sections clé:
– .text – Code executable
– .bss – Les variables globales à initializer à 0
– .data, .rodata – données initialisées
– .strtab – Noms des variables et des fonctions
14
Section (Exemple)
Chaine de car → .data
Tableau vide →
.bss
int big_big_array[10*1024*1024];
char *a_string = "Hello, World!";
int a_var_with_value = 0x100;
Variable globale
int main(void) {
initialisée→ .data
big_big_array[0] = 100;
printf("%s\n", a_string);
a_var_with_value += 20;
…
constante → .rodata
}
Code → .text 15
$ readelf --headers ./test
…
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .[Link]-tag .[Link]-id .[Link] .dynsym .dynstr .[Link] .gnu.version_r
.[Link] .[Link] .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .ctors .dtors .jcr .dynamic .got .[Link] .data .bss
04 .dynamic
05 .[Link]-tag .[Link]-id
06 .eh_frame_hdr
07
08 .ctors .dtors .jcr .dynamic .got
…
There are 36 section headers, starting at offset 0x1460:
Section Headers:
[Nr] Name Type Address Offset Size ES Flags Link Info Align
[ 0] NULL 00000000 00000000 00000000 00 0 0 0
[ 1] .interp PROGBITS 00400238 00000238 0000001c 00 A 0 0 1
[ 2] .[Link]-tag NOTE 00400254 00000254 00000020 00 A 0 0 4
[ 3] .[Link]-I NOTE 00400274 00000274 00000024 00 A 0 0 4
[ 4] .[Link] GNU_HASH 00400298 00000298 0000001c 00 A 5 0 8
[ 5] .dynsym DYNSYM 004002b8 000002b8 00000078 18 A 6 1 8
[ 6] .dynstr STRTAB 00400330 00000330 00000044 00 A 0 0 1
[ 7] .[Link] VERSYM 00400374 00000374 0000000a 02 A 5 0 2
…
Program Loader
• Une fonctionnalité système
qui charge un programme
dans la mémoire et crée des Mémoire
process : ESP
Stack
– Place les segments en mémoire
– Charge les librairies nécessaires
– Effectue la relocation en
mémoire Fichier ELF Heap
– Alloue de l’espace pour la pile ELF Header
de contexte .text .bss
.data
– Affecte à l’EIP(instruction .rodata
.rodata EIP
pointer register) le début du .data
.bss .text
programme
17
Program Loader
• La pile est utilisée pour les variables
Mémoire
locales et les appels de fonction
– Sa taille augmente de haut vers le bas Stack
• Le tas est alloué dynamiquement
(malloc/new)
– Sa taille augmente de bas en haut Heap
• Quand les deux structures se croisent, .bss
il n’y a plus de mémoire pour le .rodata
porcess .data
– Le Process va probablement crasher .text
18
• Programs
• Processus
• Changement de contexte
• Threads
19
Les processus en mémoire
• Une fois le programme chargé , le noyeau dois
gérer le nouveau process créé
• Program Control Block (PCB): structure de
données representant un process
– Contient au moins un thread (peut être plus…)
– Garde trace de la mémoire utiliée par le process
• Segments de code
• Segments de données (stack & heap)
– Garde trace de l’éxecution du process
• Valeurs de registre
• EIP ( Instruction Pointer Register )
20
Program Control Block (PCB)
• Structure qui représente un processus en mémoire
• Crée pour chaque proceussus par le loader
• Géréé par le SE
struct task_struct { // Typical Unix PCB
pid t_pid; // process identifier
long state; // state of the process
unsigned int time_slice; //scheduling information
struct task_struct *parent; // this process’s parent
struct list_head children; // this process’s children
struct files_struct *files; // list of open files
struct mm_struct *mm; // address space of this process
};
21
Etats d’un processus
• Le processus passe par plusieurs états
– new: Le processus est créé
– running: Les Instructions sont en cours d’exécution
– waiting: Le processus est en attente d’un événement
– ready: Le processus attend d’être relancé
– terminated: le porcessus a termién son exécution
22
Arboresence de processus
• Tous les processus ont des parents
– i.e. quel processus a lance un autre?
• Si un processus déclenche de nouveaux
processus , ce sont ses enfants
– On créer alors un arbre de processus
• Si un parent termine son execution avant son
enfant , l’enfant devient orphan ( orphelin)
• Si un fils sors avant que le parent execute
wait(), le fils est appelé zombie
23
Arboresence de processus
• init un processus special déclenché par le
noyeau : racine de tous les processus
i ni t
pi d = 1
l ogi n kt hr e add s s hd
pi d = 8415 pi d = 2 pi d = 3028
bas h khe l pe r pdf l us h s s hd
pi d = 8416 pi d = 6 pi d = 200 pi d = 3610
e mac s t cs ch
ps
pi d = 9204 pi d = 4005
pi d = 9298
24
Gestion des processus
• fork() – Crée une copie du processus courant
avec un nouvel espace mémoire
– Sans arguments!
• exec() – Appel système pour changer le
programme cournant
• wait() – appel système pour attendre la fin
d’execution d’un autre processus
• signal() –notification à envoyer vers un autre
processus
25
Gestion des processus
Child Process
pid = fork();
if (pid == 0) main() {
exec(…); …
else }
wait(pid);
pid = 0
pid = fork(); pid = fork();
if (pid == 0) if (pid == 0)
exec(…); exec(…);
else pid = 9418 else
wait(pid); wait(pid);
Original Process 26
Question: que fait ce code?
int child_pid = fork();
if (child_pid == 0) { // I'm the child process
printf("I am process #%d\n", getpid());
return 0;
} else { // I'm the parent process
printf("I am parent of process #%d\n", child_pid);
return 0;
}
27
• Programmes
• Processus
• Changement de contexte
• Threads
28
Changement de contexte
• Changement de context
– Sauvegarde l’état d’un prcocessus avant de passer
vers un autre
– Restore l’état du processus une fois qu’il y
retourne
• Questions :
– Comment enregistrer l’état d’un processus?
– Comment récupérer l’état d’un processus?
29
La pile de processus
• Chaque processus est défini par une pile en
mémoire qui enregistre:
– Ses variables locales
– Les arguments des fonctions
– Les addresses des fonctions
• Sur les processeurs x86:
– ESP (Stack Pointer register) pointe vers la fin de la
pile (la donnée la plus récente)
30
int bar(int a, int b) {
int r = rand();
return a + b - r;
}
stack_exam.c
int foo(int a) {
int x, y;
x = a * 2;
y = a - 7;
return bar(x, y);
}
int main(void) {
…
foo(12);
…
}
31
$ gcc -g -fno-stack-protector -m32 -o stack_exam
stack_exam.c
Memory
EBP
$ objdump --disassemble –M intel ./stack_exam
… main()’s main()’s local variables
EIP 804842a: e8 c0 ff ff ff call 80483ef <foo> Frame
804842f: b8 00 00 00 00 mov eax,0x0 ESP 12 Argument to foo()
…
0x804842f Return addr to main()
080483ef <foo>:
80483ef: 55 push ebp Saved EBP
80483f0: 89 e5 mov ebp, esp
80483f2: 83 ec 28 sub esp, 0x28
80483f5: 8b 45 08 mov eax, [ebp+0x8]
80483f8: 01 c0 add eax, eax 24 x=a*2
80483fa: 89 45 f4 mov [ebp-0xc], eax
80483fd: 8b 45 08 mov eax, [ebp+0x8] 5 y=a-7
8048400: 83 e8 07 sub eax, 0x7
foo()’s
8048403: 89 45 f0 mov [ebp-0x10],eax
Frame
8048406: 8b 45 f0 mov eax, [ebp-0x10]
8048409: 89 44 24 04 mov [esp+0x4],eax
804840d: 8b 45 f4 mov eax, [ebp-0xc]
8048410: 89 04 24 mov [esp], eax
8048413: e8 bc ff ff ff call 80483d4 <bar> 5 2nd arg for bar()
8048418: c9 leave
24 1st arg for bar()
8048419: c3 ret
… 0x8048418 Return addr to foo()
Memory
EBP
… foo()’s local variables
080483d4 <bar>:
foo()’s
EIP 80483d4: 55 push ebp Frame 5 2nd arg for bar()
80483d5: 89 e5 mov ebp, esp
24 1st arg for bar()
80483d7: 83 ec 18 sub esp, 0x18
80483da: e8 31 ff ff ff call 8048310 <rand@plt> ESP 0x8048418 Return addr to foo()
80483df: 89 45 f4 mov [ebp-0xc], eax
Saved EBP
80483e2: 8b 45 0c mov eax, [ebp+0xc]
80483e5: 8b 55 08 mov edx, [ebp+0x8]
80483e8: 01 d0 add eax,edx
bar()’s
80483ea: 2b 45 f4 sub eax, [ebp-0xc]
Frame Some # Result of rand()
80483ed: c9 leave
80483ee: c3 ret
…
• leave → mov esp, ebp; pop ebp;
• La Valeur retournée est placée dans EAX
33
Changement de contexte
• On a vu que la pile sauvegarde
– Les variables locales
– Les arguments des fonctions
– Les valeurs retournée par els fonctions
– … grosso modo : l’état du processus
• Si on change de pile , on change alors de
processus
– Changement de context : passer d’un ESP à un
autre
34
• Programmes
• Processus
• Changement de contexte
• Threads
35
Problèmes avec les processus
• La creation de nouveau processus est lourde
(i.e. lente)
– Espace mémoire dédié pour chaque nouveau
proceussus
– fork() copie les états d’un parent vers un fils :
Beaucoup d’espace mémoire consommé
36
Threads
• processus légértqui partage le même espace
mémoire que le parent
• Chaque processus dispose d’au moins un thread
• Avantages:
– Espace mémoire partagé
– Economie: se crée plus rapidemnt et le changement
de context est tout aussi rapide
– Mise à l’échelle: Idéal pour des processeurs multi-
cores
37
Processus simple Process avec plusieurs
threads
Process-Level Shared Data Process-Level Shared Data
Global File Global File
Code Code
Data Descriptors Data Descriptors
Registers Stack Registers Registers Registers
Stack Stack Stack
Thread 1
Thread 1 Thread 2 Thread 3
38
Les Threads POIX
• POSIX standard API pour la creation de thread
– IEEE 1003.1c
• L’Implementation est indépendante du
système
39
Pthread API
• pthread_create() – crée un nouveau
thread
• pthread_exit() – termine le thread
courant
• pthread_join() – attend que le thread
passé en parametre se termine
• Pthreads : dispose d’un certain nombre de
mécanismes de synchronisation
40
Pthread : Exemple
41
Processus vs. Threads
• Threads son meilleurs si:
– Besoin de rapidité
– Calcul intensif
• Processus sont Meilleur si :
– Besoin de protection
• Un processus qui plante n’affecte pas les autres
– Besoin de sécurité
• Chaque processus dispose de son propore espace
mémoire sans toucher aux autres
42