Ce TP donnera lieu à une évaluation. Vous pouvez travailler en binômes.
changer_nom
") : 1h,
superman
" et "kryptonite
") : 1h30,
Le rendu de TP se fera uniquement à travers l'interface web TPLab. Vous devrez fournir une archive .tar
(ou .tar.gz
) contenant
nom_prenom-TP3
" ou "nom1_prenom1-nom2_prenom2-TP3
",
reponses.txt
" pour les réponses aux questions 2, 3, 4 et 7 ; ainsi que vos remarques diverses,
sched
" et un sous-répertoire "pm
",
/usr/src/servers/sched/schedproc.h
/usr/src/servers/sched/schedule.c
/usr/src/servers/sched/main.c
/usr/src/servers/sched/proto.h
/usr/src/servers/pm/misc.c
/usr/src/servers/pm/main.c
/usr/src/servers/pm/proto.h
test
" contenant vos fichiers de tests (pour la question 7).
points importants :
Pierre_Hyvernat-TP3
" ; pour créer l'archive, la ligne de commande est :
MINIX$ tar cvf Pierre_Hyvernat-TP3.tar Pierre_Hyvernat-TP3/
Si ces consignes ne sont pas respectées, l'enseignant se réserve le droit de vous enlever 5 points (ou plus) sur la note finale.
L'archive fournie pour ce TP contient une image QEMU avec un MINIX déjà installé et configuré.
Pour l'utiliser, il suffit de télécharger l'image dans le répertoire /tmp
et la décompresser :
LINUX$ cd /tmp/ LINUX$ mkdir TP3-OS LINUX$ cd TP3-OS LINUX$ wget http://www.lama.univ-savoie.fr/~hyvernat/Enseignement/1617/info524/minix-TP.img.bz2 LINUX$ bunzip2 minix-TP.img.bz2
Pour démarrer MINIX :
LINUX$ qemu-system-i386 -m 256 -net user -net nic,model=rtl8139 -drive file=minix-TP.img,if=ide,media=disk,cache=writeback
Le mot de passe pour l'utilisateur root
est root
, et le mot de passe pour l'utilisateur etu
est etu
. Pour ce TP, vous devrez utiliser le compte administrateur root
.
Si vous êtes sur votre portable et que, au démarrage, Minix bloque sur l'acquisition du réseau (dhcp...
), vous pouvez :
Control-C
root
LINUX$ qemu-system-i386 -m 256 -net user -net nic,model=pcnet -drive file=minix-TP.img,if=ide,media=disk,cache=writeback
netconf
et choisir :
AMD LANCE
(choix 7)
Automatically using DHCP
(choix 1)
MINIX# reboot
Si ça ne fonctionne toujours pas, vous pouvez essayer d'utiliser la commande
LINUX$ qemu-system-i386 -m 256 -net user -net nic,model=e1000 -drive file=minix-TP.img,if=ide,media=disk,cache=writeback
et remplacer AMD LANCE
par Intel PRO/1000 Gigabit
(choix 8).
Il est possible de copier des fichiers de la machine virtuelle vers la machine hôte en utilisant "scp
". Pour cela, il faut que la machine hôte ait un serveur SSH. (C'est la cas pour les machines de l'université...)
À l'intérieur de la machine virtuelle, l'adresse IP de la machine hôte est 10.0.2.2. Pour copier un fichier depuis la machine virtuelle vers votre compte étudiant, vous utiliserez la commande suivante :
MINIX$ scp <chemin_fichier_source_(Minix)> <login_portail>@10.0.2.2:<chemin_fichier_but_(Linux)>
Pour copier un fichier depuis votre compte étudiant vers la machine virtuelle Minix, vous utiliserez
MINIX$ scp <login_portail>@10.0.2.2:<chemin_fichier_source_(Linux)> <chemin_fichier_but_(Minix)>
Vous devrez accepter la clé lors de la première utilisation et rentrer votre mot de passe portail à chaque copie.
Si vous utilisez votre portable et que vous n'avez pas de serveur ssh installé, vous devrez, au choix :
openssh-server
sous Debian ou Ubuntu),
LINUX$ qemu-system-i386 -m 256 -net user,hostfwd=tcp::2232-:22 -net nic,model=rtl8139 -drive file=minix-TP.img,if=ide,media=disk,cache=writeback
LINUX$ scp -P 2232 etu@localhost:<chemin_fichier_source_(Minix)> <chemin_fichier_but_(Linux)>
LINUX$ scp -P 2232 <chemin_fichier_source_(Linux)> etu@localhost:<chemin_fichier_but_(Minix)>
Comme MINIX est un micro-noyau, la plupart des appels systèmes sont gérés par un serveur (par exemple, "mfs
" pour le système de fichiers de MINIX, ou "sched
" pour l'ordonnanceur). Les sources de chaque serveurs sont dans le répertoire /usr/src/servers/
.
Sous MINIX, un appel système doit envoyer un message au serveur correspondant. Chaque message est une structure constituée de :
m_source
" contenant le numéro interne du processus qui doit recevoir le message,
m_type
" contenant le numéro de l'appel système que l'on effectue,
m_u
".
Les numéros d'appels système sont des constantes définies dans le fichier "/usr/include/minix/com.h
".
Suivant les arguments de l'appel système, le champ "m_u
" a un type différent. Par exemple, "m_u
" peut être de type
mess_1
" et contenir trois entiers et trois pointeurs,
mess_2
" et contenir trois entiers, deux entiers longs, un pointeur et 1 petit entier,
mess_3
" et contenir deux entiers, une chaîne de 16 caractères et un pointeur,
Le type des messages ainsi que des synonymes pour accéder aux différents champs sont définis dans le fichier "/usr/include/minix/ipc.h
".
Lorsque le message est prêt, on l'envoie avec la commande "send()
" ou "sendrec()
" (qui permet en plus de recevoir un message en réponse) de prototypes
int send (endpoint_t dest, message *m_ptr); int sendrec (endpoint_t src_dest, message *m_ptr);
Ces fonctions sont définies en langage d'assemblage dans le fichier "/usr/src/lib/libc/arch/i386/rts/_ipc.S
".
On peut également utiliser la commande "_syscall()
" de prototype
int _syscall (endpoint_t _who, int _syscallnr, message *_msgptr);
définie dans le fichier "/usr/src/lib/libc/other/syscall.c
". Modulo un traitement des erreurs, on a :
int _syscall(endpoint_t who, int syscallnr, message *msgptr) { msgptr->m_type = syscallnr; _sendrec(who, msgptr); return(msgptr->m_type); }
Suivez les étapes ci-dessous pour ajouter l'appel système "changer_nom()
" qui permettra à un processus de changer son nom tel qu'affiché par la commande "top
".
Vérifiez que vous comprenez à quoi sert chacune des étapes car vous aurez besoin de refaire le même travail dans la suite du TP. N'hésitez pas à demander de l'aide à votre encadrant de TP.
/usr/include/minix/com.h
" et "/usr/src/include/minix/com.h
"./usr/include/minix/com.h
" contient les numéros des messages utilisé par MINIX et les serveurs. L'appel système que l'on va ajouter fait intervenir le serveur "pm
" (Process Manager) et s'appelle PM_CHANGE_NAME
.
Il faudrait rajouter un #define
dans ces fichiers, mais pour gagner du temps, je l'ai déjà fait.
/usr/src/include/minix/com.h
" car il vous faudrait alors recompiler tout le noyau, ce qui prend du temps...
changer_nom()
" est le process manager dont les sources se trouvent dans "/usr/src/servers/pm/
". Le message correspondant à cet appel système va contenir le nouveau nom du processus dans son champ "m3_ca1
" (chaîne de caractères de taille inférieure à 16).
do_changename()
" dans le fichier "/usr/src/servers/pm/misc.c
". Cette fonction prend en argument un pointeur vers un message (qui contiendra le nouveau nom du processus) et un pointeur vers une structure "mproc
" (définie dans "/usr/src/servers/pm/mproc.h
") :
/*===========================================================================* * do_change_name * *===========================================================================*/ PUBLIC int do_changename(message *m_ptr, struct mproc *mp) { printf("Fonction \"do_changename\" pour le processus %d\n", mp->mp_pid); strncpy(mp->mp_name, m_ptr->m3_ca1, 16); /* Attention, dans "m3_ca1", le dernier caractère est le chiffre "un" comme dans "m3_ca2" ou "m3_ca3" */ return OK; }
do_changename()
" dans le fichier "/usr/src/servers/pm/proto.h
" :
... /* misc.c */ ... _PROTOTYPE( int do_changename, (message *m_ptr, struct mproc *mp) );
main
". Cette fonction contient une instruction "switch(call_nr)
" qui permet de gérer les messages entrants. À l'intérieur de cette fonction, la variable "m_in
" contient le message reçu et la variable "mp
" est un pointeur vers le processus qui a envoyé le message.
Ajoutez un cas dans la fonction main
pour gérer le message "PM_CHANGE_NAME
" :
PUBLIC int main() { ... switch(call_nr) { ... case PM_SETGROUPS_REPLY: ... break; case PM_CHANGE_NAME: printf("serveur PM : changement de nom...\n"); result = do_changename(&m_in, mp); break; ...
# cd /usr/src/servers/pm/ # make
# cd /usr/src/tools # make install
# halt ... c0d0p0s0> off
#include <lib.h> #include <string.h> int changer_nom(const char *s) { message m; int r; strncpy(m.m3_ca1, s, 16); r = _syscall(PM_PROC_NR,PM_CHANGE_NAME,&m); /* PM_PROC_NR est le numéro du serveur "pm" */ return(r); } int main (void) { changer_nom("Bob l'Eponge"); while (1); return 0; }
top
" vous pourrez voir un processus avec le nom "Bob l'Eponge
"...
changer_nom
" dans les bibliothèques du système pour que l'utilisateur n'ait pas à utiliser la fonction bas-niveau "_syscall()
"...
PM_CHANGE_NAME
" ?
ps
" est l'ancien nom. Proposez une explication, si possible avec une justification. (Attention, ne passez pas plus de 5 minutes sur cette question !)
Vous inclurez un fichier "reponses.txt
" à votre archive qui contiendra les réponses a ces questions...
Le but de ce TP est maintenant d'ajouter deux appels systèmes gérés par l'ordonnanceur :
superman()
" qui permet à un processus de devenir prioritaire sur tous les autres,
kryptonite()
" qui permet à un processus superman de redevenir normal.
Avant de commencer ceci, il faut d'abord comprendre quel type d'ordonnanceur MINIX utilise...
Les sources de l'ordonnanceur de MINIX se trouvent dans le répertoire "/usr/src/servers/sched/
". L'ordonnanceur de MINIX est un simple tourniquet avec quantum de temps et priorités. Chaque file ("queue" en anglais) contient des processus de même priorité :
0
pour les processus de priorité maximale,
NR_SCHED_QUEUES - 1
pour les processus de priorité minimale.
L'ordonnanceur choisit toujours le premier processus dans la file de priorité la plus forte (càd la file de numéro le plus petit possible).
La constante NR_SCHED_QUEUES
ainsi que d'autres constantes similaires sont définies dans le fichier "/usr/include/minix/config.h
".
NR_SCHED_QUEUES
".
TASK_Q
") ?
USER_Q
") ?
Pour éviter les famines qui apparaissent avec l'algorithme naïf, chaque processus qui termine son quantum de temps (qui fait donc une utilisation intensive du processeur) va devenir un peu moins prioritaire (sa priorité va diminuer et son numéro de file augmenter de 1). C'est ce que fait la fonction "do_noquantum()
" dans le fichier "/usr/src/servers/sched/schedule.c
".
Par contre, lorsqu'un processus se retrouve bloqué (entrées / sorties) avant que son quantum de temps ne soit écoulé, sa priorité ne change pas : il est simplement remis en fin de file.
Pour éviter que tous les processus se retrouvent avec un priorité minimale (càd dans la file de numéro le plus grand) au bout d'un moment, les processus peuvent regagner un niveau de priorité de temps en temps, à condition que cela ne leur donne pas une priorité supérieure à leur priorité initiale. C'est la fonction "balance_queues()
" du fichier "/usr/src/servers/sched/schedule.c
".
Vérifiez dans le code des fonctions "do_noquantum()
" et "balance_queues()
" que c'est bien ce qu'il se passe et donnez les lignes pertinentes dans votre fichier reponses.txt
.
Nous allons maintenant ajouter les appels systèmes "superman()
et "kryptonite()
". Ces appels systèmes auront l'effet suivant :
superman()
" obtiendra la priorité maximale (0) et lorsqu'il sera préempté en fin de quantum, sa priorité ne baissera pas. Un second appel à "superman()
" ne changera rien...
kryptonite()
" permettra de supprimer l'effet de "superman()
".
Pour accéder aux processus, le noyau n'utilise pas le PID (qui est défini par le process-manager), mais un numéro appelé endpoint qui permet d'obtenir plus facilement au processus dans la table des processus. Vos appels système du process-manager vers l'ordonnanceur devront donc utiliser ce endpoint plutôt que le PID du processus appelant.
Implémentez les deux appels systèmes "superman()
" et "kryptonite()
" en suivant les consignes ci-dessous.
Consignes
schedproc
" définie dans "/usr/src/servers/sched/schedproc.h
" en y ajoutant un champs "superman
". (Vous pouvez aussi utiliser le champs existant "flags
" où seul le bit "IN_USE
" est utilisé par Minix, mais c'est un peu moins facile...)
superman
" des "schedproc
" dans la fonction "do_start_scheduling()
" du fichier "schedule.c
" : un processus hérite de l'état de son père et un nouveau processus ne sera pas superman.
do_noquantum
" pour qu'elle ne baisse pas la priorité des supermen.
do_superman(message *m_ptr)
" et "do_kryptonite(message *m_ptr)
" dans le fichier "/usr/src/servers/sched/schedule.c
". Le message pointé par "m_ptr
" contiendra le endpoint du processus que l'on veut modifier.
struct schedproc *
" à partir d'un "endpoint
", il faut utiliser :
int proc_nb; struct schedproc *rmp; int endpoint = m_ptr->m1_i1; /* on récupère le "endpoint" */ sched_isokendpt(endpoint, &proc_nb); /* on le convertit en indice dans la table des processus */ rmp = &schedproc[proc_nb]; /* on récupère un pointeur vers le "schedproc" correspondant */
rmp
", il faut faire :
schedule_process(rmp);
do_nice
" fait tout cela, avec de la gestion d'erreurs en plus. Vos fonctions "do_superman
" et "do_kryptonite
" pourront être plus simple et ne pas gérer les cas problématiques...)
proto.h
".
do_superman
" et "do_kryptonite
" dans la fonctions "main
" du fichier "/usr/src/servers/sched/main.c
". Les numéros des appels systèmes correspondants (déclarés dans le fichier "/usr/include/minix/com.h
") sont :
SCHEDULING_SUPERMAN
"
SCHEDULING_KRYPTONITE
"
CHANGE_NAME
", les messages utilisent le champ "m1_i1
" pour stocker le numéro du processus appelant.
Vous aurez probablement modifié les fichiers suivants :
/usr/src/servers/sched/schedproc.h
/usr/src/servers/sched/schedule.c
/usr/src/servers/sched/main.c
/usr/src/servers/sched/proto.h
Si vous voulez modifier un autre fichier, demandez confirmation auprès de votre encadrant de TP.
On ne peut malheureusement pas envoyer de message directement à l'ordonnanceur : il faut passer par le process manager...
Ajouter un nouveau message "PM_SUPERMAN
" au process manager, en suivant les consignes ci dessous.
PM_SUPERMAN
" sera traité (comme "PM_CHANGENAME
") par le process manager. Le message contiendra un unique champ "m1_i1
" :
SCHEDULING_SUPERMAN
" à l'ordonnanceur,
SCHEDULING_KRYPTONITE
" à l'ordonnanceur.
mp
(variable définie dans la fonction main
) de type struct mproc
. Attention, cette structure n'est pas la même que la structure de processus définie dans sched/schedproc.h
.
do_superman
" du serveur process manager devra donc faire un appel système vers le serveur scheduler. Le numéro du serveur scheduler est "SCHED_PROC_NR
"...
Une fois ce dernier appel système défini, l'utilisateur pourra alors faire les appels système "superman
" et "kryptonite
" de la manière suivante :
#include <minix/ipc.h> #include <lib.h> int kryptonite(void) { message m; int r; m.m1_i1 = -1; r = _syscall(PM_PROC_NR,PM_SUPERMAN,&m); return r; } int superman(void) { message m; int r; m.m1_i1 = 1; r = _syscall(PM_PROC_NR,PM_SUPERMAN,&m); return r; }
et utiliser "superman()
" pour pouvoir passer un programme C en mode superman.
Vous devrez probablement modifier les fichiers suivants :
/usr/src/servers/pm/misc.c
/usr/src/servers/pm/main.c
/usr/src/servers/pm/proto.h
Si vous voulez modifier un autre fichier, demandez confirmation auprès de votre encadrant de TP.
Écrivez des petits programmes de test pour regarder ce qu'il se passe dans les conditions suivantes :
Commentez.