cours 5 et 6 et mv
This commit is contained in:
parent
bec44289fc
commit
9abcc15483
151
ASR31-5-Signaux.md
Normal file
151
ASR31-5-Signaux.md
Normal file
@ -0,0 +1,151 @@
|
||||
## Cours 4 : les signaux
|
||||
|
||||
Les interruptions logicielles ou signaux sont utilisées par le système d’exploitation pour aviser les processus utilisateurs de l’' par exemple) ou l'emploi d’une adresse non valide, sont converties en signaux qui sont envoyés au processus fautif. Ce mécanisme permet à un processus de réagir à cet événement sans être obligé d’en tester en permanence l’arrivée.
|
||||
|
||||
Les processus peuvent indiquer au système ce qui doit se passer à la réception d’un signal. On peut ainsi ignorer le signal, ou bien le prendre en compte, ou encore laisser le système d’exploitation appliquer le com- portement par défaut, qui en général consiste à tuer le processus. Certains signaux ne peuvent être ni ignorés, ni capturés. Si un processus choisit de prendre en compte les signaux qu’il reçoit, il doit alors spécifier la procé- dure de gestion de signal. Quand un signal arrive, la procédure associée est exécutée. A la fin de l’exécution de la procédure, le processus s’exécute à partir de l’instruction qui suit celle durant laquelle l’interruption a eu lieu.
|
||||
|
||||
Ainsi, un processus peut :
|
||||
– Ignorer le signal.
|
||||
– Appeler une routine de traitement fournie par le noyau. Cette procédure provoque normalement la mort du processus. Dans certains cas, elle provoque la création d’un fichier core, qui contient le contexte du processus avant de recevoir le signal. Ces fichiers peuvent être examinés à l’aide d’un débogueur.
|
||||
– Appeler une procédure spécifique créée par le programmeur. Tous les signaux ne permettent pas ce type d’action.
|
||||
|
||||
|
||||
### Envoie d'un signal
|
||||
|
||||
```c
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
int kill(pid_t pid, int signal);
|
||||
```
|
||||
|
||||
Renvoie $-1$ en cas d'erreur, $0$ sinon. Pour ```pid``` on peut avoir :
|
||||
- si ```pid```$> 0$, le processus ```pid```
|
||||
- si ```pid```$= 0$, le groupe de l'émetteur
|
||||
- si ```pid```$= -1$, tous les processus (seul ```root``` peut faire ça)
|
||||
- si ```pid```$< -1$, au groupe ```gid``` = valeur absolue (```pid```)
|
||||
|
||||
|
||||
#### Exemple
|
||||
|
||||
```c
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void sighandler (int signum);
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
char buffer[256];
|
||||
|
||||
if (signal(SIGTERM, &sighandler) == SIG_ERR) {
|
||||
printf("Ne peut pas manipuler le signal\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
fgets(buffer, sizeof(buffer), stdin);
|
||||
printf("Input : %s", buffer);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void sighandler (int signum) {
|
||||
printf("Masquage du signal SIGTERM\n");
|
||||
}
|
||||
```
|
||||
|
||||
Avec pour la fonction ```signal```:
|
||||
|
||||
```c
|
||||
#include <signal.h>
|
||||
void (*signal(int sig, void (*func)(int)))(int);
|
||||
```
|
||||
|
||||
or in the equivalent but easier to read typedef'd version:
|
||||
|
||||
```c
|
||||
typedef void (*sig_t) (int);
|
||||
|
||||
sig_t signal(int sig, sig_t func);
|
||||
```
|
||||
qui permet de modifier le comportement du processus à la réception d'un signal (sauf ```SIGKILL``` et ```SIGSTOP```).
|
||||
|
||||
**Remarque** par défaut la commande ```kill``` du shell envoie le signal 15 (```SIGTERM```) au processus en paramètre.
|
||||
|
||||
|
||||
### SIGUSR1 et SIGUSR2
|
||||
|
||||
```c
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define FOREVER for(;;)
|
||||
|
||||
static void action (int sig);
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
int i, pid, etat;
|
||||
sig_t s1, s2;
|
||||
|
||||
s1 = signal(SIGUSR1, action);
|
||||
s2 = signal(SIGUSR2, action);
|
||||
if (( s1 == SIG_ERR) || (s2 == SIG_ERR)) {
|
||||
perror("Erreur attachement signal");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((pid = fork()) == 0) {
|
||||
pause();
|
||||
kill(getppid(), SIGUSR1);
|
||||
sleep(10);
|
||||
}
|
||||
else {
|
||||
sleep(2);
|
||||
kill(pid, SIGUSR2);
|
||||
pause();
|
||||
printf("Parent : Demande terminaison du fils\n");
|
||||
fflush(stdout);
|
||||
kill(pid, SIGTERM);
|
||||
wait(&etat);
|
||||
printf("Parent : fils terminé\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static void action(int sig) {
|
||||
switch (sig) {
|
||||
case SIGUSR1 :
|
||||
printf("Parent : signal SIGUSR1 recu\n");
|
||||
fflush(stdout);
|
||||
break;
|
||||
case SIGUSR2 :
|
||||
printf("Fils : signal SIGUSR2 recu\n");
|
||||
fflush(stdout);
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
On peut utiliser les fonctions ```pause``` et ```sleep``` pour *faire attendre les processus* (attente d'un signal de façon non active). Ces fonctions sont interrompus à la réception d'un signal.
|
||||
|
||||
#### Exercices
|
||||
|
||||
1- Ecrire un programme qui cré un fils. Celui-ci modifie son comportement sur ```SIGQUIT``` et ```SIGINT``` en les ignorants (on affiche qu'on ne les traite pas). Le fils s'endort au moins 3 fois (pour traiter le réveil des 3 signaux ```SIGQUIT```, ```SIGINT``` et ```SIGKILL```).
|
||||
|
||||
2- Ecrire un programme qui crée un processus qui se met à travailler :
|
||||
- si pendant le travail il reçoit un message ```SIGUSR2``` alors il se met à sauvegarder ces données (dans un fichier) puis il signale à son père qu'il a terminé. Son père le tue définitivement.
|
||||
- si il a le temps de finir son travail, alors il signale à son père qu'il a terminé. Son père lui demande d'engager une sauvegarde des données (dans un fichier). Puis le tue une fois la sauvegarde terminée.
|
||||
|
||||
3- Etude de cas : est ce que le handler de signaux d'un processus est hérité par son fils?
|
||||
|
171
ASR31-6-Tubes.md
Normal file
171
ASR31-6-Tubes.md
Normal file
@ -0,0 +1,171 @@
|
||||
### La communication via les tubes
|
||||
|
||||
#### tubes sans nom
|
||||
Liaison unidirectionnelle. La taille maximale est approximativement de 4Ko. Ils existent via le _shell_ et l’appel système ```pipe()```. Lecture FIFO.
|
||||
|
||||
```c
|
||||
#include <unistd.h>
|
||||
int pipe(int desc[2]); /* ou int *tubEDesc */
|
||||
```
|
||||
|
||||
Cela correspond à la création d’un nouveau tube anonyme. En cas de réussite, il y aura location d’un noted sur le disque des tubes, de deux entrées dans la tables des fichiers ouverts (une en lecture et une en écriture et pour chacune des entrées d’un descripteur dans la table des descripteurs du processus appelant:
|
||||
- ```desc[0]``` correspond au descripteur en lecture sur le tube
|
||||
- ```desc[1]``` correspond au descripteur en écriture sur le tube
|
||||
Un appel réussi renvoie 0, si échec alors -1 (et la variable ```errno``` est positionnée (```EMFILE``` table des descripteurs du proc. pleine ou ```ENFILE``` table des fic ouverts du sys. pleine).
|
||||
|
||||
Les tubes anonymes ne sont pas des « fichiers » comme les autres les appels ```open, stat, access, link, unlink, rename, chmod, chown``` et ```lseek``` ne sont pas autorisés. On peut accéder à des informations (et les modifier) via les appels : ```fstat``` et ```fcntl```.
|
||||
|
||||
Le processus usuel pour la communication inter-processus :
|
||||
1- le processus père créé un tube sans nom avec ```pipe()```
|
||||
2- le processus père créé les fils
|
||||
3- les processus écrivains ferment l'accès en lecture du tube
|
||||
4- les processus lesteurs ferment l'accès en écriture du tube
|
||||
5- communication via ```read``` et ```write```
|
||||
6- fermeture des tubes quand plus besoin de communiquer
|
||||
|
||||
```c
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define R 0
|
||||
#define W 1
|
||||
|
||||
int main ( ) {
|
||||
int fd[2] ;
|
||||
char message[100] ;
|
||||
int nboctets ;
|
||||
char *phrase = " message envoye au pere par le fils " ;
|
||||
|
||||
pipe ( fd ) ;
|
||||
if (fork() == 0) {
|
||||
close ( fd[R] ) ;
|
||||
write ( fd[W] , phrase , strlen (phrase) + 1 ) ;
|
||||
close ( fd[W] ) ;
|
||||
}
|
||||
else {
|
||||
close ( fd[W] ) ;
|
||||
nboctets = read ( fd[R] , message , 100 ) ;
|
||||
printf ( " Lecture %d octets : %s\n" , nboctets , message ) ;
|
||||
close ( fd [R] ) ;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Si on veut structurer les échanges, on a intérêt à prévoir un protocole de communication.
|
||||
|
||||
> Comment un shell exécute une commande comme ```ls | wc```. Dans un premier temps, le tube est créé avec la commande pipe(). Puis un processus fils est créé. Ce processus fils héritera des accès en entrée et en sortie du tube. Ensuite, le processus parent ferme son accès à la sortie du tube et fait pointer ```STDOUT``` sur l’entrée du tube. Dans le même temps, le processus fils ferme son accès à l’entrée du tube et redirige ```STDIN``` sur la sortie du tube. Finalement, les deux processus sont remplacés par les programmes ```ls``` et ```wc```.
|
||||
|
||||
On peut faire de la communication bi-directionnelle en utilisant 2 tubes.
|
||||
|
||||
L'algo de lecture pour ```read(p, buf, TAILLE_BUF)```:
|
||||
|
||||
```swift
|
||||
if tube(p) != vide then
|
||||
let taille = size(p) in
|
||||
buf = extract(p, min(taille, TAILLE_BUF))
|
||||
else
|
||||
if nb(writer, p) == 0 then
|
||||
return 0
|
||||
else
|
||||
if read is Bloquant then
|
||||
process = wait
|
||||
else
|
||||
return -1
|
||||
errno = EAGAIN (* O_NONBLOCK *)
|
||||
```
|
||||
|
||||
> Attention à l'interblocage
|
||||
|
||||
Pour le cas du ```write(p, buffer, tailleBuf)```. On a la garantie que si la taille à écrire est inférieure à la ```PIPE_BUF``` alors l'écriture est atomique. Si ce n'est pas le cas, c'est le système qui décide...
|
||||
|
||||
```swift
|
||||
if nb(lecteur(p) == 0) then
|
||||
receive(SIGPIPE) (* si signal capté => return -1 et errno = EPIPE *)
|
||||
else
|
||||
if write is bloquant then
|
||||
wait until all is written
|
||||
else
|
||||
if n > PIPE_BUF then return m (* m < n *)
|
||||
if n ≤ PIPE_BUF and freesize(p) > n then GOOD
|
||||
if n ≤ PIPE_BUF and freesize(p) < n then return -1
|
||||
```
|
||||
|
||||
On peut utiliser ```dup``` et ```dup2 ``` pour dupliquer. Cela permet de rediriger les E/S standards sur un tube.
|
||||
|
||||
#### A faire en exercice de TP
|
||||
Avec exemple :
|
||||
- écriture dans un tube sans lecteur
|
||||
- ecriture dns un tube plein
|
||||
|
||||
|
||||
#### tubes nommés
|
||||
Il a été rajouté un autre type de tube qui facilite un peu les choses en rajoutant les fonctionnalités suivantes :
|
||||
- Ils ont chacun un nom qui existe dans le système de fichiers (une entrée dans la table des fichiers).
|
||||
- Ils sont considérés comme des fichiers spéciaux.
|
||||
- Ils peuvent être utilisés par des processus indépendants, à condition qu’ils s’exécutent sur une même machine.
|
||||
- Ils existeront jusqu’à ce qu’ils soient supprimés explicitement.
|
||||
- Ils sont de capacité plus grande, d’environ 40 Ko.
|
||||
|
||||
Ainsi, deux processus sans relation parent-enfant peuvent échanger des données au moyen des tubes nommés.
|
||||
|
||||
```man
|
||||
SYNOPSIS
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int
|
||||
mkfifo(const char *path, mode_t mode);
|
||||
|
||||
DESCRIPTION
|
||||
mkfifo() creates a new fifo file with name path. The access permissions are
|
||||
specified by mode and restricted by the umask(2) of the calling process.
|
||||
|
||||
The fifo's owner ID is set to the process's effective user ID. The fifo's
|
||||
group ID is set to that of the parent directory in which it is created.
|
||||
|
||||
RETURN VALUES
|
||||
A 0 return value indicates success. A -1 return value indicates an error,
|
||||
and an error code is stored in errno.
|
||||
```
|
||||
|
||||
On peut créer des tubes depuis le shell ```makefifo my_tube```. On obtient alors :
|
||||
|
||||
```sh
|
||||
bash-4.4$ ls -l my_tube
|
||||
prw-r--r-- 1 pvalarcher staff 0 9 oct 05:48 my_tube
|
||||
bash-4.4$
|
||||
```
|
||||
|
||||
|
||||
### TD
|
||||
Complétez le programme suivant afin de rendre la communication
|
||||
bidirectionnelle entre 2 processus sans laisser de zombies. Le père
|
||||
dort 2 secondes après avoir envoyé son message au fils et le fils dort 4
|
||||
secondes après avoir envoyé son message au père. On n’a pas besoin
|
||||
de variables additionnelles.
|
||||
|
||||
```c
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#define R 0
|
||||
#define W 1
|
||||
|
||||
int main() {
|
||||
int fdp[2], // père
|
||||
fdf[2]; // fils
|
||||
|
||||
char message[100]; // pour récupérer un message
|
||||
|
||||
char *phrasef = "message envoyé au père par le fils";
|
||||
|
||||
char *phrasep = "message envoyé au fils par le père";
|
||||
// À COMPLÉTER !!
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user