• Aucun résultat trouvé

3.5 Structure et impl´ ementation des services

3.5.2 M´emoire

Nous divisons la pr´esentation de la gestion de la m´emoire selon qu’elle est propre ou prˆet´ee. La Figure 3.11 fournit un sch´ema r´ecapitulatif des types de donn´ees, avec leur types de m´emoire et de la mani`ere dont ces donn´ees sont partag´ees.

Cette diff´erenciation des types de donn´ees et de m´emoire est en pratique tr`es simple `a impl´ementer, et est fondamentale pour empˆecher l’utilisation de m´emoire propre pour r´epondre `a des requˆetes du client.

3.5.2.1 M´emoire propre

Le service dispose de m´emoire propre, qui sert par exemple `a stocker son code et des variables globales (que nous appelons donn´ees globales). L’utilisation de cette

donn´ees globales

m´emoire doit faire l’objet de la plus grande attention pour ´eviter les d´eni de services.

Donn´ees par client ou connexion et statelessness La m´emoire propre ne peut pas ˆetre utilis´ee pour stocker des informations relatives `a un client ou une « connexion » : comme le nombre de clients est non born´e26, cela serait une opportu-

nit´e de d´eni de service. Toutes ces informations sont stock´ees dans de la m´emoire

26

En effet, rien n’empˆeche un grand nombre de clients d’utiliser simultan´ement la mˆeme ressource : un client peut librement copier sa capacit´e `a beaucoup d’autres clients.

3.5.2. M ´emoire Domaine du service Thread A Stack A Thread B Stack B Thread C Stack C 0 1 2 3 4 5 6 7 A,B C

A,B,C Donn´ees globales

Donn´ees par ressource

Donn´ees locales Type de donn´ee Type de m´emoire M´emoire propre Transitoire Semi- permanente M´emoire prˆet´ee

Selon leur type, les donn´ees peuvent ˆetre stock´ees dans des m´emoires d’origines diff´erentes, et peuvent ˆetre partag´ees par diff´erents clients.

• Les donn´ees locales (i.e. par client ou par “connection” ) ne sont ni partag´ees ni stock´ees dans le service, car elles sont plac´ees dans de la m´emoire transitoire. Elles ne sont accessibles que par le client. Comme il n’y a pas de donn´ees par client non-transitoire, le service est stateless.

• Les donn´ees par ressources peuvent ˆetre soit stock´ees dans de la m´emoire propre (tableau des ressources), soit dans de la m´emoire prˆet´ee semi-permanente (essentiellement pour des gros buffers de donn´ees utilis´es de mani`ere asynchrone par les services). Elles peuvent ˆetre acc´ed´ees seulement par les clients de cette ressource.

• Les donn´ees globales sont stock´ees dans de la m´emoire propre au service, et peut ˆetre acc´ed´ee par tous les clients. Fig. 3.11 – Organisation de la m´emoire dans un service. Chaque bloc de donn´ee est colori´e et ´etiquet´e en fonction des threads qui

prˆet´ee ; en pratique uniquement dans de la m´emoire prˆet´ee de mani`ere transitive27

. Le service est donc stateless : aucune information relative `a une connexion ne peut

stateless

ˆetre retenue entre deux appels. Les donn´ees associ´ees `a une connexion dans les syst`emes classiques seront soit plac´ees dans le client (§ 5.3.1), soit associ´ees `a la ressource sur laquelle agit le client.

Notons que cela ne concerne que les donn´ees par connexion qui restent entre plusieurs appels : durant une connexion, l’´etat de la connexion est stock´e dans la pile, qui est stock´ee dans de la m´emoire prˆet´ee de mani`ere transitoire (§ 3.4.2).

Les file descriptors UNIX sont un exemple typique de donn´ees associ´ees `a une connexion. Dedans sont stock´ees diff´erentes donn´ees ; dans Linux [BC05] on trouve les flags pass´e `a open, l’offset courant, l’UID et GID du processus... Dans Anaxagoros, les seules donn´ees relatives `a une connexion qui restent sont stock´ees dans la capacit´e du service (qui est immutable), et permettent seulement de pointer vers une ressource en indiquant les droits qui y sont associ´es.

Cette contrainte de statelessness va dans la mˆeme direction que le principe de minimisation des donn´ees que l’on verra en section 5.3.1, qui offre en fait un certain nombre d’avantages.

Donn´ees par ressource Contrairement aux donn´ees par connexion, il est possible de stocker entre appels les informations relatives `a une ressource, dont le nombre est born´e (quand le service ne fournit pas de ressources `a proprement parler, comme le service r´eseau, on peut cr´eer des ressources artificielles “connexion au service r´eseau” dont le nombre est born´e, voir Section 3.5.3). Ainsi le probl`eme de d´eni de service est ´evit´e : au maximum, la m´emoire consomm´ee sera de “nombre de ressources” fois “taille des donn´ees associ´ees `a chaque ressource”.

Nous faisons le choix d’allouer cette m´emoire associ´ee aux ressources de mani`ere statique. Ainsi on peut structurer les donn´ees associ´ees `a chaque ressource sous la forme d’un tableau : c’est le tableau des ressources. Cela permet un acc`es rapide

tableau des

ressources `a ces donn´ees, et simplifie la conception (structure de donn´ees, relations avec les

politiques d’allocation) par rapport au choix d’une allocation dynamique. Afin de limiter le gˆachis de m´emoire, qui se produit lorsque toutes les ressources ne sont pas utilis´ees, la taille des entr´ees de ce tableau est minimis´ee.

Allocation statique La m´emoire propre d’un service peut toujours ˆetre divis´ee comme pr´esent´e, en donn´ees “par ressource” ou “globale” (e.g. code et variables globales). La taille des donn´ees globales ´etant g´en´eralement fixe28

, le service n’a le plus souvent pas besoin de demander d’augmentation de la taille de sa m´emoire propre (allocation m´emoire enti`erement statique). Cela simplifie drastiquement l’impl´ementation du service, et permet de facilement contrˆoler qu’on a pas d’alloca- tion de m´emoire (e.g. en interdisant l’utilisation de malloc, on s’assure qu’il n’y a pas d’utilisation implicite de FCFS sur la m´emoire).

27

On pourrait utiliser de la m´emoire prˆet´ee de mani`ere semi-permanente pour stocker des informations relatives `a une connexion. Mais cela consomme de l’espace d’adressage (qui est une ressource propre), cela ne fait que reporter le probl`eme ailleurs.

3.5.2. M´emoire

L’allocation statique de la m´emoire dans le service semble ˆetre un gˆachis m´emoire, en particulier pour le tableau des ressources, o`u l’on pourrait pr´ef´erer allouer de la m´emoire au fur et `a mesure que diff´erentes ressources sont utilis´ees. Il y a n´eanmoins beaucoup d’arguments en faveur de ce choix :

• ce sont les buffers de donn´ees qui occupent le plus de place m´emoire, et ceux ci sont prˆet´es (§ 3.4.4), ainsi seule la place pour les « donn´ees de gestion », bien plus petites, est perdue. Le design des services demande par ailleurs de minimiser ces donn´ees de gestion (§ 5.3.1) pour perdre un minimum de place ; • le service doit faire l’association entre une ressource et les donn´ees qui lui sont associ´ees. Le tableau est la structure la plus efficace en terme de temps CPU pour faire cette association, et cela sans compter le temps pass´e `a faire les allocations m´emoires. Les autres structures impl´ementeraient un « tableau peu dense », par exemple avec une table de hachage. Il y a un surcoˆut en temps CPU pour calculer ces hash et faire les allocation dynamiques de m´emoire ; • de plus, quand quasiment toutes les ressources sont utilis´ees, alors cette

structure plus complexe repr´esente ´egalement un gˆachis m´emoire, `a cause des pointeurs additionnels etc. Le tableau est la structure la plus ´econome en m´emoire quand presque toutes les ressources sont utilis´ees ;

• le tableau des ressources est une structure simple. La structure statique facilite la compr´ehension fine du code, ainsi que la preuve de programme (par exemple, on n’a pas de probl`eme d’aliasing). De plus, on ´evite quantit´e d’erreurs de programmation (e.g. probl`eme de double free, utilisation de m´emoire apr`es free, etc.), ce qui rend le service plus sˆur. La garantie que la m´emoire ne « s’en va pas » est utile dans beaucoup de situations. Cela permet par exemple de simplifier certaines synchronisations, comme le notent ´egalement Greenwald et al. [GC96]. Cela permet aussi d’utiliser des pointeurs estampill´es (§3.3.3.1) ; • l’allocation statique de la m´emoire aide `a identifier toute allocation dynamique

d’une autre ressource. En g´en´eral, quand une ressource est allou´ee dynamique- ment (e.g. place dans la c-list), on utilise de la m´emoire pour conserver des op´erations sur cette ressource (e.g. `a quoi sert la capacit´e plac´ee dans la c-list), qui est ´egalement allou´ee dynamiquement. Si la m´emoire est allou´ee statiquement, on minimise les chances de faire une telle allocation par erreur ; • l’allocation dynamique risque de transformer un d´eni de service local (sur la m´emoire d’un service) en un d´eni de service global (sur la m´emoire de tous les services). L’allocation statique permet donc de confiner les erreurs ;

• enfin, l’allocation dynamique de m´emoire pose diff´erents probl`emes : l’allocation m´emoire est rarement faite en temps constant et potentiellement long ; que faire s’il n’y a plus beaucoup de m´emoire dans le syst`eme, sachant qu’on ne peut pas facilement attribuer ces allocations `a petite granularit´e (en dessous de la taille d’une page) aux clients ; le service de politique m´emoire fait dor´enavant partie du TCB du service...

Ainsi l’allocation statique r´esulte en un petit gˆachis m´emoire (quelques kilo- octets) par service lorsque toutes les ressources ne sont pas utilis´ees, mais offre un grand nombre d’avantages.

3.5.2.2 M´emoire prˆet´ee

Nous avons vu comment est prˆet´ee la m´emoire, et comment est fournie l’assurance que certains mappings ne sont pas partag´es. Nous expliquons seulement l’usage de cette m´emoire prˆet´ee, et la gestion de sa r´evocation.

Usage de la m´emoire transitoire La m´emoire pass´ee de mani`ere transitoire est `a emplacement fixe de l’espace d’adressage dans notre impl´ementation, et est facilement accessible. Elle est utilis´ee pour les donn´ees locales, i.e. la pile et les

donn´ees locales

arguments pass´es.

Usage de la m´emoire semi-permanente Comme on utilise de la m´emoire transitoire pour les donn´ees locales, et de la m´emoire propre pour les donn´ees globales, la m´emoire prˆet´ee de mani`ere semi-permanente ne sert que pour des grosses donn´ees par ressource. On l’utilise typiquement pour des donn´ees `a communiquer avec le client, et que le service utilise de mani`ere asynchrone (e.g. donn´ees envoy´ees ou re¸cues sur disque dur ou sur le r´eseau). Mis `a part la probl´ematique de gestion de l’espace d’adressage, qui est une denr´ee finie (et est abord´ee en section 3.5.3), la m´emoire semi-permanente ne pose pas de probl`eme de gestion.

Gestion de la r´evocation La principale difficult´e concerne la tentative d’acc`es par le service `a de la m´emoire qui a ´et´e r´evoqu´ee.

Si la pile est r´evoqu´ee, l’ex´ecution du service va devenir tr`es rapidement impossible. Ce cas est donc similaire `a celui de la r´evocation de temps CPU. C’est pourquoi, dans l’impl´ementation, pile et temps CPU sont tous deux li´es au thread, et on ne peut r´evoquer la pile sans d´etruire le thread. De mˆeme, tous les thread-local mappings ne peuvent ˆetre r´evoqu´es sans d´etruire le thread. Le probl`eme de r´evocation est donc r´eduit `a la m´emoire semi-permanente.

Pour la m´emoire semi-permanente, on voudrait que le code puisse g´erer le fait que la m´emoire aie ´et´e r´evoqu´ee. On va pour cela s’aider du fait qu’un emplacement m´emoire (mis `a part la pile) est toujours acc´ed´e de mani`ere explicite dans le code (du moins en C). On ne peut pas tester une adresse pour savoir si la m´emoire a ´et´e r´evoqu´ee : ce serait lent, p´enible, et sujet `a race conditions. On va faire cela par un m´ecanisme similaire aux exceptions.

La premi`ere contrainte est de d´etecter que la m´emoire acc´ed´ee `a ´et´e r´evoqu´ee. Cela se fait par le m´ecanisme d’autopagination [Han99, EKJO95], i.e. en faisant en sorte que les services soient notifi´es lorsqu’ils font un d´efaut de page (nous avons vu que les exceptions ´etaient param´etrables (§ 3.1.3.1) ; les services sont param´etr´es pour recevoir eux mˆeme leurs d´efauts de page, i.e. leur d´efaut de page changent leur program counter `a une adresse fix´ee). Et en s’assurant par le noyau que lorsque la m´emoire est r´evoqu´ee, cela cr´ee effectivement un d´efaut de page. Une technique similaire est utilis´ee dans le syst`eme de fenˆetrage d’EROS [SVNC04, § 5].

3.5.3. Capacit´e et autres ressources