• Aucun résultat trouvé

Ce probl`eme de confiance se pose aussi dans les syst`emes bas´es sur les com- munication synchrone ou asynchrone ; d’autant plus qu’ils n’ont que des thread propres. En cons´equence, les m´ecanismes de notifications sont courants, par exemple Mach [Loe92], send dans EROS [SFS96]). Notons qu’EROS semble avoir plus tard supprim´e le send (ou ne l’utilise pas pour envoyer des notifications), ce qui demande une ´emulation plus compliqu´ee [SSS04].

Autres ressources temporelles Il existe d’autre ressources temporelles que le temps CPU : le partage de l’acc`es au disque ou `a la carte r´eseau est fait par ordonnancement. Mais le transfert de ces ressources temporelles est inutile en pratique, et n’est pas impl´ement´e dans Anaxagoros. Nous allons maintenant ´etudier le transfert des ressources partag´ees spatialement, et en premier lieu de la m´emoire.

3.4.2 Prˆet de m´emoire transitoire

3.4.2.1 Besoin de prˆet transitoire

Avec le transfert de temps CPU, il faut aussi transf´erer de la m´emoire. En effet, `a moins que la requˆete ne soit triviale, l’ex´ecution de cette requˆete consomme une pile19. Comme l’ex´ecution de la requˆete peut ˆetre pr´eempt´ee dans le service, il

peut y avoir plusieurs clients simultan´ement, et il faut une pile par client servi. Et `a moins que le nombre de clients soit born´e, la m´emoire pour ces piles ne peut pas provenir d’une sous-pool statique dans le service ; ce serait l’application de la politique FCFS sur la m´emoire du service, et entraˆınerait notamment des blocages incontrˆol´es. Le prˆet de m´emoire transitoire (i.e. intra-appel) est donc n´ecessaire pour que l’ordonnancement soit ind´ependant des domaines de protections. Le prˆet de m´emoire semi-permanent (i.e. inter-appel) est abord´e en section 3.4.4.

3.4.2.2 Impl´ementation

Probl`eme de la consommation d’espace d’adressage Transf´erer de la m´emoire au service consiste `a installer dans l’espace d’adressage du service un certain nombre de mappings. Le probl`eme est que l’espace d’adressage du service est limit´e : si trop de threads prˆetent de la m´emoire au service, tout son espace d’adressage sera consomm´e, ce qui empˆechera l’entr´ee de nouveaux threads. L’espace d’adressage d’un service est en fait une ressource limit´ee, et nous venons donc de d´ecrire encore une utilisation de FCFS, sensible `a un d´eni de ressource.

La solution naturelle `a ce probl`eme est de « prˆeter de l’espace d’adressage ». Nous impl´ementons cela grˆace aux thread-local mappings, qui sont une partie de l’espace d’adressage propre au thread.

Impl´ementation du prˆet transitoire Concr`etement, tout espace d’adressage est divis´e en deux parties : une partie propre au domaine, et une partie propre au

19

et mˆeme si la requˆete ne consomme pas de pile, il faut toujours pouvoir stocker les registres lorsqu’une pr´eemption survient.

thread20

. `A chaque fois qu’un thread change de domaine (i.e. appel de service), ou lors de toute d´ecision d’ordonnancement, on change d’espace d’adressage. La Figure 3.9 pr´esente cette impl´ementation.

Mappings du thread courant Mappings du domaine courant Ex´ecution A A A S B B B S B B A S A A A AS B BS B AS A t

Fig. 3.9 – ´Evolution de l’espace d’adressage de la tˆache. La partie domaine de l’espace d’adressage change lors des appels de services, mais par la partie thread. Lorsque le thread change, l’espace d’adressage change pour accueillir les mappings du thread choisi et du domaine dans lequel est ce thread. Sur IA32, le mapping du noyau est pr´esent dans tous les espaces d’adressages.

Ce m´ecanisme n’est pas lent. Sur x86, tout changement d’espace d’adressage n´ecessite un flush complet du TLB. Mais nous avons arrang´e le code et les structures de donn´ees afin que la modification de l’espace d’adressage se fasse avant ce flush ; ainsi seule une ´ecriture dans une table des pages est n´ecessaire pour ajouter un mapping. Notons cependant que sur d’autres types d’architectures, de meilleures solutions sont envisageables. En particulier, sur les architectures pour lesquelles la gestion du TLB est d´el´egu´ee au syst`eme d’exploitation, ce m´ecanisme peut ˆetre facilement et efficacement impl´ement´e.

Ce m´ecanisme a ´egalement un avantage en s´ecurit´e : il est impossible pour un thread d’acc´eder directement aux donn´ees d’un autre thread. Ainsi, pour qu’il y ait communication entre deux threads, il faut n´ecessairement passer par de la m´emoire propre au service. Cela permet d’augmenter la garantie de non-interf´erence entre clients diff´erents.

Notons que dans le cas de single-space operating systems, dans lequel les adresses virtuelles de toutes les tˆaches sont identiques, un m´ecanisme alternatif peut ˆetre envisageable, qui permette ´egalement au service de ne pas consommer d’espace d’adressage. Il suffirait de modifier le service pour qu’il puisse acc´eder `a la m´emoire du client. Il faut faire attention `a ce que les droits d’acc`es ne fassent pas consommer de m´emoire au service.

Probl`eme du partage des mappings Le prˆet de m´emoire accorde au service le droit d’acc´eder `a une partie de la m´emoire allou´ee au client. Mais rien n’empˆeche le client d’acc´eder `a cette m´emoire par ailleurs, sans que le service puisse le savoir.

20

Les processeurs x86 requi`erent ´egalement une troisi`eme partie, propre au noyau, qu’on ne repr´esente pas

3.4.2. Prˆet de m´emoire transitoire

Cela pose en particulier probl`eme pour la pile, puisque cela permet de modifier l’ex´ecution dans le service.

Pour la pile, nous avons r´esolu le probl`eme de mani`ere simple : la pile utilis´ee est l’UTCB, qui est toujours situ´ee `a adresse fixe du thread-local mapping du thread courant, et ne peut pas ˆetre mapp´ee `a un autre endroit. Cela garantit que l’UTCB d’un thread ne peut ˆetre modifi´ee que par ce thread21

. Cette utilisation est similaire `

a la « pile noyau » dans les noyaux monolithiques.

Nous n’avons pas encore eu besoin d’impl´ementer une telle garantie pour d’autres mappings. G´en´eralement, on pourra le faire en permettant au client de fournir au service la preuve que le mapping qu’il utilise est unique. Une garantie similaire est n´ecessaire pour le prˆet semi-permanent (§ 3.4.4).

Un des avantages des thread-local mappings est qu’ils facilitent la preuve que le service ne fait pas communiquer deux clients : toute communication entre deux threads dans le service doit passer par un buffer interm´ediaire. Cette preuve sera ´etablie en montrant que deux threads n’acc`edent que rarement aux mˆeme donn´ees, et que les donn´ees partag´ees ne permettent pas de communication.

Utilisation des thread-local mappings Pour le moment, le thread-local map- ping fait 4ko d’UTCB utilis´e comme pile, pour le passage des param`etres/multicall, pour la sauvegarde des registres lors de la pr´eemption (et autres m´ecanismes de contrˆole de la pr´eemption pr´esent´e en section 4.2.3).

Les autres mappings correspondent chacun `a une entr´ee dans la table des pages de premier niveau (donc `a une plage de 4Mo chacun), et peuvent ˆetre install´es tout simplement par la modification d’une entr´ee dans la table des pages du premier niveau. Ils permettent de passer de gros volumes de donn´ees. Notons que ces mappings peuvent ˆetre simultan´ement pr´esents dans l’espace d’adressage du client : cela permet la copie directe de donn´ees du service dans l’espace d’adressage du client.

En r´etrospective, il serait plus judicieux d’avoir un autre mapping de 4ko et un mapping de 4Mo. La transmission de donn´ees par tranche de 4ko est fr´equente (les appels read et write UNIX fonctionnent ainsi), et ce mapping pourrait ˆetre install´e dans la mˆeme table des pages que l’UTCB, r´eduisant le coˆut du TLB miss.

3.4.2.3 Extension au SMP

Extension naturelle L’extension naturelle au SMP est de r´eserver, dans chaque espace d’adressage, une partie de l’espace d’adressage pour le thread-local mapping pour chaque processeur. L’espace d’adressage est ainsi divis´e, pour M processeurs, en 2M + 1 parties.

Outre la consommation d’espace d’adressage, cette extension pose des probl`emes de s´ecurit´e. Plus rien n’empˆeche, dans un espace d’adressage, un thread de pouvoir acc´eder aux thread-local mappings d’un autre thread. Et cela, mˆeme quand l’autre thread est parti ou d´etruit : en effet, mˆeme si on retire le mapping thread-local de l’espace d’adressage, il peut ˆetre rest´e dans le cache TLB des autres processeurs.

21

Un autre avantage, dans notre impl´ementation, est qu’il n’y a mˆeme pas besoin de modifier une entr´ee dans la table des pages lors d’un appel de service.

Cela demanderait de faire un TLB shootdown `a chaque appel de service, et serait extrˆemement coˆuteux en performances.

Mˆeme si on suppose faire confiance aux services, le mˆeme probl`eme se retrouve pour des clients multithreads, ce qui leur permet de modifier la pile sur un autre processeur en train d’ex´ecuter un service, par exemple.

Pour se prot´eger contre ce comportement, on peut penser utiliser le m´ecanisme de segmentation du Pentium, qui permet de restreindre l’acc`es `a une plage d’adresses virtuelles. Malheureusement, ce m´ecanisme ne fait pas seulement de la protection, mais ´egalement de la translation d’adresses. Ce qui implique que les adresses utilis´ees par diff´erents segments ne correspondent plus. Les compilateurs actuels ne g`erent pas les architectures segment´ees, et rajouter une API pour les translation d’adresses entre segments ne serait pas commode et surprenant pour le programmeur.

Autres solutions Nous avons donc envisag´e d’autres solutions. L’une d’elle con- sisterait `a avoir un espace d’adressage par service par CPU (i.e., une table des pages de premier niveau diff´erente pour chaque processeur ; le reste serait partag´e). Ainsi, chaque processeur utiliserait la mˆeme plage d’adresses pour les thread-local map- pings, mais ne pourraient plus acc´eder `a celui des autres. Le principal probl`eme de cette solution est le coˆut en m´emoire occasionn´e. De plus, elle oblige chaque domaine `

a tenir compte du nombre de processeurs qui peuvent l’ex´ecuter simultan´ement. Ce coˆut peut ˆetre ´elimin´e en g´en´erant l’espace d’adressage `a la vol´ee au moment de l’appel. Concr`etement, le noyau garderait une page des tables de premier niveau par CPU. Lorsqu’un thread invoque un service, les entr´ees n´ecessaires, peu nombreuses, sont copi´ees dans cette table des pages avant qu’elle soit utilis´ee. Cette technique permet d’´eviter tout surcoˆut en m´emoire, mais rajoute un surcoˆut en temps CPU `a chaque invocation de service ou pr´eemption de thread. Sur Pentium, il faut copier une entr´ee par tranche de 4Mo d’espace d’adressage. La majorit´e des services et programmes font moins de 4Mo, mais il y en a qui peuvent ˆetre relativement gros (syst`emes de fichiers ou Linux paravirtualis´e)22.

Finalement, la meilleure solution est peut ˆetre une solution mixte entre les deux : suivant la taille de l’espace d’adressage, un domaine pourra choisir l’une ou l’autre des solutions.

Notons que ce probl`eme est sp´ecifique aux syst`emes pour lesquels les tables des pages sont g´er´ees en hardware, et ne se pose pas pour les architectures pour lesquelles le TLB est g´er´e en logiciel. Dans ce dernier cas, chaque processeur maintient tout simplement dans son TLB, les thread-local mappings de son thread, et tous les processeurs partagent la mˆeme plage d’adresses virtuelles pour ces mappings.

Certains processeurs ARM ont ´egalement un m´ecanisme qui permet de rediriger une r´egion de l’espace d’adressage vers un autre endroit [WTUH03], qui pourrait ´egalement ˆetre mise `a profit.

22

On peut penser faire l’´ecriture de certaines entr´ees de mani`ere paresseuse : l’entr´ee est install´ee lorsqu’un d´efaut de page se produit. Il n’est pas certain que cela am´eliore les performances, mˆeme pour les tˆaches non temps r´eel.