ITC33 - Informatique. Partie Programmation Syst`eme

Transcription

ITC33 - Informatique. Partie Programmation Syst`eme
ITC33 - Informatique.
Partie Programmation Système
TD1- Gestion des fichiers et signaux
11 septembre 2012
Exercice 1 : Gestion des descripteurs et identifiants de descripteurs
On considère le code suivant :
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int main() {
int fd1 = open("/etc/passwd", O_RDONLY);
int fd2 = open("/etc/fichierInexistant", O_RDONLY);
int fd3 = open("/etc/group", O_RDONLY);
close(fd1);
int fd4 = open("/etc/profile", O_RDONLY);
exit(EXIT_SUCCESS);
}
Seuls les fichiers /etc/passwd, /etc/group et /etc/profile existent et sont accessibles en lecture par
l’utilisateur exécutant ce processus. On rappelle que la valeur allouée à chaque nouveau descripteur
créé est le plus petit entier naturel disponible non référencé dans la table des descripteurs.
1. Quelles devraient alors être les valeurs respectives de fd1, fd2, fd3 et fd4 ?
2. A quoi correspond l’instruction close(1); . Quelle est son effet sur l’exécution d’un programme ?
Exercice 2 : Impact de la taille du buffer
On reprend dans cet exercice un code vu en cours, permettant de copier le fichier /etc/passwd dans
un fichier /tmp/fichier :
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include
#include
#include
#include
#include
<stdio.h>
<unistd.h>
<sys/types.h>
<sys/stat.h>
<fcntl.h>
int main() {
int nbLus;
int fd, fd2;
char buffer[1024];
fd = open("/etc/passwd", O_RDONLY);
fd2 = open("/tmp/fichier", O_WRONLY | O_CREAT, 0644);
do {
nbLus = read(fd, buffer, 900);
write(fd2, buffer, nbLus);
} while (nbLus > 0);
}
1
1. Combien d’octets sont lus à chaque opération de lecture sur le fichier /etc/passwd ?
2. Où sont stockés les octets lus à chaque itération ?
3. En ne changeant que l’instruction de lecture et pas le reste du code, jusqu’à combien d’octets
pourrait-on se permettre de lire sur le fichier ?
4. Décrire quel impact peut alors avoir la taille du tableau buffer ? Penser notamment à ce qui
se passe si l’on double ou réduit de moitié la taille de ce tableau.
Exercice 3 : fusion de fichiers
1. Ecrire un programme fusion.c qui lit deux fichiers sngk.txt et vgt.txt de même taille, et les
fusionne en un fichier ggt.txt créé en prenant les 10 premiers octets de sngk.txt, puis les 10
premiers de vgt.txt, puis les 10 suivants de sngk.txt et ainsi de suite . . .
2. Ecrire le programme inverse separation.c qui, à partir du fichier ggt.txt créé dans la question
précédente, permet de recréer les fichiers sngk.txt et vgt.txt ;
Exercice 4 : Signaux et redirections
1. Rappeler quels sont les deux signaux qui ne peuvent pas être redirigés.
2. A votre avis, pourquoi le système vous empêche de rediriger ces signaux ?
Exercice 5 : capture de signal
En vous inspirant des commandes et exemples présentés dans le cours :
1. Ecrire un script shell comportant une boucle infinie (commande while : ) à l’intérieur de
laquelle le processus affiche un point sur une ligne chaque seconde (commande sleep 1 pour
attendre une seconde). Ajoutez-y une instruction pour qu’il affiche un message de votre choix
si l’utilisateur appuie sur CTRL+C (SIGINT) pendant son exécution. Modifiez ensuite le
script pour qu’il affiche un autre message si l’utilisateur envoie le signal SIGUSR1 lors de son
exécution.
2. Comment l’utilisateur peut-il envoyer le signal SIGUSR1 au processus ?
3. Comment arrêter ce processus ?
4. Traduisez votre script shell en langage C.
2
Correction :
Exercice 1
1. fd1=3, fd2=-1, fd3=4, fd4=3.
Explications :
– Au début du programme, les descripteurs 0 1 et 2 (correspondant respectivement à stdin,
stout et stderr) existent. Le premier appel à la fonction open() (ligne 4) s’effectue correctement et renvoie le plus petit numéro disponible non assigné, donc fd1=3.
– Le second appel à la fonction open() (ligne 5) échoue car le fichier n’existe pas. Il renvoie
la valeur -1 pour signaler l’erreur, ainsi fd2=-1.
– Le troisième appel à la fonction open() (ligne 6) s’effectue correctement et renvoie le plus
petit numéro disponible non assigné, donc fd3=4.
– L’appel à la fonction close() (ligne 7) ferme le fichier associé au descripteur fd1. La
valeur associée au descripteur fd1, à savoir 3, est à nouveau disponible, et devient la
plus petite valeur assignable. A noter cependant que la valeur contenue dans fd1 n’est pas
modifiée.
– Le quatrième appel à la fonction open() (ligne 8) s’effectue correctement et renvoie le plus
petit numéro disponible non assigné, donc fd4=3.
2. L’instruction close(1) va fermer le fichier associé au descripteur 1, à savoir stdout. Dès
lors tous les messages à destination de la sortie écran (envoyés par printf(...)) ne seront
plus affichés.
Exercice 2
1. 900, c’est à dire le troisième argument de l’appel système read() (ligne 25)
2. Dans le tableau de caractères ”buffer”, défini comme tableau de caractères car un caractère
possède une taille de 1 octet.
3. On peut se permettre de lire au plus autant de d’octets que la taille de la zone tampon dans
laquelle on stocke ces derniers. A savoir 1024 caractères, ce qui correspond à la taille du
tableau buffer.
4. Plus le tableau buffer sera grand, moins grand sera le nombre d’appel à la fonction read() pour
parcourir la totalité du fichier lu. En terme de temps d’exécution, il est intéressant d’augmenter la taille du tableau buffer. En revanche, plus ce tableau sera grand, plus il consommera de
mémoire, qui est l’autre ressource critique du système.
3
Exercice 3
#include
#include
#include
#include
<stdio.h>
<stdlib.h>
<fcntl.h>
<unistd.h>
int main() {
int buffer[10]; // buffer pour la lecture
int nbLus;
int fd1 = open("./sngk.txt", O_RDONLY);
int fd2 = open("./vgt.txt", O_RDONLY);
int fd3 = open("./ggt.txt", O_WRONLY | O_CREAT, 0666);
if ((fd1 == -1) || (fd2 ==-1)) {
printf("probleme a l’ouverture des fichiers sngk.txt et/ou vgt.txt\n");
exit(1);
}
if (fd3 == -1) {
printf("probleme a l’ouverture et/ou creation du fichier ggt.txt\n");
exit(1);
}
do {
nbLus = read(fd1, buffer, 10);
write(fd3, buffer, nbLus);
nbLus = read(fd2, buffer, 10);
write(fd3, buffer, nbLus);
} while (nbLus > 0);
clsoe(fd1); close(fd2); close(fd3);
exit(EXIT_SUCCESS);
}
Exercice 4
1. SIGKILL et SIGSTP
2. Nécessaire pour l’ordonnanceur et pour stopper les programmes malveillants ou malfonctionnels. Un utilisateur malicieux pourrait empêcher son programme d’être stoppé et monopoliserait 100% de l’utilisation CPU.
4
Exercice 5
1. #!/bin/bash
trap "echo signal SIGINT capture" SIGINT
trap "echo signal SIGUSR1 capture" SIGUSR1
while :
do
echo ".";
sleep 1;
done
2. kill -SIGUSR1 suivi du PID du processus
3. Il faut envoyer un autre signal à ce processus aui arrête son exécution. Exemple : ouvrir un
terminal, repérer l’id du processus (commande ps) et envoyer le signal SIGKILL au travers
de la commande kill.
4. #include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void interruption(int signal) {
if (signal == SIGINT) printf("signal SIGINT recu\n");
if (signal == SIGUSR1) printf("signal SIGUSR1 recu\n");
}
int main () {
signal (SIGINT, interruption);
signal (SIGUSR1, interruption);
while (1) {
printf(".\n");
sleep(1);
}
}
5