Cours:TdProgSyst : Différence entre versions
(→Duplication de processus) |
(→Exercices) |
||
| Ligne 94 : | Ligne 94 : | ||
2. Ecrire un programme se dupliquant en mémoire: le fils lit une valeur saisie par l’utilisateur et se termine en retournant la valeur lue dans exit. Le père affiche alors cette valeur ou le signal qui a terminé le fils. | 2. Ecrire un programme se dupliquant en mémoire: le fils lit une valeur saisie par l’utilisateur et se termine en retournant la valeur lue dans exit. Le père affiche alors cette valeur ou le signal qui a terminé le fils. | ||
| − | 3. '''(Hors TP)''' Ecrire un programme se dupliquant dix fois en mémoire: le fils numéro '''i''' écrit un fichier de nom '''File_i.txt''' de 100 lignes identiques constituées chacune de 50 lettres majuscules aléatoires | + | 3. '''(Hors TP)''' Ecrire un programme se dupliquant dix fois en mémoire: le fils numéro '''i''' écrit un fichier de nom '''File_i.txt''' de 100 lignes identiques constituées chacune de 50 lettres majuscules aléatoires (ce fichier est réécrit en permanence pendant toute la durée de vie du fils). Après avoir généré ses fils, le processus père attend l’appui de la touche ''’Q''’ pour les supprimer et se terminer. On vérifiera la présence des fils par la commande '''ps''' ou encore '''top'''. Puis, on essaiera de supprimer le père brutalement (CTRL+C, ...) et l’on reverifiera l’état de la mémoire. |
| − | |||
=== Exécution de processus (Pour les plus avancés) === | === Exécution de processus (Pour les plus avancés) === | ||
Version du 14 décembre 2015 à 09:52
Sommaire
Td 12 Eléments de programmation système
Gestion des signaux
Deux fonctions du langage C permettent de travailler avec les signaux d’UNIX: la fonction **kill** et **signal**. La première permet d’envoyer un signal à un processus dont on connait le numéro de processus (le PID), et
la fonction signal permet d’associer une fonction écrite par
l’utilisateur à un signal UNIX: lorsque ce dernier est reçu, la fonction est
exécutée.
Les signaux les plus classiques sont les suivants:
SIGINT signal envoyé par UNIX lors d’un Ctrl C pour stopper le processus SIGHUP signal émis par UNIX lors de la déconnexion SIGALRM signal émis par la fonction alarm() SIGUSR1 signal à la disposition de l’utilisateur SIGUSR2 autre signal à la disposition de l’utilisateur
Un exemple d’utilisation de ces fonctions est représenté ci-dessous:
#include <stdio.h> #include <sys/types.h> #include <signal.h>
void gestion_signal() {
... // code de gestion du signal
}
main() {
...
signal(SIGUSR1,gestion_signal); // qd on reçoit SIGUSR1, exécute gestion_signal()
...
}
Chaque fois que le processus recevra le signal SIGUSR1, on ira exécuter la fonction gestion_signal. Pour ceux qui connaissent le concept d’ interruption, il s’agit ici d’une interruption logicielle.
La fonction alarm permet à un processus de s’auto-envoyer le signal SIGALRM au bout d’une certaine temporisation exprimée en secondes.
Exercices
1. Ecrire un programme ignorant les arrêts claviers (CTRL+C, ...).
2. Ecrire un programme ignorant le signal émis lors de la déconnexion.
3. Ecrire un programme affichant toutes les dix secondes la chaîne de caractères coucou sur l’écran.
4. (Hors TD) Ecrire un programme permettant d’afficher le numéro des signaux reçus par le programme.
Duplication de processus
La fonction fork permet à un processus de se dupliquer en mémoire: le segment de code, le segment de données et la pile sont dupliqués. Après l’appel de fork, deux processus identiques s’exécutent alors en mémoire. La valeur de retour de fork permet de savoir dans quel processus on se trouve. Si fork retourne:
- la valeur 0, on est dans le fils.
- une valeur positive, on est dans le père. La valeur retournée est le PID du fils.
- la valeur -1, fork n’a pu s’exécuter pour des raisons diverses: par exemple, plus de mémoire disponible...
Un exemple d'utilisation est:
#include<sys/types.h>
#include<unistd.h>
main() {
int pid;
...
switch (pid=fork()) {
case 0: // -------------------- on est dans le fils
.... // code du fils
exit(0); // fin du fils
case -1: // -------------------- erreur du fork
perror("erreur dans fork");
exit(-1);
default: // -------------------- on est dans le père
... // code du père
}
...
}
Exercices
1. Ecrire un programme se dupliquant en mémoire: le fils écrit des f sur l’écran en utilisant la fonction putchar(’F’) et le père écrit des P en utilisant putchar(’F’).
2. Ecrire un programme se dupliquant en mémoire: le fils lit une valeur saisie par l’utilisateur et se termine en retournant la valeur lue dans exit. Le père affiche alors cette valeur ou le signal qui a terminé le fils.
3. (Hors TP) Ecrire un programme se dupliquant dix fois en mémoire: le fils numéro i écrit un fichier de nom File_i.txt de 100 lignes identiques constituées chacune de 50 lettres majuscules aléatoires (ce fichier est réécrit en permanence pendant toute la durée de vie du fils). Après avoir généré ses fils, le processus père attend l’appui de la touche ’Q’ pour les supprimer et se terminer. On vérifiera la présence des fils par la commande ps ou encore top. Puis, on essaiera de supprimer le père brutalement (CTRL+C, ...) et l’on reverifiera l’état de la mémoire.
Exécution de processus (Pour les plus avancés)
La famille d’appels systèmes exec permet de remplacer le processus courant par un autre placé en arguments dans l’appel de exec. Le nom générique de cet appel est exec. Le premier suffixe possible permet d’expliciter le passage des arguments:
- suffixe l indique que les arguments sont passés en tant que liste terminée par NULL.
- suffixe v spécifie que les arguments sont passés dans un tableau: la dernière valeur est NULL.
Le deuxième suffixe possible indique la façon de rechercher la commande:
- suffixe e indique de rechercher la commande en utilisant les variables d’environnement passées en argument (sous forme d’un tableau terminé par NULL).
- suffixe p indique de rechercher la commande dans le chemin de recherche standard (la variable PATH).
Donnons deux exemples possibles d’utilisation (pour plus de renseignements, se réferer au manuel man exec)
#include <unistd.h>
...
char *v[10];
...
execlp("ls","2>/dev/null",NULL);
...
v[0]="ls"; // idem
v[1]="-l";
v[2]=NULL;
execvp(v[0],v);
...
==== Exercice
Ecrire un minishell permettant de lire des commandes tapées par l'utilisateur et de les exécuter.