|
|
Institut National des
Télécommunications
Télécom INT 2è
année
TP Noté CSC4508/M2 du 25/05/07
Modalités
Durée : 3 heures
Tous documents autorisés.
Les questions sont indépendantes les unes
des autres. Aussi, n'hésitez pas à lire tout le
sujet avant de commencer pour déterminer l'ordre dans lequel
vous souhaitez traiter les questions.
Le barême est donné à titre indicatif
des poids
entre les différentes questions.
La « livraison » de votre travail
en fin de TP
noté se fera par remise de votre copie à
l'enseignant et
par
remontée sous Moodle (rubrique « TP
noté de 3
heures ») du fichier d'extension tgz
constitué de
la manière suivante :
cd votreRepertoireDeTravailPourCSC4508M2 tar cvfz $USER.tgz TPNote2007
Préparation
cd votreRepertoireDeTravailPourCSC4508M2 cp ~simatic/Cours/CSC4508/tPNote2007.tgz . tar xvfz tPNote2007.tgz cd TPNote2007
Question 1 : Files d'attente de
sémaphore et priorité de processus (4 points)
Pour chacune des questions ci-dessous, répondez sur votre
copie
ou
bien dans le fichier Q1/reponse1.txt (dans ce
dernier
cas, indiquez sur votre
copie « Réponse dans Q1/reponse1.txt »
).
Chaque objet système sémaphore comprend une
file d'attente des
processus
qui ont fait une opération P() et
attendent qu'un autre
processus fasse une opération V().
À l'aide de deux expériences, cet exercice
cherche à prouver que cette file d'attente
ne prend en compte ni les priorités statiques, ni
les priorités dynamiques des processus
qui sont en attente sur ce sémaphore. Dit autrement, en cas
d'opération V(), ce
n'est pas le processus en attente le plus prioritaire (statiquement ou
dynamiquement) qui
est réveillé en premier.
Question 1.1 : Expérience avec des
processus à
priorités statiques différentes (2 points)
On décide de
s'appuyer sur le code du fichier Q1/testSemaphoreEtPrioriteStatique.c
listé ci-dessous :
/*************************************/
/*
testSemaphoreEtPrioriteStatique.c */
/*************************************/
#include
<stdlib.h>
#include
<unistd.h>
#include
<stdio.h>
#include
<sched.h>
#include
<sys/types.h>
#include
<sys/wait.h>
#include
<sys/ipc.h>
#include
<sys/sem.h>
#define
FATAL(msg) {\
char
m[128]; \
sprintf(m, "%s in %s:%d:%s", msg, __FILE__, __LINE__, __func__); \
perror(m); \
abort(); \
}
/**********************************************************************/
/* Code de P
et
V
*/
/**********************************************************************/
void P(int
semId){
struct sembuf op;
op.sem_num = 0;
op.sem_op = -1;
op.sem_flg = 0;
if
(semop(semId, &op, 1) == -1){
FATAL("semop");
}
}
void V(int
semId){
struct sembuf op = {0, 1, 0};
if
(semop(semId, &op, 1) == -1){
FATAL("semop");
}
}
/**********************************************************************/
/* Code
execute par les processus
enfant
*/
/**********************************************************************/
void
codeEnfant(int numEnfant, int semId)
{
struct sched_param sp;
/*
Section de Code 1 (SC1) */
sp.sched_priority = numEnfant;
if
(sched_setscheduler(0,SCHED_FIFO,&sp) < 0) {
FATAL("sched_setscheduler");
}
/*
Section de Code 2 (SC2) */
sleep(numEnfant);
/*
Section de Code 3 (SC3) */
P(semId);
/*
Section de Code 4 (SC4) */
printf("Enfant %d s'est reveille !\n", numEnfant);
exit(EXIT_SUCCESS);
}
/**********************************************************************/
/* Programme
principal
*/
/**********************************************************************/
int main(int
argc, char* argv[])
{
int
status;
key_t cle;
int
i;
int
semId;
/*
Section de Code 5 (SC5) */
cle
= ftok("./testSemaphoreEtPrioriteStatique.c", '0');
if
(cle < 0) {
FATAL("ftok");
}
semId = semget(cle, 1, IPC_CREAT|0666);
if
(semId < 0) {
FATAL("semget");
}
if
(semctl(semId, 0, SETVAL, 0) < 0) {
FATAL("semctl");
}
/*
Section de Code 6 (SC6) */
for
(i=1 ; i<4 ; i++){
switch (fork()) {
case -1:
FATAL("fork");
break;
case 0:
codeEnfant(i, semId);
break;
default:
break;
}
}
/*
Section de Code 7 (SC7) */
sleep(4);
/*
Section de Code 8 (SC8) */
for
(i=1 ; i<4 ; i++){
V(semId);
if (wait(&status) < 0){
FATAL("wait");
}
}
/*
Section de Code 9 (SC9) */
if
(semctl(semId, 0, IPC_RMID, 0) < 0){
FATAL("semctl");
}
return EXIT_SUCCESS;
}
Décrivez l'expérience
réalisée avec ce code.
A l'exécution, on obtient l'affichage
suivant :
Enfant 1 s'est reveille !
Enfant 2 s'est
reveille !
Enfant 3 s'est
reveille !
Qu'en concluez-vous ?
Question 1.2 : Expérience avec des
processus à
priorités dynamiques différentes (2 points)
Modifiez le fichier Q1/testSemaphoreEtPrioriteDynamique.c (qui,
pour l'instant, est une copie conforme de Q1/testSemaphoreEtPrioriteStatique.c )
pour adapter l'expérience de la question 1.1 au cas de
processus avec des priorités dynamiques.
Compilez Q1/testSemaphoreEtPrioriteDynamique.c
Exécutez Q1/testSemaphoreEtPrioriteDynamique
Sur
votre copie ou bien dans le fichier Q1/reponse1.txt ,
indiquez l'affichage obtenu et votre conclusion.
Question 2 : Des threads pour un moteur (4
points)
Pour chacune des questions ci-dessous, répondez sur votre
copie
ou
bien dans le fichier Q2/reponse2.txt (dans ce
dernier
cas, indiquez sur votre
copie « Réponse dans Q2/reponse2.txt »).
Le but du programme Q2/indexation.c
est de
créer un moteur d'indexation parcourant une arborescence de
documents. Voici son listing :
#include
<stdio.h>
#include
<stdlib.h>
#include
<pthread.h>
#include
<sys/types.h>
#include
<dirent.h>
#include
<assert.h>
#include
<unistd.h>
#include
<string.h>
#define
NBMAXDOCS 20
#define
MAXDIRNAME 80
int
nbDocIndexes = 0;
int nbThreads
= 0;
char
docname[MAXDIRNAME];
char
dirname[MAXDIRNAME];
void
*indexer_document(void *arg)
{
char *docname = (char *)arg;
printf("Debut indexer document =%s\n", docname);
sleep(3);
printf("Fin indexer document =%s\n", docname);
nbDocIndexes ++;
pthread_exit(NULL);
}
void
indexer_repertoire(DIR *dir)
{
struct dirent *entry;
pthread_t thread;
int
rc, i = 0;
do {
entry = readdir(dir);
if (entry == NULL)
break;
strcpy(docname, entry->d_name);
nbThreads ++;
rc = pthread_create(&thread, NULL, indexer_document, docname);
if (rc){
perror("pthread_create");
exit(EXIT_FAILURE);
}
i++;
}
while (i < NBMAXDOCS);
if
(i == NBMAXDOCS){
fprintf(stderr, "Indexation incomplete: le nombre de documents est
superieur a NBMAXDOCS (%d)\n", NBMAXDOCS);
}
}
int main(int
argc, char **argv){
DIR
*directory;
int
i;
if
(argc < 2){
fprintf(stderr, "Usage: %s listeRepertoires\n", argv[0]);
exit(EXIT_FAILURE);
}
for
(i = 1; i < argc; i++){
assert(strlen(argv[i]) < MAXDIRNAME);
strcpy(dirname, argv[i]);
directory = opendir(dirname);
if (directory == NULL){
perror("opendir");
exit(EXIT_FAILURE);
}
indexer_repertoire(directory);
closedir(directory);
}
printf("Le nombre de threads crees est = %d\n", nbThreads);
printf("Le nombre de documents indexes est = %d\n", nbDocIndexes);
exit(EXIT_SUCCESS);
}
Question 2.1 : Analyse du code (2 points)
- Combien de
threads sont
créés?
- Quel est le rôle de chaque
thread ?
- Combien de
threads font
appel à la fonction readdir() ?
- Précisez pour chacune des variables
globales si ces variables sont accédées par un ou
plusieurs
threads . Expliquez.
Question 2.2 : Débuggage du code
Ce code ne s'exécute pas correctement. Il contient 4
erreurs. Donnez
chacune des erreurs et proposez un principe de correction (sur votre
copie ou dans
le fichier Q2/reponse2.txt
; NB : ne modifiez pas le fichier Q2/indexation.c
: il ne sera pas corrigé !).
Question 3 : BoursINT (garanti sans ail, ni fines
herbes ;-) ) (12 points)
Pour chacune des questions ci-dessous, répondez sur votre
copie
ou
bien dans le fichier Q3/reponse3.txt (dans ce
dernier
cas, indiquez sur votre
copie « Réponse dans Q3/reponse3.txt »).
BoursINT, le club Bourse de l'INT, a décidé
d'automatiser
sa surveillance des différentes salles de marché
avec lesquelles il travaille.
L'idée est d'avoir une application constituée de
N+1 tâches et d'un tableau t de N doubles
partagé entre les
N+1 tâches :
- N tâches (nommées Si
[comme
Surveillant] dans la suite) sont chargées de surveiller
chacune
une salle de marché (Paris, Londres, New York...). Chaque
tâche Si
calcule plus ou moins rapidement le résultat de la
séance (sous
la
forme d'un nombre réel indiquant la tendance -en pourcentage
de hausse
ou de
baisse- des valeurs dans cette salle). Une fois son calcul
effectué, elle écrit ce
résultat dans la iè
case du tableau t.
- Quand les N tâches de surveillance ont
chacune
écrit leur case de tableau,
elles préviennent la N+1è
tâche
(nommée A [comme Afficheur] dans la suite) qu'elle doit
s'exécuter pour afficher la moyenne des valeurs du tableau t.
Les tâches Si se mettent
alors en attente jusqu'à ce que la tâche A leur
indique qu'elle a
terminé son calcul de moyenne. Elles redémarrent
alors leur surveillance.
Question 3.1 : Identification des paradigmes
(1,5
point)
Indiquez les différents types de problème de
synchronisation auxquels vous êtes
confrontés pour
implémenter
cette application (exclusion mutuelle, cohorte,
producteur/consommateur...) en précisant, pour chaque
problème,
ses acteurs.
Question 3.2 : Conception à l'aide de
sémaphores (4
points)
Le club BoursINT a commencé à écrire
les algorithmes :
Variables globales
==================
// Tableau permettant la communication entre les tâches
// Surveillant et la tâche Afficheur
t Tableau[1..N] de doubles
// Variables associées aux paradigmes de type Signal
Sémaphore topAfficheur initialisé à ? // A completer
Sémaphore topSurveillant initialisé à ? // A completer
// A completer (eventuellement)
Tache Surveillanti
==================
faire
t[i]=surveiller(i)
// A completer
tant que VRAI
Tache Afficheur
===============
faire
P(topAfficheur)
Ecrire "Moyenne = ", moyenne(t)
pour i=1:Entier à N faire
V(topSurveillant)
fpour i
tant que VRAI
Compléter les déclarations de variables globales et le(s)
algorithme(s) (en particulier, tous les commentaires « // A completer » doivent disparaître) pour répondre au problème posé.
NB :
- Si jamais vous avez besoin d'autres sémaphores,
essayez de vous limiter à un
seul sémaphore supplémentaire.
- Vous pouvez modifier l'algorithme de la tâche Afficheur si vous en éprouvez le besoin.
- Sous sa forme actuelle, l'algorithme de la tâche Afficheur
comprend une anomalie si la file d'attente associée aux
sémaphores n'est pas FIFO. Ne vous préoccupez pas de
cette anomalie dans cette question (elle sera traitée à
la question 3.4)
Question 3.3 : Codage (5 points)
Dans le fichier Q3/boursINT.c,
implémentez les algorithmes de la question 3.2, en
prenant N
égal à 9 et en utilisant des processus enfants.
Pour les tâches Si, la
fonction surveiller
est fournie dans le fichier (son résultat est à
stocker dans la case concernée du tableau t).
Seront
notés pour cette question :
- Le fait que, à l'exécution, le
code répond correctement
au problème posé,
- La clarté du code,
- Le fait que le code compile (sans warning),
- Le fait que le retour de chaque appel
système, s'il
y en
a un, est testé et géré (traitement
d'erreur),
- Le fait que les ressources système
réservées pendant l'exécution du
programme
sont libérées à la fin de
l'exécution.
Question 3.4 : Correction de l'anomalie de la question
3.2 (1,5
points)
Les algorithmes conçus à la question 3.2 ne
fonctionnent
correctement que si on suppose que les sémaphores ont une
liste
d'attente associée de type FIFO.
Donnez un exemple de ce qui pourrait arriver avec l'algorithme de la
question 3.2 si la liste d'attente n'était pas FIFO.
Expliquez notamment le dysfonctionnement résultant.
En utilisant au plus 4 sémaphores,
écrivez des
algorithmes qui fonctionnent même si les listes d'attente
associées au sémaphore ne sont pas FIFO.
Question (bonus) 3.5 : Reprise de la question 3.4 avec des objets de synchronisation threads (bonus de 2
points)
Reprendre le problème de la question 3.4, à
l'aide d'objets de synchronisation thread
(mutex avec les opérations pthread_mutex_lock
et pthread_mutex_unlock
et conditions avec les opérations pthread_cond_wait ,
pthread_cond_signal
et éventuellement pthread_cond_broadcast ).
|