IUT Nancy-Charlemagne Université de Nancy 2 Syst`eme Automate

Transcription

IUT Nancy-Charlemagne Université de Nancy 2 Syst`eme Automate
I.U.T. Nancy-Charlemagne
Université de Nancy 2
Système Automate Processus – D.U.T. Informatique S3
Mardi 2 novembre 2010
Durée de l’épreuve : 2 heures.
Barème donné à titre indicatif.
Dominique Colnet
Bernard Mangeol
Emmanuel Nataf
Seules les notes manuscrites prises durant les cours sont autorisées (ni photocopies, ni listing,
ni documents imprimés). Lire complètement le sujet avant de commencer.
1
1.1
Copie numéro 1 sur une feuille séparée (7 points)
L’étoile (*) du shell
Soit l’extrait de session sous l’interprète de commande bash suivant :
% ls
projet1 projet2
% echo ici *.x la
ici *.x la
% touch f.x
% ls
f.x projet1 projet2
% echo ici *.x la
ici f.x la
% echo ici ’*.x’ la
ici *.x la
% find . -name *.java
projet1/titi.java
projet2/toto.java
% touch plop.java
% find . -name *.java
plop.java
%
Sachant que la commande touch a pour effet de créer un fichier, expliquez ce qui se passe. Pourquoi les
fichiers titi.java et toto.java ne sont ils plus affichés par la commande find ? Comment faut il corriger
la commande find ?
1.2
Renommer tous les .sh en .bash
Expliquez pourquoi une commande renomme qui voudrait renommer l’ensemble des fichiers .sh en .bash ne
peut être appelée depuis le shell par la commande suivante :
$ renomme *.sh *.bash
La séquence suivante est une bonne indication pour répondre :
$ ls
tac.sh tic.sh toc.sh
$ echo t*
tac.sh tic.sh toc.sh
1
$ touch plic.bash toc.bash
$ ls
plic.bash tac.sh tic.sh toc.bash toc.sh
$ echo *.sh *.bash
tac.sh tic.sh toc.sh plic.bash toc.bash
$ echo pulp.*
pulp.*
Quelle solution proposez-vous pour les arguments d’un bon script de renomage ? Après avoir expliqué la
signification des arguments que vous aurez choisi, donnez la programmation de votre script.
1.3
Allocation mémoire (malloc) et restitution de mémoire (free).
Soit le programme C suivant :
#include <stdio.h>
#include <stdlib.h>
int main(){
char* p0; char* p1; char* p2; char* p3;
p0 = malloc(1); p1 = malloc(2); p2 = malloc(4); p3 = malloc(8);
printf("p0=%p p1=%p p2=%p p3=%p\n", p0, p1, p2, p3);
free(p3);
free(p2);
free(p1);
free(p0);
p3
p2
p1
p0
=
=
=
=
NULL;
NULL;
NULL;
NULL;
p0 = malloc(1); p1 = malloc(2); p2 = malloc(4); p3 = malloc(8);
printf("p0=%p p1=%p p2=%p p3=%p\n", p0, p1, p2, p3);
}
Soit l’exécution du programme précédent sur une machine donnée :
$ ./a.out
p0=0x8689008 p1=0x8689018 p2=0x8689028 p3=0x8689038
p0=0x8689008 p1=0x8689018 p2=0x8689028 p3=0x8689038
Rappel : le format %p permet dans un print d’afficher la valeur d’un pointeur (et non pas ce qui est pointé).
Questions :
1. La mémoire allouée par malloc est-elle prise dans la pile ?
2. D’après l’exemple d’exécution donné précédemment, pensez vous que free fonctionne ?
3. Comparez l’allocation 1*malloc(1000) avec l’allocation 1000*malloc(1). La consommation mémoire
est-elle identique ? Donnez approximativement la taille mémoire utilisée dans les deux cas.
4. Quels sont les raisons de cette apparent gachis de mémoire ?
2
2
Copie numéro 2 sur une feuille séparée (7 points)
Lisez toutes les indications données à la fin de cet exercice avant de répondre aux questions. Un automate
est représenté par le code C suivant :
int main (int argc, char*argv[]) {
char *input=NULL; int state; int caractereCourant; int iCourant = 0;
int valeur = 0; int masque = 0;
if (argc <= 1) { fprintf(stderr, "Usage: %s <ChaineBinaire>\n", argv[0]); exit(2); }
input = argv[1];
#define S_DEPART 0
#define S_BIT_1
1
#define S_BIT_0
2
#define S_FINI
3
#define S_ERREUR 4
state = S_DEPART;
while ( state < S_FINI ) {
caractereCourant = input[iCourant];
iCourant++;
switch (state) {
case S_DEPART: // ---------------------------switch ( caractereCourant ) {
case ’0’: state = S_BIT_0; break;
case ’1’: state = S_BIT_1; break;
default: state = S_ERREUR;
}
break;
case S_BIT_1: // ----------------------switch ( caractereCourant ) {
case ’0’: state = S_BIT_0; break;
case ’1’: break;
case ’\0’: state = S_FINI; break;
default: state = S_ERREUR; break;
}
break;
case S_BIT_0: // ----------------------switch ( caractereCourant ) {
case ’0’: break;
case ’1’: state = S_BIT_1; break;
case ’\0’: state = S_FINI; break;
default: state = S_ERREUR;
}
break;
}
}
if ( state == S_ERREUR) { fprintf(stderr, "Pas du binaire: \"%s\".\n", argv[1]); exit(2); }
if ( state == S_FINI) fprintf(stdout, "Valeur = %d\n", valeur);
}
1. Dessinez l’automate qui correspond au programme précédent.
2. Donnez une expression régulière équivalente à l’automate que vous avez dessiné.
3. Ajoutez sur le schéma de votre automate les instructions C permettant de calculer dans la variable
3
valeur qui est de type int la valeur de l’entier qui correspond à la chaine binaire donnée en complément
à deux.
Indications
Voici ce que doit donner l’automate sur lequel on a ajouté les intructions de conversion :
$ ./automate
Valeur = 7
$ ./automate
Valeur = 6
$ ./automate
Valeur = 5
$ ./automate
Valeur = 1
$ ./automate
Valeur = 0
$ ./automate
Valeur = -1
$ ./automate
Valeur = -2
$ ./automate
Valeur = -7
$ ./automate
Valeur = -8
$ ./automate
Valeur = -1
$ ./automate
Valeur = 127
0111
0110
0101
0001
0000
1111
1110
1001
1000
11111111
01111111
Il est possible de faire le calcul de la variable valeur en préparant correctement cette variable ainsi que le
contenu de la variable masque. Il n’est pas nécessaire d’ajouter de nouvelles déclarations de variables (utilisez
uniquement valeur et masque).
L’instruction C ”masque = -1;” positionne tous les bits de l’emplacement mémoire correspondant à 1.
L’instruction C ”masque = masque << 1;” décale vers la gauche tous les bits avec injection d’un bit à 0
dans l’emplacement de poids faible.
L’instruction C ”valeur = valeur | masque;” effectue un ou bit à bit entre valeur et masque.
L’instruction C ”valeur = (valeur << 1) + 1;” est équivalente à multiplier valeur par deux et ajouter
1 ensuite.
3
3.1
Copie numéro 3 sur une feuille séparée (6 points)
Création d’un processus fils avec fork
Soit le code C suivant :
int main (int argc, char* argv[]) {
int compteur = 3;
while ( compteur > 0 ) {
if ( fork() > 0 ) {
printf("coucou\n");
}
compteur--;
4
}
}
1. Combien de fois le programme précédent imprime-t-il "coucou" ?
2. Quel est le nombre de processus créés par le programme précédent ?
3. Le prompt est-il toujours affiché après le dernier "coucou" ?
3.2
Attente de terminaison du fils avec waitpid
Soit le code C suivant :
int main (int argc, char* argv[]) {
int compteur = 3;
int pid = fork();
compteur--;
if ( pid > 0 ) {
waitpid(pid, NULL, 0);
compteur--;
printf("compteur = %d\n", compteur);
pid = fork();
if ( pid > 0 ) {
compteur--;
printf("compteur = %d\n", compteur);
} else {
compteur--;
}
} else {
printf("compteur = %d\n", compteur);
compteur--;
}
return 0;
}
1. Qu’imprime le programme précédent ?
2. Quel est le nombre de processus créés par le programme précédent ?
3. Le prompt est-il toujours affiché après le dernier print ?
4. Qui attend qui lors de l’appel à waitpid ? Est-ce le père qui attend son fils ou bien est-ce le fils qui
attend son père ?
3.3
Remplacement du code avec execlp
Soit le code C suivant :
int main (int argc, char* argv[]) {
char *cmd = "echo coucou 1";
execlp("/bin/sh", "sh", "-c", cmd, NULL) ;
printf("coucou 2\n");
return 0;
}
5
1. Qu’imprime le programme précédent ?
2. Quel est le nombre de processus créés par le programme précédent ?
6