Table des matières du Be Book | Index du kit du Noyau |
Exemple 1 : Créer et écrire dans une area
Exemple 2 : Charger un fichier dans une area
Exemple 3 : Accéder une area particulière
Exemple 4 : Cloner et partager une area
Exemple 5 : Cloner des adresses
Comme exemple simple de création et d'usage d'une area, ici nous créons une area de dix pages et nous la remplissons à moitié (de valeurs quelconques) en baladant un pointeur :
area_id my_area; char *area_addr, *ptr; /* Créer une area. */ my_area = create_area("my area", /* Nom que vous donnez à l'area */ (void *)&area_addr, /* Retourne l'adresse de début */ B_ANY_ADDRESS, /* L'area peut démarrer n'importe où */ B_PAGE_SIZE*10, /* Taille en octets */ B_NO_LOCK, /* Verrouillée en RAM ? Non. */ B_READ_AREA | B_WRITE_AREA); /* Permissions */ /* Vérifier les erreurs */ if (my_area < 0) { printf("Quelque chose de mauvais est arrivé\n"); return; } /* Positionner ptr au début de l'area. */ ptr = area_addr; /* Remplir la moitié de l'area (avec des valeurs aléatoires). */ for (int i; i < B_PAGE_SIZE*5; i++) *ptr++ = system_time()%256;
Vous pouvez aussi appeller memcpy() et strcpy() sur l'area :
/* Copier la première moitié de l'area dans la seconde moitié. */ memcpy(ptr, area_addr, B_PAGE_SIZE*5); /* Ecraser le début de l'area. */ strcpy(area_addr, "He, regardez où je suis.");
Lorsqu'on a fini, on détruit l'area :
delete_area(my_area);
Ici, une fonction qui trouve un fichier, l'ouvre (implicite dans le constructeur BFile), et copie son contenu dans la RAM:
#include <File.h> area_id file_area; status_t file_reader(const char *pathname) { status_t err; char *area_addr; BFile file(pathname, B_READ_ONLY); if ((err=file.InitCheck()) != B_OK) { printf("%s: ouverture impossible.\n", pathname); return err; } err = file.GetSize(&file_size); if (err != B_OK || file_size == 0) { printf("%s: disparu ? vide ?\n", pathname); return err; } /* Arroundir la taille à la page supérieure. */ file_size = (((file_size-1)%B_PAGE_SIZE)+1)*B_PAGE_SIZE; /* Vérifier que la taille ne dépasse la capacité d'un size_t. */ if (file_size >= ((1<<32)-1) ) { printf("%s: que faites-vous ? Vous charger "Guerre et Paix" ?!?\n", pathname); return B_NO_MEMORY; } file_area = create_area("area du fichier", (void *)&area_addr, B_ANY_ADDRESS, file_size, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA); /* Vérifier les erreurs de create_area(), comme dans l'exemple précédant. */ ... /* Charger le fichier; détruire l'area s'il y a une erreur. */ if ((err=file.Read(area_addr, file_size)) < B_OK) { printf("%s: erreur de lecture.\n", pathname); delete_area(file_area); return err; } /* Le fichier est automatiquement fermé lorsque le BFile * sur la pile est détruit. */ return B_OK; }
Dans l'exemple précédant, une variable locale (area_addr) était utilisée pour capturer l'adresse de début de la nouvelle area. Si une autre fonction veut accéder à l'area, elle doit "retrouver" l'adresse de début (et la taille de l'area, pour les vérification des limites). Pour cela, vous appellez get_area_info().
Dans l'exemple suivant, une area est passée par nom; la fonction, qui va écrire son buffer en argument dans l'area, appelle get_area_info() pour déterminer le début et la dimension de l'area, et aussi pour être sûre que l'area appartient bien à sa team. Si l'area avait été créé par une autre team, la fonction pourrait toujours y écrire, mais elle devrait d'abord cloner l'area (le clonage est démontré dans le prochain exemple).
status_t write_to_area(const char *area_name, const void *buf, size_t len) { area_id area; area_info ai; thread_id thread; thread_info ti; status_t err; if (!area_name) return B_BAD_VALUE; area = find_area(area_name); /* L'a-t-on trouvé ? */ if (area < B_OK) { printf("Impossible de trouver l'area %s\n", area_name); return err; } /* Obtenir ses informations. */ err = get_area_info(area, &ai); if (err < B_OK) { printf("Impossible d'obtenir les infos de l'area\n"); return err; } /* Obtenir la team du thread appelant. Pour cela, nous devons * regarder dans la structure thread_info. */ err = get_thread_info(find_thread(NULL), &ti); if (err < B_OK) { printf("Impossible d'obtenir les infos du thread courant\n"); return err; } /* Comparer cette team avec celle de l'area. */ if (ai.team != ti.team) printf("Area étrangère\n"); return B_NOT_ALLOWED; } /* Vérifier que nous n'allons pas déborder de l'area, * et que l'on peut écrire dedans. */ if (len > ai.size) { printf("Buffer plus grand que l'area\n"); return B_BAD_VALUE; } if (!(ai.protection & B_WRITE_AREA)) { printf("Impossible d'écrire dans cette area\n"); return B_NOT_ALLOWED; } /* Maintenant nous pouvons écrire dedans. */ memcpy(ai.address, buf, len); return B_OK; }
Il est important que vous écriviez uniquement dans des areas crées ou clonées par la team appelant. L'adresse de début d'une area "étrangère" n'a habituellement pas de sens dans votre propre espace d'adressage.
Vous n'avez pas à vérifier la protection de l'area avant d'y écrire (ou de la lire). Les fonctions d'accès mémoire (memcpy(), dans cet exemple) générera une faute de segmentation (segfault) si une lecture ou un écriture invalide est demandée.
Dans l'exemple suivant, un serveur et un client partage une area commune. Voici le serveur :
/* Côté serveur */ class AServer { status_t make_shared_area(size_t size); area_id the_area; char *area_addr; }; status_t AServer::make_shared_area(size_t size) { /* La taille doit être arroundie à une page. */ size = ((size % B_PAGE_SIZE)+1) * B_PAGE_SIZE; the_area = create_area("server area", (void *)&area_addr B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA|B_WRITE_AREA); if (the_area < B_OK) { printf("Impossible de créer l'area serveur\n"); return the_area; return B_OK; }
Et voici le client :
/* Côté client */ class AClient { status_t make_shared_clone(); area_id the_area; char *area_addr; }; status_t AClient::make_shared_clone() { area_id src_area; src_area = find_area("server area"); if (src_area < B_ERROR) { printf("Impossible de trouver l'area du serveur\n"); return src_area; } the_area = clone_area("client area", (void *)&area_addr, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, src_area); if (the_area < B_OK) printf("Impossible de cloner l'area\n"); return the_area; } return B_OK; }
Notez que le créateur de l'area (le serveur dans l'exemple) n'a pas à signaler l'area crée comme partageable. Toutes les areas sont candidates au clonage.
Après avoir cloné l'area, l'area_id du client (AClient::the_area) sera différente de celle du serveur (AServer::the_area). Bien que les area_id soient globaux, le client doit uniquement réfèrer à l'area_id du serveur pour le cloner. Après clonage, le client "discute" à l'area via son propre area_id (la valeur retournée par clone_area()).
Il est parfois utile que des areas partagées (autrement dit, une "source" et un clone) démarrent à la même adresse de début. Par exemple, si l'area clone d'un client démarre à la même adresse que l'area originelle du serveur, alors le client et le serveur peuvent s'échanger des pointeurs sur l'area sans avoir à convertir les adresses. Ici nous modifions l'exemple précédant pour faire cela :
status_t AClient::make_shared_clone() { area_id src_area; src_area = find_area("server area"); if (src_area < B_ERROR) { printf("Impossible de trouver l'area du serveur"); return B_BAD_VALUE; } /* Cette fois-ci, on spécifie l'adresse où l'on veut que le * clone démarre. La constante B_CLONE_ADDRESS fait ça pour nous. */ area_addr = src_info.address; the_area = clone_area("client area", (void *)&area_addr, B_CLONE_ADDRESS, B_READ_AREA | B_WRITE_AREA,Impossible de cloner l'area\n src_area); if (the_area < B_OK) printf("Impossible de cloner l'area\n"); return the_area; } return B_OK; }
Bien sûr, demander qu'une area démarre à une adresse spécifique peut être trop restrictif; si de la mémoire dans [area_addr, area_addr + src_info.size] est déjà allouée, le clonage échouera.
Table des matières du Be Book | Index du kit du Noyau |