Le thème abordé dans cet article a fait l'objet d'un stage de six mois réalisé à Open Wide Ingénierie. Ce stage s'est déroulé en trois phases qui ont consisté essentiellement en :
- l'installation du système d'exploitation Android utilisant un noyau temps réel,
- l'étude du comportement temps réel du système,
- la réalisation d'une application « temps réel » pour Android.
Dans cet article, nous présenterons ainsi rapidement l'installation et le développement réalisé pour la mise en place d'un Android Temps Réel. Le but de la réalisation d'un tel système étant de bénéficier des avantages d'Android tout en respectant les contraintes temporelles associées à certaines tâches. Le choix d'utilisation du système d'exploitation Android est notamment pertinent lorsque l'on souhaite disposer d'une UI complète et intuitive, de l'intégration de la technologie tactile ainsi que d'une grande facilité de développement et d'intégration d'applications diverses sur le système. Le temps réel intervient principalement pour le pilotage matériel, l'acquisition de données et bien d'autres domaines qui nécessitent de garantir des délais précis.
L'installation du système a été réalisée pour une architecture de type ARM Cortex-A8 sur la carte
Beaglebone Black.
Intégration de Xenomai
L'installation d'Android pour la Beaglebone Black est basée sur le projet Rowboat qui propose la version Jelly Bean (4.2.2) du système d'exploitation.
Android étant basé sur un noyau Linux, nous avons opté pour l'installation de Xenomai afin de permettre la gestion du temps réel. Le noyau Linux est un noyau 3.8 adapté à la Beaglebone Black auquel on a appliqué les patchs Xenomai que l'on peut retrouver ici.
Lors de la configuration du noyau, il faut activer les options pour Xenomai ainsi que celle d'Android que l'on peut retrouver dans Device drivers → Staging drivers.
Il est à noter que Xenomai conseille fortement la désactivation des options APM, CPU frequency scaling et ACPI support que l'on retrouve au niveau du menu Power management and ACPI options pour garantir l'obtention de latences minimales.
Le projet AOSP est composé de nombreux Makefiles. Pour le compiler, on exécute la commande suivante à la racine du projet:
$ make TARGET_PRODUCT=beagleboneblack OMAPES=4.x droid
Le noyau Linux intégré par défaut dans le projet Rowboat est également compilé mais il n'est pas celui que nous utiliserons. Ici, Nous nous intéressons uniquement au système de fichier root que l'on peut récupérer sous forme compressée à l'aide du script mktarball.sh:
$ cd out/target/product/beagleboneblack # rowboat/build/tools/mktarball.sh rowboat/out/host/linux-x86/bin/fs_get_stats android_rootfs . rootfs rootfs.tar.bz2
La Beaglebone Black a pour avantage de démarrer directement à partir d'une carte microSD. L'installation du système se fait donc directement à l'aide du script mkmmc-android.sh situé dans le répertoire external/ti_android_utilities/am335x/mk-mmc. Ce script formate la microSD en quatre partitions et copie le noyau, le bootloader, le système de fichier root dans les partitions qui leur sont propres. Ces partitions nommées /boot, /rootfs, /data et /userdata, contiennent respectivement les fichiers de démarrage, le système de fichier et les répertoires de sauvegarde de données utilisateur.
$ sudo ./mkmmc-android.sh /dev/mmcblk0 MLO u-boot.img zImage uEnv.txt rootfs.tar.bz2
Remarque : quelques modifications sont à apporter aux fichiers contenus dans la partition boot après exécution de la commande précédente. L'image du kernel obtenue se nomme « uImage » mais doit être renommée en « zImage ». Il faut également ajouter le fichier am335x-boneblack.dtb qui contient la description du matériel et qui sera passé au noyau par U-boot.
Afin de pouvoir exécuter des programmes temps réel sous Android, il est nécessaire d'installer les bibliothèques et les fichiers d'en-têtes qui permettront d'utiliser les différentes APIs Xenomai ainsi que les programmes de tests et de mesure.
Remarque : une spécificité du user-space d'Android fait qu'il est nécessaire de créer un répertoire « lib » à la racine du système. Android n'utilisant pas la bibliothèque C standard de Linux mais une bibliothèque personnalisée, Bionic, qui n'intègre pas les bibliothèques nécessaires à l'exécution des programmes Xenomai.
Mesures et comparaison de latences
Xenomai intègre un certain nombre de programmes utiles pour tester et réaliser des mesures de latences. Nous nous sommes particulièrement intéressés à « latency » qui nous permet d'obtenir différentes latences et si il y a eu dépassement d'échéance.
RTH|----lat min|----lat avg|----lat max|-overrun|---msw|---lat best|--lat worst RTD| 7.291| 9.499| 43.499| 0| 0| 1.749| 59.708 RTD| 7.249| 9.166| 18.374| 0| 0| 1.749| 59.708 RTD| 6.416| 9.458| 39.166| 0| 0| 1.749| 59.708 ---|-----------|-----------|-----------|--------|------|----------------------- RTS| 1.749| 9.291| 59.708| 0| 0| 15:22:42/15:22:42
Extrait de la trace d'exécution de latency
La trace d'exécution précédente nous montre que les mesures ont été effectuées sur une durée supérieure à 15 heures et que la latence maximale atteinte lors de ce test est d'environ 60 microsecondes. On remarque également que la valeur du champs « overrun » est toujours à 0, preuve qu'il n'y a pas eu de dépassement d'échéances.
Les diverses mesures effectuées ont montré que les latences maximales atteintes varient entre les 59 et 70 microsecondes.
En comparant les résultats obtenus entre les systèmes Linux/Xenomai et Android/Xenomai, nous avons observé une différence de latences maximales d'environ 10 us minimum entre les deux systèmes. Les meilleurs résultats étant obtenus avec Linux/Xenomai. Or le système Linux dont nous disposions, généré avec Buildroot ne possédait aucune interface graphique. En ajoutant une interface graphique réalisé en Qt, nous avons observé une hausse de la latence maximale comme observé sur Android. Cette hausse de latence est due à l'utilisation de contrôleur graphique, mal supporté par Xenomai.
Réalisation d'une application temps réel
L'application réalisée a pour fonction la génération d'un signal en temps réel sur un GPIO de la carte. Concrètement elle doit permettre à un utilisateur de choisir la fréquence du signal qu'il souhaite générer en sortie, démarrer l'envoi du signal ou l'arrêter. La fréquence choisie est alors passée au programme temps réel qui se charge alors de générer une tâche périodique.
Android n'étant pas un système temps réel à la base, il a fallu adapter son architecture globale afin de pouvoir lancer un programme temps réel.
Pour réaliser cela, on a procédé en quatre étapes:
- implémentation du driver
- implémentation d'un programme temps réel de tâche périodique
- implémentation d'un programme de communication entre les domaines Android et Xenomai
- implémentation de l'application Android en Java
Le driver réalisé est un driver RTDM qui a pour fonction d'écrire la valeur reçue sur le GPIO. Cette fonction d'écriture est appelée périodiquement par un programme temps réel qui alterne les valeurs envoyées entre 0 et 1. La période à laquelle la fonction est appelée est calculée à partir de la fréquence reçue.
Or le domaine d'exécution d'Android est distinct du domaine Xenomai, c'est à dire qu'un programme du domaine Android ne peut pas utiliser de fonctions propres à Xenomai (à cause de Bionic entre autre). Il a donc fallu mettre en place un système de communication entre les deux domaines afin que le programme temps réel puisse récupérer la fréquence choisie par l'utilisateur. D'où l'implémentation d'un programme intermédiaire dont la tâche est de recevoir la fréquence envoyée par l'application Android au travers d'un socket et de l'envoyer au programme temps réel à l'aide du protocole de communication « XDDP » de Xenomai. XDDP étant un protocole de communication temps réel propre à Xenomai permettant d'échanger des données entre une tâche appartenant à un domaine non temps réel (NRT) et une autre appartenant un à domaine temps réel (RT).
* realtime_thread-------------------------------------->----------+ * => get socket | * => bind socket to port 0 v * => write scattered traffic to NRT domain via sendto() | * => read traffic from NRT domain via recvfrom() open /dev/rtp0 * | ^ * => read traffic from RT domain via read() | | * => echo traffic back to RT domain via write() +--+
Extrait de xddp-stream.c: Communication RT/NRT avec XDDP
Une fois ce mécanisme mis en place, la suite concerne essentiellement le domaine Android.
Pour simplifier le développement de la partie applicative qui est réalisée en Java, nous avons réalisé une extension du framework Android (pour plus de détails je vous renvoie vers l'article Introduction à la HAL Android). Cette extension a pour but d'intégrer une nouvelle bibliothèque au SDK d'Android qui permet de créer un objet ayant accès à une fonction spécifique permettant d'envoyer la valeur de la fréquence au programme intermédiaire. On fait alors appel au code suivant dans l'application:
import android.os.SquareSignalManager;
[...]
mSquareSignalManager = (SquareSignalManager)this.getSystemService(Context.SQUARESIGNAL_SERVICE);
[...]
mSquareSignalManager.send(frequence);
Afin de pouvoir intégrer la nouvelle bibliothèque, les sources de l'application doivent être placées dans le répertoire rowboat/package/apps. Il faut alors recompiler AOSP qui intégrera les modifications réalisées pour l'extension du framework et compilera l'application. L'application compilée doit être intégrée dans le répertoire system/app du système de fichiers. Ce répertoire contenant toutes les applications installées par défaut sur le système.
Pour disposer du driver RTDM et des programmes temps réel au démarrage du système, nous avons modifié le script init.rc. Ce script est exécuté par le processus init et permet entre autre de pouvoir charger les différentes bibliothèques et démarrer les services du système d'exploitation. On le trouve dans le répertoire system/core/rootdir/ du projet Rowboat.
On y place donc la commande d'insertion du driver :
insmod /system/lib/modules/signalRT.ko
Ainsi que les deux programmes développés sous la forme de service de la façon suivante :
service signalRT /system/bin/generateSignalRT class main oneshot service socketServer /system/bin/communicationServer class main oneshot
On peut se référer à l'article Customisation d'AOSP, partie « Ajout de service », pour tout ce qui concerne les services.
Conclusion
Cette investigation nous a permis de nous rendre compte de la possibilité d'adaptation du système d'exploitation Android au temps réel. Connaissant un très grand succès et étant de plus en plus performant, le système d'exploitation le plus utilisé sur les smartphones est de plus en plus embarqué sur des plate-formes diverses autres que les smartphones. D'où l’intérêt de se pencher sur la question de l'intégration du temps réel. Notamment avec l'arrivée sur le marché des smartphones de BlackBerry 10 OS, basé sur le système Neutrino Realtime Operating System (RTOS) de QNX, le temps réel semble de plus en plus intéresser le monde du mobile.