Skip to topic | Skip to bottom
Home
Linfo
Linfo.CTPSurProcessusr1.6 - 10 Jan 2005 - 17:07 - OlivierDalletopic end

Start of topic | Skip to actions

TP sur Getopt, Champs de bits et Processus

A propos des champs de bits

Il est courant que dans un programme on ait besoin de mémoriser de multiples informations d'état, par exemple pour indiquer que telle ou telle option du programme est active. Une solution simple consiste à utiliser un entier comme booléen pour chaque information d'état :

int option_truc = 1;    /* activer le mode truc */
int option_bidule = 0;  /* désactiver le mode bidule */
...

Cette technique est naive car si elle semble simple au premier abord, le fait d'utiliser un grand nombre de variables d'état peut serieusement compliquer les choses. Imaginez par exemple que dans un programme écrit de la façon précédente vous ayez besoin d'appeler une fonction en lui passant en paramètre l'ensemble des informations d'état (car vous voulez éviter d'utiliser des variables globales). En choisissant la méthode précédente, vous seriez obligé de prévoir autant de paramètres pour votre fonction qu'il y a d'informations d'état :

int une_fonction(int option truc, int option_bidule, int option_machin, int option_chose)
{
  ...
}

A l'évidence cette technique est d'autant plus lourde que le nombre de paramètres est important :

  • Elle gaspille inutilement la mémoire en utilisant à chaque fois un entier pour ne stocker qu'une valeur booléenne
  • Elle oblige à manipuler un gand nombre de variables avec tous les risques et contraintes que cela comporte.

Une solution couramment adoptée consiste donc à regrouper l'ensemble des valeurs booléennes dans un unique entier, sur le modèle des registres d'état, comme l'illustre la figure suivante où on utilise les premiers bits d'un entier pour stocker 8 valeurs booléennes.

choix_opt.jpg

Dans ce cas, on trouve généralement deux méthodes pour manipuler le pseudo-registre :

  • Définir des macros à l'aide des opérateurs binaires de façon à manipuler indépendamment chacun des bits du pseudo-registre
  • Utiliser des champs de bits

La technique des macros est très courante, mais elle assez lourde car elle implique généralement la définition de plusieurs macros pour chaque valeur booléenne :

  • une macro pour tester la valeur d'un bit particulier
  • une ou plusieurs macros pour fixer la valeur d'un bit (par exemple une macros pour mettre à vrai un booléen, et une autre pour mettre la valeur de ce booléen à faux).

Dans le cas du schéma précédent, la tchnique des macros aboutit à des choses de la forme suivante :

#define RESET_ALL(registre)  (registre = 0)
#define SET_ALL(registre) (registre = ~0)  /* preferable à 0xFFFF...FFFF qui dépend de l'arch. CPU */

#define PID_SET(registre)  (registre |= 0x1)
#define PID_UNSET(registre) (registre ^= 0x1)
#define PID_VALUE(registre) ((registre) & 0x1)

Idem pour ppid, uid, gid, egid ...

La technique des champs de bits permet d'aboutir exactement au même résultat, mais en supprimant le besoin de devoir recourrir à des macros, chacun des bits de notre pseudo-registre pouvant être manipulé de façon totalement indépendante des autres par de simples affectations. De façon à pouvoir manipuler simultanément tous les bits, en particulier pour pouvoir les mettre simultanément à 1 ou à 0, on combine généralement la définition d'une structure champs de bits à celle d'un entier normal, au sein d'une union.

Une telle déclaration de type peut ensuite s'utiliser de la façon suivante, où registre est une variable de type union, registre.value un entier représentant simultanément tous les bits de registre, et registre.s la structure définissant le champ de bits :

union u_registre registre;
registre.value = 0;   /* metre tous les bits à 0 */
registre.s.pid = 1;   /* mettre à 1 le bit pid */
registre.s.grp = 1;   /* mettre à 1 le bit grp */

Exercices

Exercice 1. Ecrivez un programme qui définit le type union u_registre utilisé dans l'exemple précédent, et permettant de manipuler 8 booléens regroupés dans un entier non signé tel que présenté dans la figure suivante (la même qu'au dessus) :

choix_opt.jpg

Ajoutez à votre programme une fonction affiche_infos(const union u_registre infos) qui affiche la valeur entière infos.value puis successivement les valeurs de chacun des booléens de infos.s .

Exercice 2. Modifiez le programme précédent afin qu'il utilise la fonction getopt pour reconnaitre les options suivantes (et affiche un message d'erreur en cas d'option inconnue) :

prog [-h] [-pPuUgGrc] [-v VAR [-v VAR ...]] [--]

Les options reconnues devront être traitées de la façon suivante :

  • Construire et initialiser une variable de type union u_registre indiquant la présence des options suivantes :
    • -p -> s.pid
    • -P -> s.ppid
    • -g -> s.gid
    • -G -> s.egid
    • -u -> s.uid
    • -U -> s.euid
    • -r -> s.grp
    • -c -> s.cwd
  • Afficher un message d'aide sur la sortie satndard d'ereeur et terminer immédiatement avec le code d'erreur 1 lorsque l'option -h est trouvée
  • Ne pas traiter comme des options les éventuels paramètres trouvés après l'option --
  • Pour chaque option -v VAR, mémoriser dans un (unique) tableau de chaînes la chaîne de caractères VAR (-v "truc" => mémoriser "truc", -v "bidule" => mémoriser "bidule", ...).

Exercice 3. Modifiez la fonction affiche_infos pour qu'elle affiche les informations du processus correspondant au nom de chaque booléen de l'union : le pid, le ppid, le gid, ... (cwd signifiant "Current Working Directory" = le répertoire courant). Attention : pour obtenir chacune de ces informations, vous devrez faire appel à une fonction spécifique. Relisez votre cours ...

Créez une nouvelle fonction affiche_vars qui prend en paramètre un tableau de chaînes et recherche dans l'environnement du processus la valeur de chacune des variables dont les noms sont donnés dans le tableau. La fonction devra afficher le contenu de chaque variable sur la sortie standard avec un message simple de la forme "VAR=valeur".

Testez plusieurs fois votre programme dans différentes situations, par exemple les suivantes :

[dalle@zorglub TP_getopt]$ ./choix_opt -P -U -r
ppid: 4795
euid: 500
id groupe: 5783
[dalle@zorglub TP_getopt]$ ./choix_opt -P -U -Z
Unknown option  -Z .
Usage: ./choix_opt [-h] [-pPuUgGrc] [-v VAR [-v VAR ...]] [--]
[dalle@zorglub TP_getopt]$ ./choix_opt -P -U -- -Z
ppid: 4795
euid: 500
[dalle@zorglub TP_getopt]$ ./choix_opt -P -U -v PATH -c -g -v DISPLAY
ppid: 4795
euid: 500
gid: 500
pwd: /home/dalle/Enseign/2004-2005/L3O/TP_getopt
PATH=/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/home/dalle/bin
DISPLAY=:0.0
[dalle@zorglub TP_getopt]$ ./choix_opt -P -U -v PATH -h -c -g -v DISPLAY
Usage: ./choix_opt [-h] [-pPuUgGrc] [-v VAR [-v VAR ...]] [--]

Le corrigé de l'exercice 3.

Exercice 4. Modifiez à nouveau le programme précédent de façon à créer un nouveau processus fils (à l'aide de la fonction fork() ). Faites en sorte qu'à l'issue du fork() chaque processus se présente ("XXXX: je suis le père", "XXXX: je suis le fils 1" ou XXXX est le numéro du processus courant) puis appelle successivement les deux fonctions affiche_infos et affiche_vars.

Pour bien comprendre ce qui se passe, modifiez les fonctions affiche_infos et affiche_vars pour que chaque ligne affichée commence avec le numéro du processus courant.

Exercice 5. Modifiez une dernière fois le programme précédent pour obtenir la création de deux nouveaux processus :

  • Un second fils du processus père
  • Un fils du premier fils (donc un petit fils !)

Comme précédemment, chaque processus devra se présenter immédiatement après le fork puis appeler les deux fonctions affiche_infos et affiche_vars.

Puis, avant de se terminer, chaque processus devra rechercher dans son environnement une variable DELAI_XXX qui lui est destinée :

  • DELAI_P destinée au processus père,
  • DELAI_F1 destinée au fils1
  • DELAI_F2 destinée au fils2
  • DELAI_PF1 destinée au petit fils

Cette variable sera supposée contenir un entier représentant un nombre de secondes pendant lequel le processus devra s'endormir, à l'aide la fonction sleep. (Si la variable est absente, le processus devra se terminer avec un message d'erreur.)

Afin d'alléger l'affichage, une fois réveillé, chaque processus devra remettre à zéro le champ de bit, puis mettre à 1 les booléen pid et ppid avant d'appeler à nouveau la fonction affiche_infos puis de se terminer.

Testez plusieurs fois votre programme avec chacune des combinaisons suivantes de variables d'environnement :

  • DELAI_P=1, DELAI_F1=1, DELAI_F2=1, DELAI_PF1=1
  • DELAI_P=1, DELAI_F1=3, DELAI_F2=3, DELAI_PF1=1
  • DELAI_P=1, DELAI_F1=1, DELAI_F2=1, DELAI_PF1=3
  • DELAI_P=3, DELAI_F1=1, DELAI_F2=1, DELAI_PF1=1

Analysez les résultats. Ceux-ci devraient vous permettre d'observer les choses suivantes :

  • La création de processus zombies
  • la création de processus orphelins

CONSEIL : Songez à faire des fonctions spécifiques pour chaque processus, contenant chacune les instructions à exécuter par chacun d'eux à la sortie du fork. Votre programme n'en sera que plus clair...

Un exemple de trace d'exécution :

[dalle@zorglub TP_getopt]$ ./multi_proc -P -U -c -g -v DELAI_P -v DELAI_F1 -v DELAI_F2 -v DELAI_PF1
 5956: ppid= 4795
 5956: euid= 500
 5956: gid= 500
 5956: pwd= /home/dalle/Enseign/2004-2005/L3O/TP_getopt
 5956: DELAI_P=1
 5956: DELAI_F1=1
 5956: DELAI_F2=1
 5956: DELAI_PF1=1
 5956: Je suis le pere
 5956: pid= 5956
 5956: ppid= 4795
 5956: je m'endors 1 sec.
 5957: Je suis le fils1
 5957: pid= 5957
 5957: ppid= 5956
 5957: je m'endors 1 sec.
 5958: Je suis le fils2
 5958: pid= 5958
 5958: ppid= 5956
 5958: je m'endors 1 sec.
 5959: Je suis le petit-fils
 5959: pid= 5959
 5959: ppid= 5957
 5959: je m'endors 1 sec.
 5957: fils1 se reveille
 5957: pid= 5957
 5957: ppid= 5956
 5958: fils2 se reveille
 5958: pid= 5958
 5958: ppid= 5956
 5959: petit-fils se reveille
 5959: pid= 5959
 5959: ppid= 1
 5956: pere se reveille
 5956: pid= 5956
 5956: ppid= 4795
[dalle@zorglub TP_getopt]$

multi_proc.c: Corrige de l'exercice 5

-- OlivierDalle - 11 Dec 2004


to top

I Attachment sort Action Size Date Who Comment
choix_opt.jpg manage 5.8 K 11 Dec 2004 - 18:30 OlivierDalle  
choix_opt.c manage 3.1 K 16 Dec 2004 - 15:56 OlivierDalle Corrige exo 3
multi_proc.c manage 5.1 K 10 Jan 2005 - 17:06 OlivierDalle Corrige de l'exercice 5

You are here: Linfo > ProgrammationImperative > CTPSurProcessus

to top

Copyright © 1999-2017 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding WIKIDeptinfo? Send feedback