• Aucun résultat trouvé

6.2 Présentation des services de base de JXTA

7.3.2 Stockage des blocs de données

Objectif. L’objectif du module mémoire bas est de gérer le stockage des espaces mémoire dans l’espace d’adressage des fournisseurs. Ce module est utilisé par la couche des proto-coles de cohérence. Ainsi, ils n’ont pas à se soucier de l’organisation du stockage des données sur les fournisseurs ou à tenir compte de l’hétérogénéité des machines. Les protocoles de co-hérence peuvent donc se concentrer sur les actions (règles) de coco-hérence à appliquer sur les espaces mémoire lors de l’utilisation, par les programmeurs d’applications, des différentes primitives disponibles dans le modèle d’accès mémoire (voir section 5.1.4.2). L’encodage des données est nécessaire afin de prendre en compte l’hétérogénéité des machines constituant une grille de calcul : architecture 32 ou 64 bits, représentation mémoire petit-boutiste (en an-glaislittle endian) ou gros-boutiste (en anglaisbig endian). Par exemple, ce module s’occupe de l’encodage et du décodage des données lors de leurs transferts entre clients et fournis-seurs par les protocole de cohérence. Pour cette gestion de l’hétérogénéité, JUXMEM utilise la bibliothèque XDR.

7.3 – Gestion des ressources mémoire 97 Interface externe et exemple d’utilisation. Du côté client, le module mémoire bas offre aux couches supérieures les primitives qui sont décrites ci-dessous.

juxmem_from_user_memory_to_message. Cette primitive permet d’ajouter une donnée de taille sizeoctets pointée par ptrdans le message passé en paramètre. Elle est par exemple appelée par les protocoles de cohérence lors du relâchement du verrou, pris en mode exclusif et associé à la donnée. Le listing 7.2 représente cet exemple d’utilisation. Un message JXTA est crée (ligne 2), puis la donnée du client ajoutée dans le message par le biais de cette primitive (ligne 5), enfin le message est envoyé (ligne 8).

Listing 7.2 – Utilisation de la primitivejuxmem_from_user_memory_to_messagedans la couche des protocoles de cohérence, lors du relâchement d’un verrou exclusif (code simplifié).

1 int ec_release(consistency_protocol *self, void *ptr, size_t size) {

2 Jxta_message *msg = jxta_message_new(); 3 ... 4 inc_version(self->impl->data_version); 5 juxmem_from_user_memory_to_message(self->impl->peer, self->impl->data_id, 6 ptr, size, msg); 7 ... 8 send_unlock_request(self->impl->comm, msg); 9 ... 10 }

juxmem_to_user_memory_from_message. Cette primitive permet d’extraire la donnée du message passé en paramètre pour mettre à jour l’espace mémoire du processus client pointé par ptr et d’une taille size octets. Elle est par exemple appelée par les protocoles de cohérence lors de la prise du verrou associé à la donnée.

Du côté fournisseur, le module mémoire bas offre les primitives décrites ci-dessous. Toutes ces primitives sont appelées par la couche des protocoles de cohérence.

juxmem_memory_malloc. Cette primitive permet d’allouer une collection de pages pour stocker une donnée de la taille passée en paramètre (en octets). La taille par défaut d’une page utilisée pour le stockage des données sur les fournisseurs est de 2048 ko (valeur paramétrable). Notons que si le support XDR est activé, la taille passée en pa-ramètre est multipliée par 4 pour tenir compte du surcoût en termes d’espace mémoire pour stocker la donnée encodée. Cette primitive est appelée sur les fournisseurs lors du traitement de demande d’allocation mémoire.

juxmem_memory_free. Cette primitive permet de libérer la collection de pages allouées pour la donnée dont l’identifiant est passé en paramètre. Elle est appelée lorsque la donnée est détruite sur le fournisseur.

juxmem_to_memory_from_message. Cette primitive permet d’ajouter dans le message passé en paramètre le contenu de la donnée, dont l’identifiant est passé en paramètre. Elle est appelée pour mettre à jour la copie de la donnée sur le fournisseur.

juxmem_from_memory_to_message. Cette primitive permet d’extraire du message passé en paramètre le contenu de la donnée, dont l’identifiant est passé en paramètre. Elle est appelée pour envoyer une copie de la donnée du fournisseur vers le client.

Implémentation. Le listing 7.3 présente le code utilisé pour le décodage d’un bloc de don-nées reçu dans un message du côté client. Le code présenté est celui exécuté lorsque l’option XDR est activée. Un des aspects qui guide la conception de JUXMEM est la performance de l’accès aux données. Ainsi, ce listing illustre le mécanisme utilisé pour minimiser le nombre de copies de la donnée lors de sa réception dans un message. Un mécanisme d’appel de fonctions préenregistrées disponible dans JXTA-C permet de copier directement une par-tie du message à l’adresse mémoire retournée par cette fonction (appelé mode zéro-copie). Sur la figure, cette fonction s’appelle memory_callback(ligne 1). Une description plus dé-taillée de ce mécanisme est donnée à la section 10.2.2. Dans l’exemple présenté, la fonction est appelée dès qu’un identifiant local de donnée enregistré est contenu dans le message2

(voir section 7.3.1 pour la définition d’un identifiant local de données). Ainsi, l’adresse cor-recte du tampon XDR de la donnée est calculée, en tenant compte de l’offset, puis retournée à JXTA-C (lignes 9 à 17). Ce dernier utilise ce tampon pour stocker la donnée contenue dans le message. Enfin, lors de l’appel à la primitivejuxmem_to_memory_from_message

(ligne 21), la donnée est décodée via XDR dans l’espace mémoire où le client a alloué la donnée (ligne 33). Dans le cas où l’option XDR n’est pas activée, la donnée extraite du mes-sage est directement copiée à l’adresse mémoire utilisée par le client, via notre mécanisme de fonctions pré-enregistrées.

Listing 7.3 – Code utilisé pour le décodage d’une donnée reçue dans un message, du côté client.

1 static void* JXTA_STDCALL memory_callback (const char *ns,

2 const char *element_name,

3 const char *element_mime_type) {

4 JuxMem_descriptor *descriptor = NULL;

5 void *user_buffer = NULL;

6 char *key = malloc(32);

7

8 user_buffer = (void *) apr_hash_get(self_static->user_buffers,

9 (const void *) ns, APR_HASH_KEY_STRING);

10

11 sprintf(key, "%li", (long) user_buffer);

12 descriptor = (JuxMem_descriptor *) apr_hash_get(self_static->client_descriptors,

13 (const void*) key,

14 (apr_ssize_t) strlen(key));

15 free(key);

16 return ((char *) descriptor->xdr_data_ptr) +

17 (atoi(element_name) * JUXMEM_CHUNK_SIZE);

18 }

19

20 int juxmem_to_user_memory_from_message(JuxMem_peer *peer, Jxta_id *data_id,

21 void *ptr, size_t size,

22 Jxta_message *message) {

23 JuxMem_peer_memory *self = juxmem_peer_get_memory(peer);

24 JuxMem_descriptor *descriptor = NULL;

25 u_int xdr_size = 0;

26 char *key = malloc(32);

27

28 sprintf(key, "%li", (long) ptr);

29 descriptor = (JuxMem_descriptor *) apr_hash_get(self->descriptors,

2Sur le listing 7.3, l’étape d’enregistrement de la fonction de rappel n’est pas représentée pour des raisons de lisibilité.

7.3 – Gestion des ressources mémoire 99

30 (const void*) key,

31 (apr_ssize_t) strlen(key));

32 xdr_bytes(descriptor->xdr_decode_ptr, (char **) &ptr, &xdr_size, size);

33

34 free(key);

35 return (int) TRUE;

36 }