9 Commits

Author SHA1 Message Date
Denis Monnerat 2a483e0f4b ordonnancement 2026-09-25 12:17:20 +02:00
Denis Monnerat c0ba99e291 interruptions.md 2026-09-24 13:23:07 +02:00
Denis Monnerat d95de5f45e tp4 2026-09-24 13:21:22 +02:00
Denis Monnerat 30398d72c9 td4 2026-09-23 08:47:07 +02:00
Denis Monnerat 35bdc4fcd4 ajout cm 2026-09-22 18:55:28 +02:00
Denis Monnerat 3cce780526 maj readme 2026-09-22 18:36:31 +02:00
Denis Monnerat de9994fe3a cm pdf 2026-09-22 18:35:38 +02:00
Denis Monnerat 93ef7aa105 merci M.Tafok 2026-09-18 14:28:13 +02:00
Denis Monnerat 60816fac42 cm processus 2026-09-16 20:46:50 +02:00
23 changed files with 1026 additions and 2 deletions
+12 -1
View File
@@ -18,7 +18,18 @@ R3.05 (Programmation système).
Merci de lire les [quelques recommandations](./misc/CODE.md) sur les bonnes pratiques d'écriture de code.
#### Semaine 1 (01/09 - 05/09)
#### Semaine 1 (31/08 - 04/09)
Rappels sur la gestion de la [mémoire](cours/memoire.pdf), [td1](td/td1/td1.pdf), [tp1](tp/tp1/).
#### Semaine 2 (07/09 - 11/09)
[Système de fichiers et api E/S](cours/fichier.pdf), [td2](td/td2/td2.pdf), [tp2](tp/tp2).
#### Semaine 3 (14/09 - 18/09)
[Processus](cours/processus.pdf), [interruption/ordonnancement](cours/interruption_ordoanncement.pdf), [td3](td/td3/td3.pdf), [tp3](tp/tp3).
#### Semaine 4 (21/09 - 25/09)
[interruption/ordonnancement](cours/interruption_ordoanncement.pdf), [td4](td/td4/td4.pdf), [tp4](tp/tp4).
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -16,7 +16,7 @@ void clean(size_t b)
{
for (; b > 0; b -= 1 KB)
// calloc(1 KB, sizeof(char));
malloc(1 KB);
malloc(1 KB);
}
Binary file not shown.
+117
View File
@@ -0,0 +1,117 @@
# Processus
#### Ex1
Compilez et exécutez [ex1-stdio.c](src/ex1-stdio.c) et [ex1-syscall.c](src/ex1-syscall.c).
Expliquez.
#### Ex2
Compilez et exécutez [fork_and_fd1.c](src/fork_and_fd1.c) et [fork_and_fd2.c](src/fork_and_fd2.c).
Expliquez.
#### Ex3
Que fait le programme [copy1byte.c](src/copy1byte.c) ?
Décommentez la ligne du `fork`. Expliquez ce qui se passe.
#### Ex4
Compilez et exécutez [copy_on_write.c](src/copy_on_write.c). En utilisant l'interface `/proc/pid/smaps`, vérifiez
la technique du copy on write utilisé par le système.
#### Ex5
Ecrire un programme qui crée un processus fils.
Dans le fils :
* imprimer le retour de `fork()`, `getpid()`, `getppid()`.
* bloquer 4 secondes (`sleep`).
* se terminer avec `exit(2)`.
Dans le père :
* imprimer le retour de `fork()`, `getpid()`, `getppid()`.
* attendre la fin de son fils (`wait()`), et imprimer son code de retour.
* afficher alors la liste de tous les processus actifs (`execl()` avec
`ps -ef`).
#### Ex6
Le but de l'exercice est de détecter la présence d'un zéro dans un
tableau `unsigned char` de taille `SIZE` en découpant le travail entre
plusieurs processus. On part du programme source suivant :
```c
#define SIZE 1000
int search(const unsigned char * t,int start,int end)
{
/* renvoie 1 s'il y a un 0 dans la tranche du tableau,
* 0 sinon */
}
int main(int argc , char * argv[])
{
int i;
unsigned char arr[SIZE];
srandom(time(NULL));
for (i = 0; i < SIZE; i++)
arr[i] = (unsigned char) (random() % 255) + 1;
printf("Enter a number between 0 and %d: ", SIZE);
scanf(" %d", &i);
if (i >= 0 && i < SIZE) arr[i] = 0;
if (search(arr,0,SIZE-1))
printf("Found !\n");
else
printf("Not found !\n");
return EXIT_SUCCES;
}
```
Combien y-at-il de 0 au plus dans le tableau ? Complétez la fonction
`search`, et testez.
1. Première version. Modifiez le programme pour que le processus crée
un fils. Le fils et le père cherche chacun le zéro dans une moitié
du tableau. Le fils communique le résultat à son père. Celui-ci, à
l'aide de son propre travail, donnera la réponse.
2. Deuxième version. Votre programme accepte sur la ligne de commande
un entier n entre 1 et 100. Le programme crée n fils
qui cherche chacun dans une partie du tableau. Le père attend la fin
de chacun de ses fils, récupère leur résultat et affiche la réponse.
3. Troisième version. On améliore la version précédente. Lorsque qu'un
fils trouve le 0 dans le tableau, et que le père en est averti,
faites en sorte que les autres fils vivants se terminent. On
utilisera la primitive `kill()` qui perment d'envoyer le signal de
terminaison (`SIGTERM`) à tout un groupe.
#### Ex7
Exécutez le programme [session.c](src/session.c) et interprétez avec `ps`
les informations `pid,ppid,pgid,sess,tpgid` des processus créés.
#### Ex8
Écrire un programme qui pour `n>0` donné sur la ligne de commande, engendre l'arbre généalogique :
```
0|
|\ 1
| \
| |\ 2
| | \
| | |\ ...
| | | \
| | | |\ n
x x x x x
```
Chaque processus choisit un nombre au hasard entre 0 et 127. Le processus 0 affichera la plus grande valeur choisie par tous
les processus.
Remarque : on pourra modifier la séquence de nombres aléatoires en utilisant srand dans chaque processus créé. (pourquoi ?)
+45
View File
@@ -0,0 +1,45 @@
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
#include <stdlib.h>
#include<assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define msg1 "je suis le pere"
#define msg2 "je suis le fils !!!"
int main(int argc,char * argv[]){
int infd,outfd;
ssize_t nbread;
char buf[1];
pid_t p;
if (argc != 3){
printf("%s infile outfile\n",argv[0]);
exit(1);
}
infd = open(argv[1],O_RDONLY);
assert(infd >= 0);
outfd = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0644);
assert(outfd >= 0);
//p=fork(); // <- decommentez cette ligne
while(1){
nbread=read(infd,buf,sizeof(buf));
if (nbread <=0 ) break;
write(outfd,buf,sizeof(buf));
}
close(infd);
close(outfd);
}
+49
View File
@@ -0,0 +1,49 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define N 8*1024*1024
char tab[N];
int main(void)
{
for (int i = 0; i < N; i++)
tab[i] = (char)i;
printf("Avant fork : tab = %p\n", (void *)tab);
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
printf("Fils : tab = %p, tab[5000] = %d\n",
(void *)(tab+5000), tab[5000]);
printf("FILS PID = %d\n", getpid());
getchar();
tab[5000] = 42;
printf("-> modification\n");
getchar();
printf("Fils après modification : tab[5000] = %d\n",
tab[5000]);
exit(EXIT_SUCCESS);
}
else {
sleep(1);
printf("Père : tab = %p, tab[5000] = %d\n",
(void *)tab, tab[5000]);
wait(NULL);
}
return 0;
}
+12
View File
@@ -0,0 +1,12 @@
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(){
printf("NON");
if (fork()) {
printf("OUI\n");
}
}
+14
View File
@@ -0,0 +1,14 @@
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(){
//printf("NON");
write(STDOUT_FILENO,"NON",3);
if (fork()) {
//printf("OUI\n");
write(STDOUT_FILENO,"OUI\n",4);
}
}
+32
View File
@@ -0,0 +1,32 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
pid_t p;
int status;
p = fork();
assert( p >= 0);
if ( p == 0){
printf("@retour de fork = %d pid = %d ppid = %d\n",p,getpid(),getppid());
sleep(4);
exit(2);
}
printf("@retour de fork = %d pid = %d ppid = %d\n",p,getpid(),getppid());
assert( wait(&status) >= 0);
if (WIFEXITED(status))
printf("code retour fils = %d\n",WEXITSTATUS(status));
execlp("ls","totototo","-l","/",NULL);
/* code ici */
assert(0);
return 0;
}
+43
View File
@@ -0,0 +1,43 @@
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
#include <stdlib.h>
#include<assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define msg1 "je suis le pere"
#define msg2 "je suis le fils !!!"
int main(int argc,char * argv[]){
int outfd;
pid_t p;
if (argc != 2){
printf("%s file\n",argv[0]);
exit(1);
}
//p=fork();
outfd = open(argv[1],O_WRONLY|O_CREAT,0644);
assert(outfd >= 0);
p=fork();
switch(p){
case (pid_t)-1 :
perror(NULL);
exit(2);
case (pid_t)0 :
write(outfd,msg2,strlen(msg2));
break;
default :
write (outfd,msg1,strlen(msg1));
break;
}
getchar();
close(outfd);
}
+43
View File
@@ -0,0 +1,43 @@
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
#include <stdlib.h>
#include<assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define msg1 "je suis le pere"
#define msg2 "je suis le fils !!!"
int main(int argc,char * argv[]){
int outfd;
pid_t p;
if (argc != 2){
printf("%s file\n",argv[0]);
exit(1);
}
p=fork();
outfd = open(argv[1],O_WRONLY|O_CREAT,0644);
assert(outfd >= 0);
//p=fork();
switch(p){
case (pid_t)-1 :
perror(NULL);
exit(2);
case (pid_t)0 :
write(outfd,msg2,strlen(msg2));
break;
default :
write (outfd,msg1,strlen(msg1));
break;
}
getchar();
close(outfd);
}
+18
View File
@@ -0,0 +1,18 @@
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if (fork()){
printf("session = %d\n",getsid(getpid()));
while(1);
} else {
setsid();
printf("session = %d\n",getsid(getpid()));
while(1);
}
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
# Interruptions - Ordonnancement
- partie 1 : [interruptions](interruptions.md)
- partie 2 : [Ordonnamcement](ordonnancement.md)
+13
View File
@@ -0,0 +1,13 @@
#include <stdio.h>
int main(int argc, char *argv[])
{
unsigned long long i = 0;
while (1)
i++;
return 0;
}
+16
View File
@@ -0,0 +1,16 @@
#include <stdio.h>
void (fonction) (void)
{
asm volatile (".byte 255, 255, 255, 255");
// asm volatile ("ud2");
}
int main(int argc, char * argv[])
{
fprintf(stderr, "J'execute la fonction avec le code invalide...\n");
fonction();
fprintf(stderr, "Et me voici de retour\n");
return 0;
}
+206
View File
@@ -0,0 +1,206 @@
# Interruptions - Ordonnancement
- partie 1 : [interruptions](interruptions.md)
- partie 2 : [Ordonnamcement](ordonnancement.md)
#### Ex1 :
Linux fournit les compteurs d'interruptions dans :
```bash
cat /proc/interrupts
```
Exécutez :
```bash
cat /proc/interrupts
```
Vous devriez obtenir un tableau comportant notamment une colonne par CPU.
Pour connaître le nombre de processeurs logiques :
```bash
nproc
```
Vous pouvez également utiliser :
```bash
lscpu
```
1. Combien de processeurs logiques possède votre machine ?
2. Combien de colonnes correspondant aux CPU observez-vous dans `/proc/interrupts` ?
3. À quoi correspond chaque ligne du tableau ?
4. Pourquoi les compteurs sont-ils différents selon les CPU ?
#### Ex2
Une interruption matérielle peut être générée lorsqu'un périphérique demande l'attention du processeur.
Sur un PC traditionnel, le clavier est associé à l'IRQ 1.
Recherchez les informations relatives au clavier :
```bash
grep -i keyboard /proc/interrupts
```
Vous pouvez également rechercher le contrôleur `i8042` :
```bash
grep i8042 /proc/interrupts
```
Sur de nombreuses machines, vous observerez une ligne semblable à :
```text
1: ... IR-IO-APIC 1-edge i8042
```
Le numéro `1` est le numéro d'IRQ.
Le suffixe :
```text
1-edge
```
indique que cette interruption est configurée en mode déclenché sur front (*edge-triggered*).
Dans un terminal, lancez :
```bash
watch -n 1 'grep "^ *1:" /proc/interrupts'
```
Puis appuyez plusieurs fois sur des touches.
1. Le compteur de l'IRQ 1 augmente-t-il lorsque vous utilisez le clavier ?
2. Sur quel CPU le compteur augmente-t-il ?
3. Pourquoi le clavier n'a-t-il pas besoin d'être interrogé en permanence par le processeur ?
4. Quelle différence faites-vous entre :
- une interruption ;
- une interrogation périodique (*polling*) ?
#### Ex3
Linux permet de contrôler les processeurs auxquels une IRQ peut être distribuée.
Pour l'IRQ 1 :
```bash
cat /proc/irq/1/smp_affinity
```
Vous pouvez également utiliser :
```bash
cat /proc/irq/1/effective_affinity
```
et :
```bash
cat /proc/irq/1/effective_affinity_list
```
1. Quel est le masque d'affinité pour l'IRQ 1 sur votre machine ?
2. Quel(s) CPU(s) est(sont) séléctionné(s) pour la gérer ?
3. Y-a-t-il un interêt à la gérer toujours sur le même cpu ?
#### Ex4
Sur une machine multicœur, un processeur peut avoir besoin de demander une action à un autre processeur.
Linux utilise alors des **IPI** (*Inter-Processor Interrupts*).
Examinez :
```bash
cat /proc/interrupts
```
Recherchez notamment les lignes :
```text
LOC
RES
CAL
TLB
```
Les significations importantes sont :
- `LOC` : interruptions du timer local ;
- `RES` : demandes de réordonnancement ;
- `CAL` : appels de fonctions entre CPU ;
- `TLB` : invalidations de TLB entre CPU.
##### `LOC` : timer local
`LOC` correspond aux interruptions du timer local de chaque CPU.
Elles permettent notamment au noyau d'effectuer certaines opérations liées :
- à la gestion du temps ;
- à la comptabilisation du temps CPU ;
- à l'ordonnancement.
1. Pourquoi chaque CPU possède-t-il son propre compteur `LOC` ?
##### `RES` : rescheduling
Une ligne `RES` correspond à des demandes de réordonnancement transmises entre CPU.
Par exemple, un CPU peut devoir signaler à un autre CPU qu'il doit réévaluer le processus actuellement exécuté.
1. Pourquoi un CPU peut-il avoir besoin de demander à un autre CPU de réévaluer son ordonnancement ?
##### `TLB` — TLB shootdown
Le TLB (*Translation Lookaside Buffer*) contient des traductions récentes :
```text
adresse virtuelle → adresse physique
```
Supposons qu'un CPU modifie les tables de pages d'un processus.
Un autre CPU peut encore posséder dans son TLB une ancienne traduction.
Linux doit alors demander à ce CPU d'invalider l'entrée correspondante.
Il peut utiliser une IPI.
Le mécanisme est souvent appelé :
> **TLB shootdown**
1. Pourquoi une modification des tables de pages peut-elle nécessiter une action sur un autre CPU ?
2. Pourquoi le TLB doit-il être invalidé ?
3. Une IPI TLB provient-elle d'un périphérique matériel externe ?
#### Ex4
Pour déclencher l’exception `Illegal instruction` , il faut exécuter du code
invalide pour le processeur employé. C’est difficile avec les architectures
actuelles car les pages de code sont verrouillées en lecture seule (donc
impossible de modifier des fonctions existantes) et les pages de données ne sont
pas exécutables (donc impossible d’y dérouter l’exécution).
La solution consiste à générer un code assembleur invalide et à l’insérer
manuellement dans un programme C, comme c’est fait dans le fichier
illegal.c (les instructions sont invalides pour un processeur x86, il
faudra peut-être changer les valeurs si on le teste sur une autre architecture).
1. Observez l'effet avec
```bash
strace ./illegal
```
Comment l'exception s'est traduite au niveau du processus fautif ?
+399
View File
@@ -0,0 +1,399 @@
# Interruptions - Ordonnancement
- partie 1 : [interruptions](interruptions.md)
- partie 2 : [Ordonnamcement](ordonnancement.md)
#### Ex1 1
Ouvrez un terminal et exécutez :
```bash
ps
```
Puis :
```bash
ps -f
```
et :
```bash
ps -eo pid,ppid,stat,ni,pri,psr,comm
```
Cette dernière commande affiche notamment :
- `PID` : identifiant du processus ;
- `PPID` : identifiant du processus père ;
- `STAT` : état du processus ;
- `NI` : valeur de nice ;
- `PRI` : priorité affichée par Linux ;
- `PSR` : numéro du processeur sur lequel le processus s'est exécuté ;
- `COMMAND` : nom du programme.
### Questions
1. Quelle est la valeur de `nice` d'un processus nouvellement créé ?
2. Quelles valeurs peut prendre `nice` ?
3. Que signifie une valeur de `nice` élevée ?
4. Que signifie l'état `R` ?
5. Que signifie l'état `S` ?
6. Pourquoi le champ `PSR` peut-il changer au cours du temps ?
#### Ex2.
Nous allons créer un programme très simple qui consomme du temps processeur.
Créez le fichier `cpu.c` :
```c
#include <stdio.h>
int main(void)
{
unsigned long long i = 0;
while (1)
i++;
return 0;
}
```
Compilez-le :
```bash
gcc -O2 -o cpu cpu.c
```
Lancez-le :
```bash
./cpu
```
Dans un autre terminal :
```bash
top
```
### Questions
7. Quel pourcentage de CPU utilise le processus `cpu` ?
8. Observez le champ `NI`.
9. Observez le champ `PR`.
10. Sur quel processeur le processus s'exécute-t-il ?
Utilisez :
```bash
watch -n 0.1 ps -C cpu -o pid,ni,pri,psr,comm
```
11. Appuyez sur `1` dans `top`. Que constatez-vous concernant les différents processeurs ?
Pour arrêter le programme :
```bash
killall cpu
```
#### Ex3
Lancez maintenant **deux** instances :
```bash
./cpu &
./cpu &
```
Puis :
```bash
watch -n 0.1 ps -C cpu -o pid,ni,pri,psr,pcpu,comm
```
et observez avec :
```bash
top
```
### Questions
12. Si votre machine possède plusieurs processeurs logiques, les deux processus utilisent-ils nécessairement le même processeur ?
13. Lancez quatre processus :
```bash
./cpu &
./cpu &
./cpu &
./cpu &
```
Observez leur utilisation CPU.
14. Que se passe-t-il si vous lancez davantage de processus que votre machine ne possède de processeurs logiques ?
15. Pourquoi un processus CPU-bound finit-il malgré tout par laisser le processeur à un autre processus ?
#### Ex4
La commande `nice` permet de lancer un processus avec une valeur de `nice` particulière.
Lancez :
```bash
nice -n 10 ./cpu &
```
Puis :
```bash
watch -n 0.1 ps -C cpu -o pid,ni,pri,pcpu,comm
```
Comparez avec un processus lancé normalement :
```bash
./cpu &
```
### Questions
16. Quelle est la valeur de `nice` des deux processus ?
17. Leur valeur de `PRI` est-elle identique ?
18. Que signifie concrètement `nice = 10` ?
19. Est-ce que `nice` signifie que le processus est exécuté « moins souvent » au sens strict ?
#### Ex5
Lancez deux processus :
```bash
./cpu &
./cpu &
```
Repérez leurs PID :
```bash
pgrep cpu
```
Choisissez l'un des deux PID et modifiez sa valeur de nice :
```bash
renice 10 -p PID
```
par exemple :
```bash
renice 10 -p 12345
```
Observez :
```bash
ps -p 12345 -o pid,ni,pri,pcpu,comm
```
### Expérience
Essayez de faire tourner simultanément deux processus `cpu` :
- l'un avec `nice = 0` ;
- l'autre avec `nice = 10`.
Observez leur consommation CPU pendant plusieurs secondes.
### Questions
20. Le processus avec `nice = 10` reçoit-il toujours du CPU ?
21. Que constatez-vous lorsque les deux processus se disputent le même processeur ?
22. Si les deux processus peuvent s'exécuter simultanément sur deux processeurs différents, l'effet de `nice` est-il aussi visible ?
23. Expliquez pourquoi.
> **Attention :** `nice` modifie la priorité relative d'un processus, mais ne lui réserve pas une quantité fixe du processeur.
#### Ex6
Linux permet de restreindre les processeurs sur lesquels un processus peut s'exécuter.
Utilisez :
```bash
nproc
```
pour connaître le nombre de processeurs logiques disponibles.
Lancez :
```bash
taskset -c 0 ./cpu &
```
Puis :
```bash
ps -C cpu -o pid,psr,pcpu,comm
```
Vous pouvez également consulter directement l'affinité :
```bash
taskset -p PID
```
### Questions
24. Sur quel processeur le processus est-il autorisé à s'exécuter ?
25. Pourquoi le champ `PSR` reste-t-il normalement égal à `0` ?
26. Quelle différence faites-vous entre :
```text
nice
```
et :
```text
taskset
```
#### Ex7
Nous allons maintenant forcer deux processus à utiliser **le même processeur**.
Lancez :
```bash
taskset -c 0 ./cpu &
taskset -c 0 ./cpu &
```
Observez :
```bash
top
```
Puis :
```bash
ps -C cpu -o pid,ni,pri,psr,pcpu,comm
```
### Questions
27. Les deux processus peuvent-ils s'exécuter simultanément sur le CPU 0 ?
28. Quelle proportion approximative du temps processeur reçoit chaque processus ?
Modifiez maintenant la priorité du premier processus :
```bash
renice 10 -p PID1
```
et laissez le second avec :
```text
nice = 0
```
29. Observez à nouveau `top`.
30. Expliquez qualitativement l'effet de `nice` lorsque deux processus CPU-bound sont contraints de partager le même processeur.
#### Ex8
Linux fournit de nombreuses informations sur chaque processus dans :
```text
/proc/PID/
```
Pour un processus `cpu`, examinez :
```bash
cat /proc/PID/status
```
puis :
```bash
cat /proc/PID/sched
```
Dans `sched`, recherchez notamment :
```text
se.sum_exec_runtime
nr_switches
nr_voluntary_switches
nr_involuntary_switches
```
### Questions
31. Que représente `se.sum_exec_runtime` ?
32. Quelle différence faites-vous entre :
- changement de contexte volontaire ;
- changement de contexte involontaire ?
33. Pourquoi un processus `cpu` devrait-il avoir relativement peu de changements de contexte volontaires ?
#### Ex9
Nous allons mettre en place l'expérience suivante :
- deux processus `cpu` ;
- tous deux limités au CPU 0 ;
- processus A : `nice = 0` ;
- processus B : `nice = 15`.
Lancez-les :
```bash
taskset -c 0 ./cpu &
```
et :
```bash
taskset -c 0 nice -n 15 ./cpu &
```
Notez leurs PID.
Après environ 30 secondes, récupérez :
```bash
cat /proc/PID_A/sched
cat /proc/PID_B/sched
```
Relevez notamment :
```text
se.sum_exec_runtime
nr_switches
```
### Questions
34. Le temps CPU obtenu par A et B est-il identique ?
35. Comparez les valeurs de `se.sum_exec_runtime`.
36. Quelle conclusion pouvez-vous tirer sur l'influence de `nice` ?
37. Pourquoi cette expérience est-elle plus significative lorsque les deux processus sont attachés au **même CPU** ?