Table des matières du Be Book     Index du kit du Noyau

Concepts d'image

Une image est du code compilé. Il y a trois types d'image :

Les sections suivantes expliquent comment charger et exécuter une image applicative, comment créer une librairie partagée, et comment créer et charger une image d'add-on.


Charger une image applicative

Charger une image applicative est comme exécuter un "sous-programme". L'image que vous chargez est lancée pratiquement de la même manière que si vous l'aviez double-cliqué dans le Tracker, ou lancée depuis la ligne de commande. Elle s'exécute dans sa propre team—elle ne partage pas l'espace d'adressage de l'application qui l'a lancée—et, habituellement, fait sa vie.

Toute application peut être chargée comme une image applicative; vous n'avez pas besoin d'effectuer des instructions spéciales de compilation, ni de manipuler le fichier binaire. L'unique exigence d'une image applicative est qu'elle doit avoir une fonction main().

Pour charger une image applicative, vous appellez la fonction load_image()  :

   thread_id load_image(int32 argc,
            const char **argv,
            const char **env)

Les deux premiers arguments de la fonction identifient l'image applicative (le fichier) que vous voulez lancer—nous reviendrons dessus dans un moment. Après avoir localisé le fichier, la fonction crée une nouvelle team, engendre un thread principal dans cette team, et vous retourne le thread_id du thread. Le thread n'est pas démarré : pour le démarrer vous passez le thread_id à resume_thread() ou wait_for_thread() (comme expliqué dans "Threads et Teams").

La paire argc/argv est copiée et transmise à la fonction main() du nouveau thread :

L'exemple suivant montre un usage typique de load_image(). D'abord, nous incluons les fichiers appropriés et déclarons les variables nécessaires :

   #include <image.h>  /* load_executable() */
   #include <OS.h>     /* wait_for_thread() */
   #include <stdlib.h> /* malloc() */
   
   char **arg_v; /* Choisissez un nom qui n'entre pas en conflit avec argv */
   int32 arg_c; /* Idem ici vis à vis de argc */
   thread_id exec_thread;
   int32 return_value;

Installons, dans le tableau arg_v, les arguments de la "ligne de commande". Disons que nous lançons un programme situé dans /boot/home/apps/adder qui prend deux entiers, les additionne, et retourne le resultat comme code de sortie de main(). Ainsi, il y a trois arguments : le nom du programme, et les valeurs des deux entiers converties en chaines. Comme il y a trois arguments, nous allouons arg_v pour contenir quatre pointeurs (pour recevoir le NULL final). Ensuite nous allouons et copions les arguments.

   arg_c = 3;
   arg_v = (char **)malloc(sizeof(char *) * (arg_c + 1));
   
   arg_v[0] = strdup("/boot/home/apps/adder");
   arg_v[1] = strdup("5");
   arg_v[2] = strdup("3");
   arg_v[3] = NULL;

Maintenant que tout est correctement assemblé, nous appellons load_image(). Après le retour de la fonction, c'est sans risque que nous libérons le tableau arg_v alloué :

   exec_thread = load_image(arg_c, arg_v, environ);
   
   while (--arg_c >= 0)
      free(arg_v[arg_c]);
   
   free(arg_v);

Rendu là, exec_thread est suspendu (l'état naturel d'un thread fraichement engendré). Afin de récupérer la valeur de retour, nous utilisons wait_for_thread() pour demander au thread de s'exécuter :

   wait_for_thread(exec_thread, &return_value);

Après le retour de wait_for_thread(), la valeur de return_value devrait être 8 (c.a.d. 5 + 3).


Créer une librairie partagée

La principale documentation pour créer une librairie partagée est fourni par MetroWerks dans leur manuel CodeWarrior. Au delà des informations que vous y trouverez, vous devez avoir conscience des rectifications et avertissements suivants :

Exporter et importer des symboles

Si vous developpez une librairie partagée vous devez explicitement exporter tout symbole global dans votre librairie en utilisant la macro __declspec(). Pour exporter un symbole, vous le déclarez ainsi...

   __declspec(dllexport) type name

...où "_declspec(dllexport)" est littéral, et type et name declare le symbole. Quelques exemples :

   __declspec(dllexport) char *some_name;
   __declspec(dllexport) void some_func() {...} 
   class __declspec(dllexport) MyView {...}

Pour importer ces symboles, une application qui veut utiliser votre librairie doit "inverser" la déclaration en remplaçant dllexport par dllimport :

   __declspec(dllimport) char *some_name;
   __declspec(dllimport) void some_func();
   class __declspec(dllimport) MyView;

L'ennui avec ce système est qu'il implique deux jeux d'entêtes, un pour l'exportation (pour développer votre librairie) et un autre pour l'importation (que le client de la librairie utilisera). Les librairies Be utilisent des macros, définies dans be/BeBuild.h, qui cache la bascule import/export afin que les fichiers d'entête puissent être unifiés. Par exemple, voici la macro pour libbe :

   #if _BUILDING_be
   #define _IMPEXP_BE     __declspec(dllexport)
   #else 
   #define _IMPEXP_BE __declspec(dllimport)
   #endif

Lorsque libbe est construite, une directive privée du compilateur définit _BUILDING_be à non nulle, et _IMPEXP_BE exporte les symboles. Lorsqu'un développeur inclut BeBuild.h, la variable _BUILDING_be est nulle, donc _IMPEXP_BE importe les symboles.

Vous voudrez peut-être émuler ce système en définissant des macros pour vos propres librairies. Cela implique que vous devrez définir une directive de compilation (semblable à _BUILDING_be) vous-même. Positionnez cette directive à une valeur non nulle lorsque vous construisez votre librairie, et ensuite à zéro lorsque vous publiez vos entêtes à utiliser par les clients de la librairie.


Créer et utiliser une image d'add-on

Une image d'add-on est indifférenciable d'une image de librairie partagée. Créer un add-on est exactement comme créer une librairie partagée, un sujet que l'on a traversé ci-dessus, mais avec deux différences mineures :

      $ echo $ADDON_PATH
      %A/add-ons:/boot/home/config/add-ons:/boot/beos/system/add-ons

Exporter les symboles d'un add-on

Pour exporter les symboles de votre add-on, déclarez-les ainsi :

   extern "C" __declspec(dllexport) void some_func();
   extern "C" __declspec(dllexport) int32 some_global_data;

Externaliser une classe C++ demande plus de travail. Vous ne pouvez pas externaliser une classe directement; typiquement ce que vous faites est de créer (et externaliser) une fonction C qui cache le constructeur de la classe :

   extern "C" __declspec(dllexport) MyClass *instantiate_my_class();

instantiate_my_class() est implémentée pour appeler le constructeur de MyClass :

   MyClass *instantiate_my_class()
   {
     return new MyClass();
   }

Charger une image d'add-on

Pour charger un add-on dans votre application, vous appellez la fonction load_add_on(). La fonction prend un chemin d'accès (absolu ou relatif au répertoire de travail actuel) au fichier de l'add-on, et retourne un image_id qui identifie de manière unique l'image à travers le système.

Par exemple, disons que vous avez créé une image d'add-on stockée dans le fichier /boot/home/add-ons/adder. Le code chargeant l'add-on ressemblera à ceci :

   /* Pour simplifier, nous ne véfirions pas les erreurs.  */
   image_id addon_image;
   
   /* Charger l'add-on. */
   addon_image = load_add_on("/boot/home/add-ons/adder");

Contrairement au chargement d'un exécutable, charger un add-on ne créer pas une team séparée, ni n'engendre un autre thread. L'objectif du chargement est d'amener l'image dans l'espace d'adressage de votre application afin que vous puissiez appeller les fonctions et jouer avec les variables que l'add-on définit.

Symboles

Après avoir chargé un add-on dans votre application, vous voudrez examiner les symboles (variables et fonctions) qu'il amène avec lui. Pour obtenir de l'information sur un symbole, vous appellez la fonction get_image_symbol() :

   status_t get_image_symbol(image_id image,
            char *symbol_name,
            int32 symbol_type,
            void **location)

Les trois premiers arguments de la fonction identifient le symbole que vous voulez observer :

La fonction retourne, par référence dans son argument final, un pointeur sur l'adresse du symbole. Par exemple, disons que le code de l'add-on adder soit celui-ci :

   extern "C" int32 a1 = 0;
   extern "C" int32 a2 = 0;
   extern "C" int32 adder(void);
   
   int32 adder(void)
   {
      return (a1 + a2);
   }

Pour examiner les variables (a1 et a2), vous appellez get_image_symbol() ainsi :

   int32 *var_a1, *var_a2; 
   
   get_image_symbol(addon_image, "a1", B_SYMBOL_TYPE_DATA, &var_a1);
   get_image_symbol(addon_image, "a2", B_SYMBOL_TYPE_DATA, &var_a2);

Ici nous obtenons le symbole de la fonction adder() :

   int32 (*func_add)();
   get_image_symbol(addon_image, "adder", B_SYMBOL_TYPE_TEXT, &func_add);

Maintenant que nous avons récupéré tous les symboles, nous pouvons affecter les valeurs des deux variables et appeller la fonction :

   *var_a1 = 5;
   *var_a2 = 3;
   int32 return_value = (*func_add)();

Table des matières du Be Book     Index du kit du Noyau


Le Be Book,
...en superbe HTML...
pour BeOS Release 5.

Copyright © 2000 Be, Inc. Tous droits réservés.
Traduit en Français par Philippe Houdoin

Copyright © 2000 Be, Inc. All rights reserved..