Archive for novembre 2011

Xenomai sur Pandaboard

Embarqué, Linux, Temps-réel | Publié par cpb
nov 27 2011

L’installation de la dernière version de Xenomai (2.6.0) sur une carte Pandaboard ne devrait en principe pas présenter de difficultés. En principe. Mais en pratique je crois être tombé dans tous les pièges possibles avant d’arriver à faire fonctionner correctement mon système. Je vous fais grâce de mes mésaventures, et voici donc un petit résumé des opérations à réaliser pour pouvoir disposer de temps-réel strict sur la carte Pandaboard.

Chaîne de compilation

J’ai habituellement un regard assez dubitatif sur les personnes qui accusent le compilateur de ne pas fournir le code attendu. Il a quand même fallu que je finisse par me poser la question lorsque le noyau modifié et les bibliothèques Xenomai étaient incompatibles et que j’étais obligé de modifier manuellement des Makefile pour forcer la compilation. En fait les options de compilation de Xenomai (notamment FASTSYNCH) dépendent du type de processeur cible. Et ce dernier est déterminé en interrogeant le compilateur… Alors que dans le noyau Linux les options dépendent de la configuration choisie manuellement lors du make menuconfig.
Mon tort avait été d’utiliser une chaîne de compilation Arm générique, que j’avais préparée pour une carte IGEPv2 comme je l’avais décrit dans cet article.

Pour que la compilation des bibliothèques et applications Xenomai se passe bien, il faut utiliser une toolchain adaptée au processeur de la Pandaboard. Ce dernier est basé sur un coeur Cortex A9. Voici la préparation de la toolchain avec la dernière version de Buildroot.

[~]$ cd Projets/Panda/
[Panda]$ wget http://buildroot.uclibc.org/downloads/buildroot-2011.08.tar.bz2
--2011-11-27 06:52:17--  http://buildroot.uclibc.org/downloads/buildroot-2011.08.tar.bz2
Résolution de buildroot.uclibc.org... 140.211.167.224
Connexion vers buildroot.uclibc.org|140.211.167.224|:80... connecté.
requête HTTP transmise, en attente de la réponse... 200 OK
Longueur: 1838467 (1,8M) [application/x-bzip2]
Sauvegarde en : «buildroot-2011.08.tar.bz2.1»

100%[=======================================================================>] 1 838 467   64,7K/s   ds 39s     

2011-11-27 06:52:57 (45,9 KB/s) - «buildroot-2011.08.tar.bz2.1» sauvegardé [1838467/1838467]

[Panda]$ tar xjf buildroot-2011.08.tar.bz2 
[Panda]$ cd buildroot-2011.08/
[buildroot-2011.08]$ wget http://www.blaess.fr/christophe/files/article-2011-11-27/config-buildroot-2011.08-Pandaboard
--2011-11-27 06:53:48--  http://www.blaess.fr/christophe/files/article-2011-11-27/config-buildroot-2011.08-Pandaboard
Résolution de www.blaess.fr... 217.16.3.18
Connexion vers www.blaess.fr|217.16.3.18|:80... connecté.
requête HTTP transmise, en attente de la réponse... 200 OK
Longueur: 18679 (18K) [text/plain]
Sauvegarde en : «config-buildroot-2011.08-Pandaboard»

100%[=======================================================================>] 18 679      31,1K/s   ds 0,6s    

2011-11-27 06:53:49 (31,1 KB/s) - «config-buildroot-2011.08-Pandaboard» sauvegardé [18679/18679]

[buildroot-2011.08]$ mv config-buildroot-2011.08-Pandaboard .config
[buildroot-2011.08]$ make menuconfig

Éditez l’option « Host dir » du menu « Build options » pour choisir le chemin absolu d’installation de la toolchain. Par défaut le chemin est /home/cpb/cross-panda ce qui correspond à l’installation dans mon répertoire personnel. Pensez donc à l’adapter à votre environnement. Puis lancez la compilation avec

[buildroot-2011.08]$ make

La durée de cette étape varie très nettement suivant la puissance du processeur, l’activité du système et la vitesse du réseau. Disons qu’elle se mesure en dizaines de minutes. Elle nous fournira seulement la chaîne de compilation, je n’ai rien sélectionné d’autre dans la configuration de Buildroot (mais rien ne vous en empêche). La compilation se terminera ainsi :

[...]
for dir in ; \
	do \
		for lang in $(cd $dir; ls .|grep -v man); \
		do \
			grep -qx $lang /home/cpb/Projets/Panda/buildroot-2011.08/output/build/locales.nopurge || rm -rf $dir/$lang; \
		done; \
	done
rm -f /home/cpb/Projets/Panda/buildroot-2011.08/output/build/.fakeroot*
[buildroot-2011.08]$

Compilation du noyau Linux

Pour savoir quel noyau Linux utiliser, il nous faut d’abord télécharger la version désirée de Xenomai et regarder quels patches sont disponibles.

[~]$ cd ~/Projets/Panda/
[Panda]$ wget http://download.gna.org/xenomai/stable/xenomai-2.6.0.tar.bz2
--2011-11-27 07:23:30--  http://download.gna.org/xenomai/stable/xenomai-2.6.0.tar.bz2
Résolution de download.gna.org... 78.40.125.79
Connexion vers download.gna.org|78.40.125.79|:80... connecté.
requête HTTP transmise, en attente de la réponse... 200 OK
Longueur: 21068430 (20M) [application/x-bzip2]
Sauvegarde en : «xenomai-2.6.0.tar.bz2»

100%[=========================================================================>] 21 068 430  54,5K/s   ds 3m 45s  

2011-11-27 07:27:16 (91,5 KB/s) - «xenomai-2.6.0.tar.bz2» sauvegardé [21068430/21068430]

[Panda]$ tar xjf xenomai-2.6.0.tar.bz2 
[Panda]$ ls xenomai-2.6.0/ksrc/arch/arm/patches/
adeos-ipipe-2.6.35.9-arm-1.18-03.patch  adeos-ipipe-2.6.37.6-arm-1.18-03.patch  adeos-ipipe-2.6.38.8-arm-1.18-04.patch  mxc  README
[Panda]$

La version 2.6.0 de Xenomai propose donc un patch pour le noyau Linux 2.6.38.8. Nous allons donc télécharger ce dernier.

[Panda]$ wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.38.8.tar.bz2
--2011-11-27 07:34:33--  http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.38.8.tar.bz2
Résolution de www.kernel.org... 149.20.4.69
Connexion vers www.kernel.org|149.20.4.69|:80... connecté.
requête HTTP transmise, en attente de la réponse... 200 OK
Longueur: 74811146 (71M) [application/x-bzip2]
Sauvegarde en : «linux-2.6.38.8.tar.bz2»

100%[=========================================================================>] 74 811 146  1,41M/s   ds 88s     

2011-11-27 07:36:01 (831 KB/s) - «linux-2.6.38.8.tar.bz2» sauvegardé [74811146/74811146]

[Panda]$ tar xjf linux-2.6.38.8.tar.bz2
[Panda]$

Ce noyau pose un souci pour la Pandaboard en ce qui concerne l’initialisation du limitateur de courant. La conséquence est que la carte risque de s’éteindre automatiquement dès que l’on exécute une opération qui réclame une augmentation de la consommation électrique (communication sur port série, etc.) Heureusement il existe un patch pour la série des Linux 2.6.38 permettant de corriger ce problème. J’ai passé quelques heures à modifier la configuration du noyau avant de le découvrir… Appliquons ce patch.

[Panda]$ cd linux-2.6.38.8/
[linux-2.6.38.8]$ wget http://www.blaess.fr/christophe/files/article-2011-11-27/pandaboard-current-2.6.38.patch
--2011-11-27 07:46:56--  http://www.blaess.fr/christophe/files/article-2011-11-27/pandaboard-current-2.6.38.patch
Résolution de www.blaess.fr... 217.16.3.18
Connexion vers www.blaess.fr|217.16.3.18|:80... connecté.
requête HTTP transmise, en attente de la réponse... 200 OK
Longueur: 1745 (1,7K) [text/plain]
Sauvegarde en : «pandaboard-current-2.6.38.patch»

100%[==========================================================================>] 1 745       --.-K/s   ds 0s      

2011-11-27 07:46:56 (32,2 MB/s) - «pandaboard-current-2.6.38.patch» sauvegardé [1745/1745]

[linux-2.6.38.8]$ patch -p1 < pandaboard-current-2.6.38.patch 
patching file arch/arm/mach-omap2/board-omap4panda.c
[linux-2.6.38.8]$ cd ..
[Panda]$

Avant de compiler le noyau, il convient de le modifier pour y incorporer les ajouts de Xenomai (notamment ipipe, le pipeline d’interruption bas-niveau).

[Panda]$ cd xenomai-2.6.0/
[xenomai-2.6.0]$ scripts/prepare-kernel.sh --linux=../linux-2.6.38.8 --adeos=ksrc/arch/arm/patches/adeos-ipipe-2.6.38.8-arm-1.18-04.patch --arch=arm
patching file arch/arm/Kconfig
patching file arch/arm/boot/compressed/head.S
patching file arch/arm/common/gic.c
patching file arch/arm/common/it8152.c
patching file arch/arm/common/timer-sp.c
[...]
patching file mm/mmu_context.c
patching file mm/mprotect.c
patching file mm/vmalloc.c
[xenomai-2.6.0]$ cd ..
[Panda]$

Puis compilons enfin notre noyau Linux modifié. Pour cela j’ai préparé rapidement un fichier de configuration qui pourrait être nettement amélioré. J’en profite également pour renommer le répertoire afin de pouvoir éventuellement disposer d’une version 2.6.38.8 sans Xenomai pour comparaison.

[Panda]$ mv linux-2.6.38.8 linux-2.6.38.8-xenomai
[Panda]$ cd linux-2.6.38.8-xenomai/
[linux-2.6.38.8-xenomai]$ wget http://www.blaess.fr/christophe/files/article-2011-11-27/config-linux-2.6.38.8-xenomai
--2011-11-27 08:00:43--  http://www.blaess.fr/christophe/files/article-2011-11-27/config-linux-2.6.38.8-xenomai
Résolution de www.blaess.fr... 217.16.3.18
Connexion vers www.blaess.fr|217.16.3.18|:80... connecté.
requête HTTP transmise, en attente de la réponse... 200 OK
Longueur: 66509 (65K) [text/plain]
Sauvegarde en : «config-linux-2.6.38.8-xenomai»

100%[==========================================================================>] 66 509       260K/s   ds 0,2s    

2011-11-27 08:00:43 (260 KB/s) - «config-linux-2.6.38.8-xenomai» sauvegardé [66509/66509]

[linux-2.6.38.8-xenomai]$ mv config-linux-2.6.38.8-xenomai .config
[linux-2.6.38.8-xenomai]$ make ARCH=arm menuconfig

En vous promenant dans le menu de configuration, vous apercevrez un nouveau sous-menu « Realtime Subsystem » correspondant à la configuration de Xenomai notamment de ses « skins » qui permettent de reproduire l’interface de programmation de différents systèmes temps-réel (RTAI, VxWorks, pSos+, etc.). Nous pouvons à présent lancer la compilation du noyau.

[linux-2.6.38.8-xenomai]$ make ARCH=arm CROSS_COMPILE=~/cross-panda/usr/bin/arm-linux- uImage modules
  CHK     include/linux/version.h
  UPD     include/linux/version.h
  CHK     include/generated/utsrelease.h
  UPD     include/generated/utsrelease.h
[...]
  CC      fs/nls/nls_iso8859-15.mod.o
  LD [M]  fs/nls/nls_iso8859-15.ko
  CC      fs/nls/nls_utf8.mod.o
  LD [M]  fs/nls/nls_utf8.ko
  CC      net/bluetooth/l2cap.mod.o
  LD [M]  net/bluetooth/l2cap.ko
  IHEX    firmware/kaweth/new_code.bin
  IHEX    firmware/kaweth/trigger_code.bin
  IHEX    firmware/kaweth/new_code_fix.bin
  IHEX    firmware/kaweth/trigger_code_fix.bin
[linux-2.6.38.8-xenomai]$

Installation du noyau modifie

Pour l’installation, j’insère la carte micro-SD de la Pandaboard dans un connecteur sur mon poste de travail, où le système de fichiers principal se trouve monté en /media/root et la partition de démarrage (contenant le bootloader) sur /media/boot. Sur ma carte se trouve un système de fichiers minimal avec une configuration de Busybox et Dropbear comme décrit dans cet article et celui-ci.

Attention, si vous avez déjà installé une toolchain en suivant mes premiers articles sur la Pandaboard, il faut absolument installer les nouvelles bibliothèques sinon vous ne pourrez pas utiliser celles de Xenomai (et resterez bloqués comme moi pendant plusieurs heures à jouer à tort sur sa configuration). Il faut donc les copier ainsi :

[linux-2.6.38.8-xenomai]$ rm -rf /media/root/lib/*
[linux-2.6.38.8-xenomai]$ cp -Rdp ~/cross-panda/usr/arm-linux/sysroot/lib /media/root/
[linux-2.6.38.8-xenomai]$

On peut installer le noyau et ses modules ainsi :

[linux-2.6.38.8-xenomai]$ cp arch/arm/boot/uImage /media/boot/
[linux-2.6.38.8-xenomai]$ make ARCH=arm INSTALL_MOD_PATH=/media/root/ modules_install
  INSTALL drivers/hid/hid-gaff.ko
  INSTALL drivers/hid/hid-samsung.ko
  INSTALL drivers/hid/hid-sjoy.ko
  [...]
  INSTALL /media/root//lib/firmware/kaweth/trigger_code.bin
  INSTALL /media/root//lib/firmware/kaweth/new_code_fix.bin
  INSTALL /media/root//lib/firmware/kaweth/trigger_code_fix.bin
  DEPMOD  2.6.38.8-xenomai-cpb
[linux-2.6.38.8-xenomai]$ umount /media/*oot
[linux-2.6.38.8-xenomai]$

Après avoir démarré la Pandaboard, et s’être connecté par SSHs, nous observons deux nouvelles entrées dans /proc.

[Panda]# ls /proc/ipipe/
Linux    Xenomai  version
[Pandaboard]# ls /proc/xenomai/
acct          apc           heap          irq           lock          sched         stat          timer         version
affinity      faults        interfaces    latency       registry      schedclasses  timebases     timerstat
[Pandaboard]#

La première correspond à Adeos (renommé ipipe pour interrupt pipeline) et la seconde au domaine de Xenomai, dans lequel seront ordonnancées les futures tâches temps-réel.

Bibliothèques de Xenomai

Les bibliothèques de Xenomai doivent être compilées et installées pour que les tâches de l’espace utilisateur puissent accéder aux fonctionnalités temps-réel. Certaines options de configuration ont changé depuis les dernières versions de Xenomai (notamment l’option --enable-smp dont je cherchais en vain la présence dans la configuration du kernel).

Pour compiler ces bibliothèques nous utiliserons les manipulations suivantes. Attention, dans la ligne « ./configure« , précisez le chemin dans lequel les bibliothèques et exécutables de Xenomai seront installés sur votre poste de développement (en modifiant donc le chemin qui est prévu pour mon répertoire).

[Panda]$ cd xenomai-2.6.0/
[xenomai-2.6.0]$ PATH=$PATH:~/cross-panda/usr/bin/
[xenomai-2.6.0]$ ./configure --prefix=/home/cpb/Projets/Panda/xenomai --host=arm-linux CFLAGS='-march=armv7-a' LDFLAGS='-march=armv7-a' --enable-smp
configure: WARNING: if you wanted to set the --build type, don't use --host.
    If a cross compiler is detected then cross compile mode will be used
checking build system type... i686-pc-linux-gnu
checking host system type... arm-unknown-linux-gnu
checking for a BSD-compatible install... /usr/bin/install -c
checking for arm-linux-gcc... arm-linux-gcc
checking whether the C compiler works... yes
[...]
config.status: executing depfiles commands
config.status: executing libtool commands
[xenomai-2.6.0]$ make 
Making all in src
make[1]: entrant dans le répertoire « /home/cpb/Projets/Panda/xenomai-2.6.0/src »
Making all in include
[...]
make[1]: entrant dans le répertoire « /home/cpb/Projets/Panda/xenomai-2.6.0 »
make[1]: Rien à faire pour « all-am ».
make[1]: quittant le répertoire « /home/cpb/Projets/Panda/xenomai-2.6.0 »
[xenomai-2.6.0]$ make install
[...]
make[2]: Rien à faire pour « install-data-am ».
make[2]: quittant le répertoire « /home/cpb/Projets/Panda/xenomai-2.6.0 »
make[1]: quittant le répertoire « /home/cpb/Projets/Panda/xenomai-2.6.0 »
[xenomai-2.6.0]$ cd ..
[Panda]$

Transférons les exécutables et les bibliothèques en utilisant scp (s’il n’est pas installé sur votre Pandaboard, faites une copie directe avec la carte micro-SD).

[Panda]$ cd xenomai
[xenomai]$ ls
bin  include  lib  sbin  share
[xenomai]$ scp -r bin/ lib/ sbin/ root@192.168.3.152:/usr/
root@192.168.3.152's password:
xeno-test-run-wrapper                                                100%  181     0.2KB/s   00:00
xeno-test-run                                                        100%   17KB  17.3KB/s   00:00
insn_read                                                            100%   14KB  14.1KB/s   00:00
[...]
analogy_config                                                       100% 9300     9.1KB/s   00:00
rtcanconfig                                                          100% 8493     8.3KB/s   00:00
rtps                                                                 100%   22KB  22.2KB/s   00:00
[xenomai]$

Test de Xenomai

Après s’être connecté sur Xenomai, nous pouvons lancer les tests se trouvant installés dans le répertoire /usr/bin. Il y en a plusieurs que je vous laisse découvrir, voyons juste le résultat d’un outil que j’aime bien : cyclictest qui mesure la variabilité des timers. Il en existe une version utilisant l’API système Linux classique (que l’on trouve dans le projet Linux-rt) et un portage utilisant l’API de Xenomai. Voyons un exemple où nous déclenchons un timer toutes les 100 micro-secondes et observons le jitter.

[Panda]# cd /usr/bin
[Panda]# ./cyclictest -i 100
0.47 0.17 0.09 1/50 11834          

T: 0 (11809) P:99 I:     100 C: 2372138 Min:       0 Act:       4 Avg:       0 Max:      17

En outre, l’utilitaire latency donne des résultats comparables.

[Panda]# ./latency -p 100
== Sampling period: 100 us
== Test mode: periodic user-mode task
== All results in microseconds
warming up...
RTT| 00:00:01 (periodic user-mode task, 100 us period, priority 99)
RTH|----lat min|----lat avg|----lat max|-overrun|---msw|---lat best|--lat worst
RTD| 0.652| 0.837| 9.404| 0| 0| 0.652| 9.404
RTD| 0.498| 0.857| 8.105| 0| 0| 0.498| 9.404
[...]
RTT| 04:28:07 (periodic user-mode task, 100 us period, priority 99)
RTH|----lat min|----lat avg|----lat max|-overrun|---msw|---lat best|--lat worst
RTD| 0.460| 0.869| 9.210| 0| 0| -0.116| 17.025
RTD| 0.478| 0.869| 8.599| 0| 0| -0.116| 17.025
RTD| 0.511| 0.869| 8.744| 0| 0| -0.116| 17.025
RTD| 0.494| 0.873| 9.376| 0| 0| -0.116| 17.025
RTD| 0.499| 0.871| 9.029| 0| 0| -0.116| 17.025
RTD| 0.523| 0.869| 8.698| 0| 0| -0.116| 17.025

Au bout de quatre heure et demi de travail, la variation maximale observée est de 17 micro-secondes, ce qui est excellent pour une Pandaboard. A titre d’exemple, avec un noyau Linux 3.0 standard, j’ai observé un retard maximal de plus de 500 micro-secondes (avec un timer cadencé à la milli-seconde), et un décalage maximal de 58 micro-secondes avec un noyau 3.0 auquel le patch Linux-rt a été appliqué.

[ACTU] RTwiki de retour !

Sans catégorie | Publié par cpb
nov 15 2011

Le site RTwiki, victime collatérale de l’attaque d’août dernier sur Kernel.org, a longtemps été désactivé. Le voici de retour depuis quelques heures.

Comme d’habitude on y trouve de nombreuses informations sur le projet Linux-rt qui visiblement suit au plus près les évolutions du noyau standard (version 3.2-rc1 en l’occurrence).

Les patches de Thomas Gleixner vont progressivement regagner leur répertoire d’origine, toutefois, les plus récents se trouvent encore sur son site personnel.

 

 

Expérimentation sur la préemptibilité du noyau Linux

Embarqué, Linux, Microprocesseur, Temps-réel | Publié par cpb
nov 04 2011

Cet article est extrait de la version préparatoire de mon livre « Solutions temps-réel sous Linux » (parution envisagée au début 2012).

J’ai eu envie de mettre en évidence la différence de comportement entre un noyau préemptible (avec l’option CONFIG_PREEMPT activée durant sa compilation) et un noyau non-préemptible classique. Toutefois, cette mise en évidence n’est pas très simple, car elle concerne précisément des cas rares et difficiles à  reproduire.

Nous allons nous intéresser à une interruption déclenchée par le port de communication série RS-232. Nous allons envoyer un caractère sur une liaison série à destination d’un système Linux sur lequel fonctionnera un processus temps-réel qui renverra le même caractère dans l’autre sens sur la même liaison série.

Mise en œuvre

L’expérience met en œuvre une carte à processeur Arm (Pandaboard, à gauche sur la photo ci-dessous) fonctionnant avec un noyau Linux 3.0. Ce choix s’explique par le fait que la Pandaboard dispose d’un véritable port série RS-232 possédant sa propre interruption – et non d’une émulation basée sur un contrôleur USB comme la plupart des PC actuels. Nous essaierons de mesurer précisément le temps de réponse de notre processus temps-réel et voir s’il varie sous l’influence d’un processus de moindre priorité.

Pour effectuer cette mesure, j’utiliserai une carte de développement (STK500, à droite sur la photo) pour micro-contrôleurs Atmel, plus particulièrement ici un ATmega32 (inséré dans le support de programmation rouge sur la partie gauche de la carte). Ce dernier fonctionne sans système d’exploitation et nous permet un contrôle direct et sans perturbation de la liaison série. Le micro-contrôleur enverra le caractère et mesurera le temps écoulé jusqu’à la réception du caractère renvoyé.

 

Pandaboard et STK500

Voici le programme qui est compilé puis chargé dans le micro-contrôleur (à l’aide des utilitaires avr-gcc et avr-dude qui sont librement disponible sous Linux). Mentionnons pour le lecteur non habitué à la programmation de micro-contrôleur que les constantes UDR, UCSRA, UCSRB, UCSRC, UBRRH et UBRRL représentent des accès directs aux registres du micro-contrôleur.

exemple-mesure-atmega.c :
// Declarer la frequence du micro-controleur (1MHz)
#define F_CPU 1000000
// Inclusion d'entetes specifiques au micro-controleur
#include <avr/io.h>
#include <util/delay.h>

void envoyer_octet (unsigned char octet)
{
    // Attendre buffer emission libre
    while ((UCSRA & (1<<UDRE)) == 0)
        ;
    // Ecriture sur le port Usart Data Register
    UDR = octet;
}

int main(void)
{
    unsigned char uc;
    unsigned long int nb_boucles;

    // Parametrage du port de communication
    // 12 -> 9600 bits/sec
    UBRRH = (unsigned char) (0);
    UBRRL = (unsigned char) (12);
    // Clock x 2
    UCSRA = (1 << U2X);
    // TX et RX actives
    UCSRB = (1 << RXEN) | (1 << TXEN);
    // 8 bits, 1 stop, pas de parite
    UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);

    while (1) {
        nb_boucles = 0;

        envoyer_octet(0xFF);
        // Attendre 1 octet recu
        while ((UCSRA & (1 << RXC)) == 0)
            nb_boucles ++;
        // Lire l'octet
        uc = UDR;

        envoyer_octet((nb_boucles >> 24) & 0xFF);
        _delay_ms(2);
        envoyer_octet((nb_boucles >> 16) & 0xFF);
        _delay_ms(2);
        envoyer_octet((nb_boucles >> 8 ) & 0xFF);
        _delay_ms(2);
        envoyer_octet((nb_boucles >> 0 ) & 0xFF);
        _delay_ms(500);
    }
    return 0;
}

On peut voir que ce programme envoie un octet (0xFF) sur le port série et boucle – en incrémentant une variable – tant qu’il n’a pas reçu de réponse. La durée de la boucle importe peu. Chaque itération dure environ 10 micro-secondes, et on pourrait la calibrer précisément, mais ceci ne présente pas de véritable intérêt. Ce n’est pas le nombre absolu de boucles effectuées qui nous concerne, mais plutôt les variations de ce nombre au cours des essais successifs. Une fois la réponse obtenue, nous envoyons sur le même port série le nombre de boucles effectuées. Le petit sommeil de 2 ms entre les octets sert à garantir la réception par le correspondant car il n’y a pas de contrôle de flux (CTS/RTS, DTS/DSR, etc.) sur la carte à micro-contrôleur employée ici.

Le programme qui fonctionne sur la Pandaboard sous Linux est le suivant :

exemple-reponse-irq-serie.c  :
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>

volatile int quitter = 0;

void handler_sigint(int unused)
{
    quitter = 1;
}

int main(int argc, char * argv[])
{
    int fd;
    unsigned char  octet;
    int nb_cycles;
    struct termios parametres;
    struct termios original;

    if (argc != 2) {
        fprintf(stderr, "usage: %s port_serie\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    signal(SIGINT, handler_sigint);

    if ((fd = open(argv[1], O_RDWR | | O_NONBLOCK))<0){
        perror(argv[1]);
        exit(EXIT_FAILURE);
    }

    tcgetattr(fd, & original);
    tcgetattr(fd, & parametres);
    cfmakeraw(& parametres);
    cfsetispeed(& parametres, B9600);
    cfsetospeed(& parametres, B9600);
    parametres.c_iflag |= IGNPAR;
    parametres.c_cflag ^= (CSIZE | PARENB | CSTOPB);
    parametres.c_cflag |= CS8 | CLOCAL;
    if (tcsetattr(fd, TCSANOW, & parametres) != 0) {
        perror("tcsetattr");
        exit(EXIT_FAILURE);
    }
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) ^ O_NONBLOCK);

    while (! quitter) {
        if (read(fd, & octet, 1) < 0) {
            perror(argv[1]);
            exit(EXIT_FAILURE);
        }
        if (write(fd, & octet, 1) < 0) {
            perror(argv[1]);
            exit(EXIT_FAILURE);
        }
        nb_cycles = 0;
        read(fd, & octet, 1);
        nb_cycles |= octet;
        nb_cycles <<= 8;
        read(fd, & octet, 1);
        nb_cycles |= octet;
        nb_cycles <<= 8;
        read(fd, & octet, 1);
        nb_cycles |= octet;
        nb_cycles <<= 8;
        read(fd, & octet, 1);
        nb_cycles |= octet;
        fprintf(stdout, "%d\n", nb_cycles);
    }
    tcsetattr(fd, TCSANOW, & original);
    close(fd);
    fprintf(stderr, "Bye !\n");
    return EXIT_SUCCESS;
}

Le programme attend donc un caractère, le renvoie en écho, récupère les quatre octets représentant son temps de réponse et les affiche sur sa sortie standard. Voici un exemple d’exécution sur un noyau non-préemptible.

[Panda]# ./exemple-reponse-irq-serie /dev/ttyO2
290
287
289
287
[...]
287
287
286
286
(Contrôle-C)
Bye !
[Panda]#

Appel-système long

Pour perturber le fonctionnement du programme, j’ai réalisé un petit script shell qui charge (avec insmod) et décharge (avec rmmod) toutes les cinq secondes ce petit module du kernel :

module-delay.c  :
#include <linux/module.h>

static int __init module_delay_init (void)
{
    unsigned long fin = jiffies + HZ/2; // 500 ms
    while(time_before(jiffies, fin))
        ;
    return 0;
}

static void __exit module_delay_exit (void)
{}

module_init(module_delay_init);
module_exit(module_delay_exit);
MODULE_LICENSE("GPL");

Ce module effectue une boucle active de 500 millisecondes dès son chargement dans le kernel. Ceci nous permet de disposer d’un appel-système suffisamment long pour perturber le temps de réponse à une interruption sur un système non-préemptible. Une remarque : j’ai également monté le thread kernel kworker qui est utilisé pour gérer une partie de l’interruption série à la priorité Fifo 50.

Résultats sur système non-préemptible

[NB: Pour étudier les résultats, après les avoir redirigés dans un fichier, j'utilise un ensemble de petits outils développés précédement dans le livre]

[Panda]# uname -a
Linux (Pandaboard) 3.0.0-cpb #4 SMP Mon Oct 31 19:52:43 CET 2011 armv7l GNU/Linux
[Panda]# taskset -p 1  $$
pid 547's current affinity mask: 3
pid 547's new affinity mask: 1
[Panda]#  chrt -f 40 . ./exemple-reponse-irq-serie /dev/ttyO2 > resultats-non-preempt.txt
(Contrôle-C après quelques minutes)
Bye!
[Panda]#

Pendant le déroulement du programme, mon script qui charge et décharge le module perturbateur toutes les cinq secondes s’exécute sur le même processeur, avec une priorité temps-réel 10.

L’analyse du résultat donne :

$ ../chapitre-04/calculer-statistiques < resultats-non-preempt.txt
Nb mesures = 1156
Minimum = 283
Maximum = 46181
Moyenne = 4737
Ecart-type = 13397
$ ../chapitre-04/calculer-histogramme 100 0 50000  < resultat-nonpreempt.txt > histo-nonpreempt.txt

[NB: L'histogramme calculé ici est ensuite injecté dans un petit script Gnuplot, présenté dans un chapitre précédent, pour produire les figures ci-dessous]

Les résultats statistiques paraissent assez catastrophiques : une variabilité entre 283 et 46181 itérations de boucles, et un écart-type largement plus grand que la moyenne des valeurs ! Graphiquement les résultats sont plus compréhensibles, comme nous le voyons sur la figure suivante.

Réponse aux interruptions sur noyau non-préemptible

Notre système fait un grand écart entre un temps de réponse faible (290 itérations par boucles, environ 3 millisecondes) et une réponse très retardée par l’appel-système perturbateur (45000 itérations par boucle, à peu près 500 ms).

Résultat sur système préemptible

Après avoir redémarré la carte Pandaboard sur un noyau Linux compilé avec l’option CONFIG_PREEMPT, nous obtenons les résultats suivants.

[Panda] # uname -a 
Linux Pandaboard 3.0.0-rc7-cpb #1 SMP PREEMPT Thu Sep 29 14:49:25 CEST 2011 armv7l GNU/Linux
[Panda]# taskset -p 1 $$
pid 579's current affinity mask: 3
pid 579's new affinity mask: 1
[Panda]#  chrt -f 40 . ./exemple-reponse-irq-serie /dev/ttyO2 > resultats-preempt.txt
(Contrôle-C après quelques minutes)
Bye!
[Panda]#

Après avoir rappatrié le fichier sur le PC de développement, nous analysons les résultats.

$ ../chapitre-04/calculer-statistiques < resultats-preempt.txt
Nb mesures = 1307
Minimum = 284
Maximum = 309
Moyenne = 287
Ecart-type = 2
$

Voilà qui est beaucoup mieux ! Le résultat est visible sur la figure suivante avec la même échelle que précédemment.
Réponse aux interruptions sur noyau préemptible (1)

Un zoom au début de l’axe des abscisses est représenté sur la figure ci-dessous. Cette fois aucune préemption du processus de haute priorité, la réponse à l’interruption est toujours comprise entre 284 et 309 boucles de la carte à micro-contrôleur. La réponse n’est pas réellement plus rapide, mais elle est plus fiable et prévisible.
Réponse aux interruptions sur noyau préemptible (2)

Conclusion

La préemptibilité optionnelle du noyau, disponible depuis sa version 2.6, est un élément important pour améliorer la fiabilité des systèmes temps-réel dans leur réponse aux événements externes. Elle permet de garantir qu’un processus de haute priorité en attente d’un événement externe (matérialisé par cette interruption) sera réveillé avec rapidité et surtout fiabilité, même si une autre tâche – de priorité moindre – est en train d’exécuter un appel-système. Ceci évite le problème classique de l’inversion de priorité.