[Kos-dev] Babel

PETAZZONI,THOMAS (Non-HP-Germany,ex1) kos-dev@enix.org
Thu, 1 Aug 2002 10:49:43 +0200


Salut,

Ceci aurai pu faire l'objet d'un mail prive =E0 d2, mais j'ai =
finalement je
prefere que les discussions aient lieu sur kos-dev : c'est archive et =
c'est
public.

Tout d'abord, resituons le contexte. Nous avons deja echange quelques =
mails
concernant la modification de Babel, afin que celui-ci s'adapte mieux a =
la
communication avec le CPL3, et reponde proprement a tous les cas de =
figure.
En effet, dans la version courante de Babel, nous n'avions reellement
envisage que le cas du fichier "normal", sans penser aux fichiers =
"speciaux"
que sont les devices par exemple.

La version amelioree de Babel doit donc trouver un modele objet =
suffisament
generique pour qu'il englobe toutes les situations possibles, sans =
toutefois
rentrer dans la genericite a outrance qui conduirait a un modele objet =
lourd
et inutilisable.=20

Je donnerai en fin de mail quelques references pour de la lecture sur =
le
sujet.

Je vais rendre visite a David le WE prochain (pas ce WE, celui =
d'apres).
Nous avons donc decide de proceder en 3 etapes :
 * definir sur papier proprement le modele objet et ce qui l'entoure.
 * coder en C++ sous Linux (c'est a dire en offline) ce modele objet en =
le
faisant "fonctionner", et eventuellement en simulant un syscall pour =
bien se
rendre compte de la maniere avec laquelle tout cela va se passer.
 * implanter ce modele objet, soit en C, soit en C++ dans KOS.

Tout d'abord, je propose de parler de resources plutot que de fichiers.
Ainsi le tableau des descripteurs de fichiers ouvert Unix se transforme =
en
un tableau des descripteurs de ressources ouvertes. Une resource peut =
aussi
bien etre un fichier, une team, un thread, une semaphore, etc...

Evidemment on ne pourra pas faire n'importe quelle operation sur =
n'importe
quelle ressource : on ne pourra pas faire de read() sur une team par =
exemple
(a moins que l'on trouve un sens a cela).

Cette methode permet de conserver la compatibilite Unix, tout en =
l'etendant
en etant plus generique. Lors d'un fork() on doit heriter des =
descripteurs
de fichiers ouvert selon la semantique Unix (POSIX ?). Nos descripteurs
seront donc types avec un flag IS_HERITABLE. Ainsi on pourra definir si =
tel
ou tel resource est heritable. Par defaut tous les fichiers seront
heritables et tout le reste ne le sera pas. Mais on peut tres bien dire =
tel
thread est heritable, et comme ca ce thread restera au travers des =
fork().

Cette facon d'identifier les ressources au niveau utilisateur me semble
assez elegante. Nous aurons des identifiants reserves, statiques pour =
que la
glibc s'y retrouve :
 * 0 : entree standard
 * 1 : sortie standard
 * 2 : erreur standard
 * 3 : team courante
 * 4 : thread principal

Le thread principal est celui cree a la base pour la team. C'est celui =
qui
est cree pour simuler le process Unix via nos teams et threads. Notre =
glibc
devra donc etre code de maniere a pouvoir demander par exemple le PID =
de la
ressource 3. Si on demande le PID de la ressource 2, on aura un retour =
de -1
et eventuellement un errno a ENOSYS.

En ce qui concerne le portage de la glibc, j'ai deja commence a =
regarder
comment cela s'organisait en lisant les sources (pas tout evidemment). =
Je
trouve que cela a ete plutot bien pense. Je n'ai pas encore code, mais =
a
priori cela semble bien foutu. Tout ce qui dependant du systeme, et qui =
doit
etre modifie pour le portage se trouve dans le repertoire sysdeps/. Il
suffit de creer un sous repertoire pour son systeme, et d'implementer =
les
appels de bas niveau (read, write, etc...), sachant que on peut dire =
qu'on
veut "reutiliser" des fonctions d'autres repertoires en utilisant le =
fichier
Implies.=20

Passons maintenant au modele objet au niveau noyau. Il doit a la fois
permettre a la libc d'acceder de maniere uniforme a tous les fichiers =
du
systeme, et d'etendre de maniere simple les interfaces et que les =
nouvelles
capabilities offertes soient utilisables par des librairies separees de =
la
glibc. En effet, il est clair que nous n'allons pas ajouter de =
fonctions a
la glibc. Pour tout ce qui est specifique, on aura une librairie. Par
exemple on peut imaginer une librairie sound card qui permettra =
d'appeler
les methodes specifiques aux devices de type soundcard.

D'une maniere generale on peut dire que les fonctions bas niveau de la =
libc
seront simplement des wrappers vers le noyau. Par exemple :

  int read(int fd, char *buf, size_t len)
  {
    return kernel_do_2(fd, "read", (unsigned long) buf, (unsigned long)
len);
  }

La fonction kernel_do_2 va empaqueter les 2 parametres buf et len dans =
une
seule structure sur la pile (comme cela est fait actuellement dans =
kos-sys),
puis appeler le noyau en lui passant la chaine de caractere en =
parametre. Et
ensuite le noyau va voir si y'a moyen de faire un "read" sur la =
ressource
specifiee en parametre. Si oui il l'execute, si non il retourne une =
erreur.=20

Donc c'est le noyau qui detectera si la methode peut etre appliquee sur =
la
ressource donnee en parametre, mais je pense pas que cela soit trop =
grave :
le mec qui s'amuse a provoquer des erreurs tout le temps il est un peu =
bete.
Je pense pas qu'il faille optimiser pour les erreurs, mais plutot pour =
le
deroulement "normal".

Revenons a notre modele objet. Pour moi il y a deux hierarchies de =
classes,
intimement liees : d'un cote les classes heritant de la classe =
"ressource"
et de l'autre les classes heritant de la classe "manager". On pourra =
ainsi
avoir par exmeple :

 + resource
   + task
   + file
     + ext2
     + fat
     + device
        + soundcard
        + disk
        + part
   + semaphore

Et de l'autre
=20
  + manager
    + filesystem
      + fatfs
      + ext2fs
      + devfs
    + tasksystem
    + semsystem

Comme on le voit pour certaines classes il y a un mapping direct entre
manager et ressource, pour d'autres non (par exemple soundcard, disk et
part) dependent du manager devfs. Les managers sont des classes charges =
de
produire les objets de type ressource, ils vont etre capable de gerer =
leur
espace de nommage local. Par exemple la classe tasksystem va permettre =
de
retrouver la tache de PID x.

La gestion globale du probleme de nommage sera realisee par un module
externe a tout cela, pas du tout oriente objet, si possible un truc =
simple.
On a deux choix : soit on decide que ce module fait le minimum, c'est a =
dire
il trouve quelle instance de quel manager gere la partie de l'espace de
nommage demandee, puis delegue a ce manager la recherche de l'objet. Ou
alors on decide que ce module fait plus, c'est a dire qu'il va "aider" =
le
manager dans la recherche de l'objet (c'est ce qui est fait =
actuellement de
KOS).

Bref si on cherche le fichier /usr/local/src/bidule/truc (et que /usr =
est
une partition a part). Avec la premiere solution la gestion de l'espace =
de
nommage consiste a dire ce fichier est sur /usr et a demander au =
manager de
/usr de trouver le fichier local/src/bidule/truc. La deuxieme solution
consiste a demander au manager de /usr le fichier local, puis a partir =
du
fichier local demander src, puis a partir de la demander bidule, etc...

L'avantage de la deuxieme solution est clair : factorisation de code
importante (c'est pour cela qu'on l'a implementee dans KOS), mais par =
contre
elle impose une grande rigidite dans le nommage. Pas question d'avoir =
un
manager qui gere une partie de l'espace de nommage avec un truc pas
classique separe par des /. Toutefois malgre cela, j'ai tendance a =
preferer
la seconde solution pour la factorisation de code et aussi pour
l'externalisation des problemes de nommage. D'autre part, je pense que =
cette
solution permettra de faciliter la resolution des liens symboliques.

Bien entendu, les ressources devront contenir des champs permettant de =
les
chainer entre elles (arborescence), mais ce ne seront pas elles memes =
qui
s'occuperont de ces champs. Soit c'est le gestionnaire global de =
l'espace de
nommage (seconde solution), soit c'est le manager (premiere solution).
Evidemment l'envie de factoriser la encore le code nous fait pencher =
vers la
seconde solution.

Passons maintenant aux quelques questions que j'ai :
 * les devices sont d'interfaces differentes mais gerees par le meme
manager. Cela pose-t-il un probleme ?
 * les tubes nommees comment ca fonctione ? est-ce que c'est dependant =
d'un
systeme de fichier particulier, ou est-ce qu'on peut en creer partout =
meme
sur un systeme de fichier fat ?
 * avec le driver de fat actuel, je ne simule pas d'inodes. pourtant il
semblerait que Linux simule des inodes, Hurd aussi. Pourquoi ? Est-ce =
si
important d'avoir des inodes ? Pour moi c'est completement dependant du
systeme de fichier, non ?

Si on code le truc en C, je pense qu'il va falloir faire le lookup et
l'enregistrement des methodes a la main. Voici un extrait de code =
imaginaire
d'un driver de carte son :

int write(int fd, char *buf, size_t len)
{


}

int change_volume(int fd, int volume)
{


}

On devra alors faire :
    id =3D register_class("soundcard"); // retourne un identifiant =
unique pour
chaque classe dans le systeme.
    register_method(id, "write", write, 3);=20
    register_method(id, "change_volume", change_volume, 2);

D'abord on enregistre notre classe pour disposer d'un identifiant =
unique
pour cette classe, et ensuite on enregistre les capabilities de cette
classe. Je peux ecrire (methode write avec 3 parametres). Babel =
maintient
donc une liste des capabilities par classe.

Lorsqu'on recoit notre syscall on connait :
 * l'identifiant de la ressource dans notre tableau de descripteurs de
ressources=20
 * le nom (chaine de caractere) de la fonctionnalite a utiliser
 * les parametres (et leur nombre)

D'abord on cherche a quelle classe correspond la ressource, pour =
trouver
l'id de cette classe. Ensuite on regarde dans la table des methodes de =
cette
classe (geree en interne par Babel) si on trouve une methode qui a le =
bon
nom. Ensuite on verifie que le nombre de parametres est bon, et grace =
au
pointeur vers la methode dont on dispose on peut l'appeler !

Il reste les problemes de typage : les parametres qu'on recoit sont =
tous des
unsigned long, mais je pense qu'on peut soit avoir des wrappers au =
niveau
noyau, soit donner en plus du nombre de parametres des informations sur =
le
typage de ces parametres.

Honnetement, j'ai pas code la chose, mais je trouve que c'est plutot =
pas
mal. Ca permet d'ajouter des fonctionnalites n'importe ou n'importe =
quand en
runtime. Quand aux problemes de perf liees aux chaines de caracteres =
avec
des hash tables je pense qu'on peut s'en sortir pas trop mal.=20

Voila, je crois que c'est un peu pres tout ce que j'avais a dire. J'ai =
peut
etre oublie des choses, c'est surement un peu le souk, mais j'ai livre =
brut
mes idees.

Bien entendu, toutes les reactions sont les bienvenues voire attendues.

Bonne journee,

Thomas Petazzoni
thomas.petazzoni@non.hp.com
KOS : http://kos.enix.org
Perso : http://kos.enix.org/~thomas/=20