[SOS] Passage d'arguments aux nouveaux processus/variables d'environnement

David Decotigny david.decotigny at free.fr
Sam 25 Fév 12:43:01 CET 2006


Bonjour,

Le patch décrit dans ce qui suit est un "bonus" spécial mailing-list. Il
ne sera probablement pas couvert par le prochain article. En revanche il
sera intégré au code qui accompagnera ledit prochain article.

Cette démarche est quasiment inédite dans SOS mais je pense qu'elle est
appelée à se répéter deux ou trois fois. Nous avons en effet une poignée
de fonctionnalités que nous aimerions ajouter à SOS mais qui ne méritent
pas un article à elles toutes seules. Ce sont des petites améliorations
"sexy" pas vraiment fondamentales.

Pour résumer, ce patch ajoute les fonctionnalités suivantes :
 - la possibilité de passer des arguments (les fameux argc/argv) aux
   nouveaux processus,
 - la possibilité de manipuler et de transmettre des variables
   d'environnement.

Pour faire court : au niveau du noyau, les modifications concernent le
syscall "exec" dont le comportement est maintenant adapté à execve (2).
Au niveau utilisateur, les modifications consistent en la définition
d'un wrapper execve au sens classique (autour de ce syscall sos_exec),
en la définition des variantes standard user execl/execle/execv qui
reposent sur ce execve, en la définition des fonctions user de
manipulation des variables d'environnement
(getenv/putenv/setenv/unsetenv/clearenv) et en la modification de la
routine user _start de lancement de main() pour lui passer les
paramètres standard argc/argv/envp.

En fait, ça c'est la théorie. En pratique, SOS a dû être modifié plus en
profondeur car il n'était pas adapté à ces objectifs. En particulier,
depuis le noyau on ne pouvait pas accéder à l'espace user de n'importe
quel thread : on ne pouvait accéder a l'espace user que du thread
courant. Pas question par exemple d'accéder a l'espace user du nouveau
processus qu'un thread créait par exec !

Or ceci est gênant car, dans le syscall exec, on aimerait pouvoir
accéder a l'espace user qu'on est en train de créer. En effet, on
aimerait tout simplement recopier des données (le tableau argv[] et les
variables d'environnement) de l'espace user du thread courant, vers
l'espace user qu'on est en train de créer. Comme cet espace user n'est
pas encore l'espace user du thread qui traite le syscall (puisqu'on est
en train de le créer), eh bien une telle modif n'était pas possible. Il
fallait donc revoir ce point. La solution la plus simple consisterait à
faire la chose suivante (syscall exec) :
  - début de la création du nouvel espace d'adressage comme d'hab
  - recopie des argv[] / envp[] a transmettre temporairement en espace
    noyau
  - changement d'espace d'adressage pour le processus courant : on
    utilise maintenant le nouvel espace d'adressage. Suppression de
    l'ancien espace d'adressage
  - recopie des argv[] / envp[] du noyau vers le nouvel espace
    d'adressage

Ce n'est finalement pas la solution que j'ai adoptée. J'ai préféré une
solution plus générale et pas réellement plus compliquée. Avec cette
solution, nous pouvons accéder à l'espace user de n'importe quel
processus à tout moment, pas uniquement celui du processus du thread
courant, et pas uniquement dans le cadre du syscall exec.

Au centre de cette solution, la modif fondamentale se situe dans
thread.h. Il s'agit de remplacer le champ suivant de struct sos_thread :
   struct sos_mm_context *squatted_mm_context;
Par :
   struct sos_umem_vmm_as *squatted_address_space;
Le but de cette modif est de ne pas permettre la suppression d'un espace
d'adressage pendant qu'on est en train de le squatter, et elle permet de
remplacer la notion "reconfigurer la MMU pour acceder a une zone user"
par la notion "reellement acceder a un autre espace d'adressage", ce qui
permet l'acces aux regions et ressources mappées dans cet espace ! Pour
cela, une deuxième modif concerne umem_vmm.c et consiste en l'ajout des
deux fonctions :
   struct sos_umem_vmm_as * sos_umem_vmm_get_current_as(void);
   sos_ret_t sos_umem_vmm_set_current_as(struct sos_umem_vmm_as * as);
Qui manipulent une variable locale :
   static struct sos_umem_vmm_as * current_address_space;

Quand un thread veut accéder a l'espace user d'un espace d'adressage,
appelons-le "as", il se passe les choses suivantes :
  - on vérifie qu'il ne squattait aucun espace avant
  - on appelle sos_umem_vmm_set_current_as(as)
    - qui incrémente le compteur de refs sur le process associé
    ==> Ni le process ni l'espace d'adressage squattés ne pourront
        disparaître
  - on peut acceder aux adresses user
    - si il y a une faute de page, c'est l'espace d'adressage squatté
      qui est pris en compte, ses ressources mappées, etc.
    ==> ainsi les fichiers mappés dans cet espace d'adressage squatté
        pourront être accédés depuis le noyau
  - on appelle sos_umem_vmm_set_current_as(NULL) pour ne plus squatter
    cet espace d'adressage
    - qui incremente le compteur de refs sur le process associé
    ==> Ce process peut maintenant etre supprime si necessaire, ce qui
        supprimera l'espace d'adressage associé

Les fonctions
sos_thread_begin_user_space_access(as)/sos_thread_end_user_space_access()
ont ete modifiees pour utiliser ce protocole.

Voila pour le coeur des modifications. Concernant spécifiquement le
syscall execve, son fonctionnement est le même que dans la version
"officielle" de l'article 9.5, à ceci pres qu'il prend 4 arguments
supplémentaires :
  - l'adresse user d'une zone ou sont stockés les arguments à passer au
    nouveau programme
  - la taille de cette zone
  - l'adresse de la zone user où sont stockées les variables
    d'environnement à transmettre au nouveau programme
  - sa taille
Pour les données de ces deux zones, le syscall exec :
  - mappe une region de /dev/zero de taille suffisante pour les donnees
    dans le nouvel espace d'adressage
  - appelle la nouvelle fonction sos_usercpy() pour copier les données
    de l'ancien espace d'adressage vers le nouveau.

La nouvelle fonction sos_usercpy() est définie dans uaccess.c et c'est
elle qui copie des donnees user d'un espace d'adressage quelconque vers
la zone user d'un (autre) espace d'adressage quelconque. Elle utilise
pour cela les fonctions
sos_thread_begin_user_space_access(as)/sos_thread_end_user_space_access().
Cette fonction sos_usercpy() fait une allocation temporaire en zone
noyau pour stocker les données a transférer. Il y a donc 2 copies :
userSrc -> noyau et noyau -> userDest. Ceci pourrait être optimisé pour
qu'il n'y ait plus qu'une seule copie (userSrc -> noyau), en remplaçant
la deuxieme copie par un remapping de page noyau en zone user. Ceci est
laissé en guise d'exercice.

Voila. Pour le reste, les zones argv[]/envp[] passées a execve sont
stockées sous forme monolithique en mémoire afin d'éviter que le noyau
fasse trop d'opérations de déréférencement de pointeurs user. En effet,
moins on fait de choses dans le noyau, mieux c'est puisque le noyau SOS
n'est pas préemptible. Ce sont donc /et/ le wrapper execve() de la libc
user, /et/ la fonction _start() de userland/crt.c qui s'occuperont de
manipuler des tableaux argv[]/envp[] à double indirection pour les
transformer en une zone "plate" (on parle de "sérialisation" ou de
"marshalling"), dont le format est donné au début de crt.c. Ce sont en
réalité les fonctions unmarshall_args()/unmarshall_env() et
marshall_execve_parameter() qui s'occuperont de ces transformations.

Autres modifications apportées par ce patch :
  - la maladresse pointée par Anthoine Bourgeois
  - une série de macros SOS_PAGING_IS_USER_AREA/SOS_PAGING_IS_KEREL_AREA
    pour clarifier le code en remplacement de tests d'inegalité sur
    SOS_PAGING_BASE_USER_ADDRESS
  - le programme user "envtest" pour afficher le contenu de argv et de
    envp passés a un nouveau programme
  - les commandes shell getenv/setenv/clearenv

En principe il n'y a pas de limitation particulière sur la taille de
argv/envp (hormis la taille de la RAM...).

La patch est disponible au bas de la page "Bugs" sur le site de SOS
(bien que ce ne soit ni un bug ni un errata...) :
  http://sos.enix.org/fr/SOSPatches

Bonne journée !

-- 
http://david.decotigny.free.fr/


Plus d'informations sur la liste de diffusion Sos