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