Files
BUT2FI_2025_R3.05/tp/tp4/README.md

202 lines
5.1 KiB
Markdown
Raw Normal View History

2025-09-23 12:48:51 +02:00
# Signaux
>sources à compléter dans le repertoire [src](./src)
#### Ex1
1. Complétez le programme [rebours.c](./src/ex1/rebours.c) qui compte à
rebours seconde après seconde à partir d'un nombre passé en arguments. Sur
chaque ligne, rebours affiche son pid et le nombre de secondes restantes.
```bash
2025-09-23 13:26:43 +02:00
[denis@portabledenis src]$ ./rebours 5
2025-09-23 12:48:51 +02:00
2576552: debut
2576552: 5
2576552: 4
2576552: 3
2576552: 2
2576552: 1
2576552: fin
```
On utilisera `getpid` et `sleep`.
2025-09-23 13:26:43 +02:00
2. Complétez [parexec.c](./src/paraexec.c) prend en arguments de ligne de commande un nom de programme prog,
suivi dune liste arbitrairement longue darguments, et il exécute prog en parallèle (dans des processus)
sur chacun des arguments. Autrement dit, `./parexec prog arg1 arg2 ... argN` exécutera simulta-
nément toutes les commandes prog arg1 , prog arg2 ... prog argN chacune dans un processus
distinct. Testez avec le programme `rebours`.
```bash
[denis@portabledenis scr]$ ./parexec ./rebours 1 2 3
2586243: debut
2586243: 1
2586244: debut
2586244: 2
2586245: debut
2586245: 3
2586243: fin
2586244: 1
2586245: 2
2586244: fin
2586245: 1
2586245: fin
```
On utilisera `fork` et wait`.
3. Écrivez une version de `paraexec` qui prend un argumant supplémentaire N entre prog et arg1 qui indique
le nomnre maximum d'instancesde prog à lancer en parallèle. Lorsque ce nombre est atteint, `parexec` doit attendre la
fin d'un de ses fils pour en relancer un nouveau.
4. Modifiez votre programme `paraexec` pour que si une des instances de `prog` se termine anormalement alors il tue immédiatement
toutes les instances puis il quitte.
Pour testez, vous pouvez à la console envoyer un signal à un processus executé par `parexec`, ou dans rebours faire une division par
zéro, ou utilisé `abort`.
Utilisez `kill`, `WIFSIGNALED`
2025-09-23 12:48:51 +02:00
#### Ex2
Pour calculer le nombre $\pi$, on utilise la méthode de Monte-Carlo.
On tire aléatoirement des couples $(x,y)$ de nombres de
$[0,1]\times[0,1]$. La probabilité qu'il tombe dans le disque de
rayon 1 est exactement de $\frac{\pi}{4}$. On procède à plusieurs
tirages pour estimer la probabilité correspondante.
```c
uint64_t shots_in=0,shots=0;
double x,y;
int main(int argc, char **argv)
{
shots_in = 0;
shots = 0;
for (;;){
x = rand()/(RAND_MAX*1.0);
y = rand()/(RAND_MAX*1.0);
shots ++;
if ((x*x+y*y)<=1){
shots_in ++;
}
}
/* la probabilité vaut tirsIn/tirs,
Elle converge lentement vers pi/4*/
}
```
En utilisant les signaux, mettre en place :
- avec `SIGALRM`, toutes les 5 secondes, l'affichage de la valeur
de pi en cours et le nombre de tirs effectués.
- avec `SIGINT` l'arrêt du programme (après la demande d'une
confirmation), avec l'affichage du temps écoulé depuis son
lancement, quand on fait `ctrl+C` au terminal.
- avec `SIGQUIT` la réinitialisation du calcul avec `ctrl+\`
depuis le terminal. (faites en sorte que toutes les valeurs restent cohérentes)
Dans chaque handler, les 2 autres signaux seront bloqués.
#### Ex3
Le but est de protéger un morceau de code (**section critique**) d'un éventuellement
déroutement à cause de la prise en compte d'un signal.
```c
int x=2,y=3;
int swap(int *x,int *y){
int tmp=*x;
*x=*y;
*y=tmp;
}
void sig_handler(int signo){
switch(signo){
case SIGQUIT :
printf("x=%d y=%d\n",x,y);
break;
}
}
int main(int argc,char * argv[]){
assert(set_signal_handler(SIGQUIT,sig_handler)==0);
while(1){
swap(&x,&y);
}
}
```
1. Lancez le programme, et envoyez (depuis le terminal) le signal SIGQUIT souvent. La fonction
`swap` est-elle interrompue ? comment le voyez-vous ?
2. Ajoutez le code nécessaire pour assurer que `swap` ne soit jamais interrompue par `SIGQUIT`.
## Pour ceux/celles qui ont **tout** fait
#### Ex4
On va simuler un match de **ping-pong** entre un père et son fils, en utilisant le signal `SIGUSR1`.
- Le père commence à jouer.
- On simule 10 échanges. À chaque coup, le père affiche Ping, le fils Pong. L'envoie de la balle
consiste à envoyer le signal `SIGUSR1` à son adversaire.
La difficulté consiste à synchroniser correctement les échanges.
Voici un premier code naïf :
```c
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
#include <assert.h>
#define N 10
void sig_hand(int sig) {}
sigset_t saveMask, blockMask;
void player_wait(){
pause();
}
void child_process()
{
int x = 0;
while(x < N)
{
player_wait();
printf("\tPong %d!\n", ++x);
kill(getppid(), SIGUSR1);
}
return ;
}
void parent_process(pid_t pid)
{
int y = 0;
while (y < N)
{
printf("Ping %d!\n", ++y);
kill(pid, SIGUSR1);
player_wait();
}
return ;
}
int main(int argc, char* argv[])
{
//set up signal handler for parent & child
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = sig_hand;
assert (sigaction(SIGUSR1, &sa, NULL) != -1);
pid_t pid = fork();
if (pid == 0)
child_process();
else
parent_process(pid);
return 0;
}
```
1. Expliquez pourquoi ce code n'est pas correct (Faites varier `N`).
2. Proposez une solution.