tornado2 - michel marie
TRANSCRIPT
Tornado2
Système de développement intégré temps réel
Noyau VxWorks 5.4.2
Cible VME, ELTEC BAB-40
Wind River Systems : http://www.wrs.com
News group : comp.os.vxworks
Auteur : M Michel Marie
2 Système temps réel – Tornado2 Professeur : Michel MARIE
1. Objectifs du cours :
Environnement Tornado2, outils du noyau VxWorks.
Construction d’un noyau VxWorks et téléchargement des modules sur la cible.
Utilisation des outils Tornado en développement croisé.
Configuration et contrôle des outils de synchronisation VxWorks (sémaphore binaire, mutex, sémaphores à compte).
Configuration et contrôle des outils de communication VxWorks (mémoire partagée, queues de
messages, tubes de communication « pipes »).
Mise en œuvre d’un timer pour une exécution périodique.
Création et utilisation de système de fichiers locaux (création d’une RAM disque)
Accès à un système de fichiers sur machine distante.
Utilisation des protocoles réseaux pour communiquer entre des tâches s’exécutant sur différentes machines (NFS, FTP, RPC) ; programmation réseau, les sockets.
Reconfiguration de VxWorks, linkage et démarrage du noyau sur une tâche définie par l’utilisateur.
2. Vue d’ensemble des outils de développement :
Outils de développement Tornado2 :
- Gnu : Compilateur C et C++
- Launcher : Plateforme des outils Tornado
- WindShell : Shell permettant un accès interactif avec la cible.
- CrossWind : Debugger au niveau source.
- Browser : Outil de visualisation d’informations système (état des tâches,
des sémaphores, de la pile, de la mémoire, etc…).
- Projects : Outil de création d’applications téléchargeables sur la cible et
de reconfiguration du noyau VxWorks.
- Simulator : Outil permettant de simuler l’exécution d’applications sur la
machine Host
- WindView : Debugger avec GUI permettant une analyse graphique d’une
application. La version de base de WindView n’est utilisable
qu’avec le simulateur.
- Windnavigator : Outil de visualisation graphique du code et des structures de
code d’applications écrites en langage C et C++ ( hiérarchie
des classes, appel des fonctions, etc…).
- Triggering : Outil permettant à l’utilisateur de produire des actions
spécifiques (démarrage/arrêt de WindView, exécution d’une
fonction) lorsqu’un événement ou une combinaison
d’événements est survenue.
4. Outils optionnels à Tornado :
WindNet SNMP : Outil de gestion du protocole SNMP (Simple Network Management
Protocol).
WindView : Complément à la version de base permettant d’utiliser cet outil sur une cible physique.
VxMP : Outils de développement d’applications multiprocesseurs.
VxVMI : Outils de développement d’une unité de gestion de mémoire virtuelle (MMU).
3 Système temps réel – Tornado2 Professeur : Michel MARIE
Télécharger et démarrer VxWorks
1. Mise en situation.
Pour de nombreuses cibles compatibles avec le noyau VxWorks, Wind River commercialise un BSP (Board Support Package). Dans notre cas, pour la cible ELTEC 68040 il s’agit du BAB40.
Ce BSP contient tous les fichiers de configuration de la cible, les drivers nécessaires à
l’utilisation de ses différents périphériques (ports série, port Ethernet, Timers, contrôleur de bus
VME, etc.) et un noyau VxWorks de base, à télécharger.
(fichier d:\tornado2\target\proj\projectNoyau\default\vxworks)
La liaison série entre le PC et la cible sert à configurer les paramètres strictement nécessaires au
téléchargement par le réseau Ethernet du noyau VxWorks et des modules d’application. Ces
paramètres se trouvent ensuite sauvegardés dans une NVRAM afin d’être lus par la
BOOTROM lors des futurs démarrages de la cible. Une fois VxWorks démarré la liaison série
se trouve configurée en mode terminal par défaut. Pour communiquer avec la cible via la liaison
série l’application hyperterminal est installée avec Tornado.
Commandes de configuration :
Command Description
h Help command--print a list of available boot commands.
? Same as h.
@ Boot (load and execute the file) using the current boot parameters.
p Print the current boot parameter values.
c Change the boot parameter values.
l Load the file using current boot parameters, but without executing.
g adrs Go to (execute at) hex address adrs.
d adrs[, n] Display n words of memory starting at hex address adrs. If n is omitted, the
default is 64.
t adrs1,
adrs2,
nbytes
Copy nbytes of memory, starting at adrs1, to adrs2.
e Display a synopsis of the last occurring VxWorks exception.
n netif Display the address of the network interface device netif.
La commande c permet de configurer la liaison Ethernet.
La commande @ permet de télécharger le fichier image de VxWorks puis de démarrer le noyau. Le téléchargement se fait via le réseau selon le protocole FTP. Il est par conséquent nécessaire,
avant d’activer cette commande, d’exécuter le serveur FTP installé avec tornado en précisant le
nom d’utilisateur, le mot de passe choisi et les droits sur le fichier à télécharger.
4 Système temps réel – Tornado2 Professeur : Michel MARIE
- Exemple de configuration :
[VxWorks Boot]: p
boot device : ln
processor number : 0
host name : mars
file name : c:\tornado\target\config\bab40\vxWorks
inet on ethernet (e) : 90.0.0.50
inet on backplane (b) :
host inet (h) : 90.0.0.1
gateway inet (g) :
user (u) : fred
ftp password (pw)(blank=use rsh) :secret
flags (f) : 0x0
target name (tn) : phobos
startup script (s) :
other (o) :
Redémarrer le noyau est possible soit en tapant CTRL+X dans la fenêtre du terminal X soit par la
commande reboot() de WindShell.
La liste des cibles (target) connectées au réseau est visualisable par le Launcher, Vous pouvez ainsi
sélectionner la cible de votre choix pour développer votre application.
Les cibles accessibles par le Launcher auront dûes, précédemment, être configurées à l’aide du
menu ToolsTargetServerConfigure. Il est alors nécessaire de préciser :
- Une nom décrivant la commande d’attachement
- Un nom pour le target serveur
- Des propriétés (Back End)
- Un mode de liaison (wdbrpc)
- Une adresse IP
La case à cocher « Add description to menu » permet d’ajouter une ligne associée à cette
commande d’attachement au menu TargetServer.
La commande Launch réalise alors l’attachement du PC par l’application dite Target Server
(tgtsvr) et le test de l’agent cible WDB.
Toutes les interactions de développement croisé entre les applications sur le PC (WindShell,
CrossWind, WindView, etc…) et la cible VxWorks se feront a l’aide du Target Server coté PC et
d’un agent WDB coté cible.
5 Système temps réel – Tornado2 Professeur : Michel MARIE
Une fois le PC attaché à la cible le menu du launcher devient actif :
Les icônes correspondent, dans l’ordre de gauche à droite au lancement des outils suivants :
le browser, permettant d’obtenir des informations sur la cible, la mémoire, les modules, les
objets, etc,
WindShell, démarrant une session Shell sur la cible choisie,
Crosswind, démarrant le debugger niveau source,
Simulator, démarrant le simulateur de cible,
WindView, démarrant l’outil de "debuggage" graphique,
Windnavigator, démarrant l’outil de visualisation des structures d’applications,
Triggering, démarrant l’outil de synchronisation.
2. Le Browser
La fenêtre du browser propose 7 rubriques :
La rubrique "Target information" apportant quelques informations de base sur la cible
d’attachement.
La rubrique « Module information » informant des modules téléchargés sur la cible et de
leur contenu (y compris le noyau).
La rubrique "Object information", une des plus utiles puisqu’elle permet de connaître
l’état des objets utilisés par une application, sémaphores, queues de messages, etc.
La rubrique "Spy chart" qui permet de connaître le taux d’occupation du processeur de la
cible et la répartition de cette occupation entre les tâches.
La rubrique "Stack check" donnant l’état d’utilisation de la pile des tâches actives.
La rubrique "Tasks" indiquant les tâches actives et leur état.
La rubrique "Memory usage" indiquant la quantité de mémoire vive utilisée par chaque
module ou application.
Exemples : « Target information »
« Spy chart »
Les 3 icônes du browser correspondent, dans l’ordre de gauche à droite :
Au rafraîchissement ponctuel de l’affichage.
Au rafraîchissement périodique de l’affichage.
A la configuration du rafraîchissement périodique.
6 Système temps réel – Tornado2 Professeur : Michel MARIE
3. WindShell
WindShell est un interpréteur de langage C interactif permettant :
d’accéder à l’ensemble des facilités de VxWorks,
d’invoquer chaque fonction chargée en mémoire,
de créer dynamiquement et d’examiner des variables. C’est également un interpréteur de langage Tcl interactif permettant :
de définir des commandes utilisateur,
d’automatiser des interactions avec la cible.
L’outil WindShell est activé soit à partir du menu ToolsShell, soit à partir du launcher.
Exemples :
Le Shell lit les lignes en entrée comme une file de commandes, les évalue et retourne le résultat
obtenu.
Conversion de données.
68
value = 68 = 0x44 = ‘D’
0xf5de
value = 62942 = 0xf5de = _init + 0x52
Le shell retourne le résultat en décimal puis en hexadécimal et si possible comme un caractère
constant ou une adresse symbolique plus un offset.
Calcul de données.
(14*9)/3
value = 42 = 0x2a = ‘*’
(0x1355<<3) & 0x0f0f
value = 2568 = 0xa08
Calcul avec variables et allocation dynamique.
x = va1 + va2
new symbol « x » added to symbol table
address = ……….
value = ……….
Invocation de fonctions VxWorks.
taskSpawn(tmaTache , 10 , 0 , 1000 , maTache , fd1 , 300)
value = 0x0
fd = open (fichier , 0 , 0 )
fd = …… : value = 0x0
Invocation de fonctions utilisateur.
bilan = Mafonction(dites, 33 )
bilan = ( adresse de bilan) : value = …..
Invocation de fonctions « Built-In Shell ».
i
NAME ENTRY TID PRI STATUS PC SP ERRNO DELAY
---------- ------------ -------- --- ---------- -------- -------- ------- -----
tLogTask _logTask 3e6188 0 PEND 2fd00 3e60e4 0 0
tExcTask _excTask 3e8a9c 0 PEND 2fd00 3e89fc 0 0
tWdbTask 0x24ab2 3c2f30 3 READY 2f9b2 3c2e20 0 0
tNetTask _netTask 3e1d54 50 READY 9a14 3e1cec 0 0
tFtpdTask _ftpdTask 3c6248 55 PEND 26090 3c6164 0 0
value = 0 = 0x0
Attention : Les « macros » définies pour le C préprocesseur avec la directive #define et
qui peuvent apparaître dans l’aide Tornado ne peuvent pas être utilisées dans les fonctions
« Built-In Shell».
7 Système temps réel – Tornado2 Professeur : Michel MARIE
Quelques fonctions « Built-In Shell » classées par centre d’intérêt.
Commandes de gestion des tâches
Fonction Rôle Fonction Rôle
sp crée une tâche avec options/défaut period Active une tâche périodiquement
sps Crée une tâche avec l’état
suspendu repeat N répétitions/lancement d’une
tâche
ts/tr suspend/reprend une tâche taskIdDefault Spécifie le TID pris par défaut
td Supprime une tâche
Commandes d’information sur les tâches.
Fonction Rôle Fonction Rôle
i
Affichage d’informations sur les
tâches du système tw Retourne des informations sur
l’objet par lequel une tâche se
trouve en état d’attente
iStrict Equivalent à i checkStack Affiche l’usage de la pile par une
tâche
ti Affichage d’informations
détaillées sur une tâche tt Affiche un « trace » de la pile
w Retourne des informations sur les
tâches en état d’attente
« PENDING »
taskIdFigure Retourne le TID d’une tâche à
partir de son nom
Commandes d’information système.
Fonction Rôle Fonction Rôle
devs Liste des périphériques connus
par le système ls Liste des fichiers dans le
répertoire courant
lkup Liste des symboles issus de la
table des symboles pwd Affichage du répertoire courant
lkAddr Liste les symboles trouvés à
partir de l’adresse spécifiée help Description de quelques
commandes usuelles du Shell
d Dump de la mémoire h Affiche les 20 dernières
commandes
l Désassemble et affiche un
nombre spécifié d’instructions shellHistory Modifie la dynamique de la
commande h
printErrno Décrit la dernière valeur de la
variable « error » shellPromptSet Modifie le prompt du Shell
version Affichage de la version
VxWorks printLogo Affiche le logo du Shell
Tornado
cd Change le répertoire courant
Commandes de modification du système et de «debuggage ».
Fonction Rôle Fonction Rôle
ld Charge un module sur la cible et le
lie dynamiquement au noyau
temps réel.
cret Exécuter jusqu’à la fin de la
fonction en cours
unld Efface un module de la cible et
libère la mémoire occupée bdall Efface tous les points d’arrêt
m Modification de la mémoire bd Efface un point d’arrêt
mRegs Modification des registres pour
une tâche spécifiée reboot Redémarrage de la cible
b Positionne ou affiche les points
d’arrêt bootChange Modification des paramètres
de boot sauvegardés dans la
NVRAM
8 Système temps réel – Tornado2 Professeur : Michel MARIE
bh Positionne un point d’arrêt
matériel sysSuspend Arrêt système (En mode
système uniquement – Agent
WDB externe)
s Exécution d’un pas de programme sysResume Redémarrage du système (En
mode système uniquement –
Agent WDB externe)
so Exécution d’une instruction, toute
fonction étant exécutée en 1 fois agentModeShow Retourne le mode de l’agent
Target, système ou tâche
c Redémarre jusqu’au prochain point
d’arrêt sysStatusShow Retourne l’état du contexte
système, « suspended » ou
« running »
quit ou exit Fermeture du Shell
Commandes de développement C++.
Fonction Rôle Fonction Rôle
cplusCtors Appel manuel d’un constructeur
statique CplusStratShow Retourne la stratégie du
constructeur/destructeur
courant
cplusDtors Appel manuel d’un destructeur
statique cplusXtorSet Appel d’un constructeur
« stratégie »
Commandes d’affichage d’objets.
Fonction Rôle Fonction Rôle
show Affichage
d’informations sur un
objet spécifié dans la
fenêtre du Shell
iosDrvShow Affichage de la liste des
« drivers » système
browse Affichage
d’informations sur un
objet spécifié dans la
fenêtre du Browser
iosDevShow Affichage de la liste des
« devices » système
( tubes de communication )
classShow Affichage
d’informations sur une
classe du noyau
VxWorks
iosFdShow Affichage de la liste des
« File descriptors » système
taskShow Affichage
d’information sur la
structure TCB
memPartShow Affichage des partitions et
des statistiques
semShow Affichage
d’informations sur un
sémaphore spécifié
memShow Affichage de la totalité de
l’espace libre et alloué dans
les partitions système
semPxShow Affichage
d’informations sur un
sémaphore POSIX
spécifié
smMemShow Affichage de la totalité de
l’espace libre et alloué dans
la mémoire système
wdShow Affichage
d’informations sur un
« watchdog »
smMemPartShow Affichage de la totalité de
l’espace libre et alloué dans
une partition système
msgQShow Affichage
d’informations sur une
queue de messages
moduleShow Affichage des modules
mqPxShow Affichage
d’informations sur une
queue de messages
POSIX
moduleIdFigure Retourne l’ID d’un module
à partir de son nom
9 Système temps réel – Tornado2 Professeur : Michel MARIE
taskCreateHookShow Retourne la liste des
tâches Hook create taskRegsShow Retourne le contenu des
registres de la tâche
TaskDeleteHookShow Retourne la liste des
tâches Hook delete taskWaitShow Retourne des informations
sur l’objet qui a
conditionné l’état
« PENDING » d’une tâche.
TaskSwitchHookShow Retourne la liste des
tâches Hook Switch intVecShow Retourne la table des
vecteurs d’interruption
Commandes d’affichage de l’état réseau.
Fonction Rôle Fonction Rôle
hostShow Retourne la table "Host" routestatShow Retourne les statistiques de
routage
icmpstatShow Retourne les statistiques
pour le protocole ICMP
« internet control
message protocol »
tcpstatShow Retourne les statistiques
pour le protocole TCP
ifShow Retourne l’interface
d’attachement du réseau tftpInfoShow Retourne les informations
d’état dur TFTP
inetstatShow Retourne toutes les
connexions actives pour
les sockets IP
udpstatShow Retourne les statistiques
pour le protocole UDP
ipstatShow Retourne les statistiques
pour le protocole IP
Remarque : Pour de plus amples renseignements sur ces fonctions il est préconisé d’utiliser l’aide
tornado (HelpManualsSubroutines).
Editions de commandes Shell
Le mode édition de WindShell utilise les commandes conviviales et on ne peut plus explicites de vi
d’UNIX. La commande h du shell affiche l’historique des commandes.
Quelques rappels à ce sujet !
Basic Control
h [size] Display shell history if no argument; otherwise set history buffer to size. ESC Switch to line-editing mode from regular input mode. ENTER Give line to shell and leave edit mode.
CTRL+D
Complete symbol or path name (edit mode), display synopsis of current symbol
(symbol must be complete, followed by a space), or end shell session (if the command
line is empty). [tab] Complete symbol or path name (edit mode). CTRL+H Delete a character (backspace). CTRL+U Delete entire line (edit mode). CTRL+L Redraw line (works in edit mode).
CTRL+S and
CTRL+Q . Suspend output, and resume output
CTRL+W Display HTML reference page for a routine.
Movement and Search Commands
nG Go to command number n.1
/s or ?s Search for string s backward in history, or forward. n Repeat last search.
nk or n- Get nth previous shell command.*
nj or n+ Get nth next shell command.*
nh Go left n characters (also CTRL+H).*
nl or Go right n characters.*
10 Système temps réel – Tornado2 Professeur : Michel MARIE
SPACE
nw or nW Go n words forward, or n large words. *2
ne or nE Go to end of the nth next word, or nth next large word. *
nb or nB Go back n words, or n large words.* $ Go to end of line.
0 or ^ Go to beginning of line, or first nonblank character.
fc or Fc Find character c, searching forward, or backward.
Insert and Change Commands
a or A ...ESC Append, or append at end of line (ESC ends input).
i or I ...ESC Insert, or insert at beginning of line (ESC ends input).
ns ...ESC Change n characters (ESC ends input).*
nc SPACE ...ESC Change n characters (ESC ends input).*
cw ...ESC Change word (ESC ends input).
cc or S ...ESC Change entire line (ESC ends input).
c$ or C ...ESC Change from cursor to end of line (ESC ends input).
c0 ...ESC Change from cursor to beginning of line (ESC ends input).
R ...ESC Type over characters (ESC ends input).
nrc Replace the following n characters with c.* ~ Toggle case, lower to upper or vice versa.
Delete Commands
nx Delete n characters starting at cursor.*
nX Delete n character to left of cursor.* dw Delete word. dd Delete entire line (also CTRL+U).
d$ or D Delete from cursor to end of line. d0 Delete from cursor to beginning of line.
Put and Undo Commands
p or P Put last deletion after cursor, or in front of cursor. u Undo last command.
1: The default value for n is 1.
2: words are separated by blanks or punctuation; large words are separated by blanks only.
Exécution d’une fonction utilisateur avec le Shell
Une fois le module dans lequel réside votre fonction construit sans erreur il faut le charger sur la
cible et le lier dynamiquement au noyau à partir du Shell : ld < filename.o. La commande
moduleShow permet de vérifier l’ensemble des modules présent sur la cible. On peut également le
vérifier à partir du Browser.
Pour supprimer un module et restituer la mémoire la commande unld filename.o est nécessaire.
Supposons à présent que le module utilisateur téléchargé contient une fonction appelée demo() qui
retourne un paramètre.
Deux méthodes sont alors envisageables pour exécuter cette fonction à partir du Shell :
Une première méthode consiste à exécuter la commande demo(). Le Shell est alors bloqué
jusqu’à ce que la fonction demo() retourne son paramètre, il l’affiche alors et rend la main, ceci
est perceptible par le retour du prompt.
La tâche en charge de l’exécution de la fonction demo() sera nommée tn.
Une seconde méthode consiste à exécuter la fonction demo() directement en spécifiant une
tâche à partir du Shell à l’aide de la commande sp(demo,…). La main est alors instantanément
rendue au Shell sans attendre la fin de la tâche activée ; les paramètres retournés sont perdus.
La tâche en charge de l’exécution de la fonction demo() sera nommée un.
11 Système temps réel – Tornado2 Professeur : Michel MARIE
Il peut également être intéressant de connaître la durée que nécessite l’exécution d’une tâche
particulière ; on dispose pour ceci des fonctions timex() et timexN().
- La commande timex( demo() ) retourne, si la précision du timer utilisé est suffisante par
rapport à la durée de la tâche demo(), la durée d’exécution en microsecondes de cette
tâche.
- La commande timexN( demo() ) retourne la durée moyenne d’exécution en
microsecondes de la tâche demo(), après avoir exécuté n fois cette tâche, n assurant une
précision sur la mesure meilleure que 2%.
Attention, si un module utilisateur comporte une fonction portant le même nom « nomCommande » qu’une fonction du Shell :
nomCommande active la fonction « Built-In » du Shell.
@nomCommande active la fonction utilisateur.
4. CrossWind
L’outil CrossWind est un « debugger » au niveau source dont l’intérêt principal est de positionner
des points d’arrêt dans le but de contrôler l’exécution d’un programme en visualisant dans des
fenêtres graphiques l’évolution de ses variables.
L’utilisation du debugger nécessite en première instance la création d’un projet ou l’ouverture d’un
projet existant au sein d’un espace de travail « workspace ». Un projet permet de :
Rassembler les fichiers d’une même application.
Grouper plusieurs projets au sein d’un espace de travail.
Personnaliser son noyau VxWorks
Ajouter des fonctions d’initialisation au noyau VxWorks
Définir des options de construction.
Construire des applications et des images du noyau VxWorks.
Télécharger des applications sur une cible VxWorks.
4.1. Quelques notions de terminologie
Application téléchargeable : Consiste en un ou plusieurs modules relogeables qui peuvent
être téléchargés et dynamiquement liés avec le noyau VxWorks.
Dans le cas où elle comporte plusieurs fichiers et modules, le
module est alors Nomdeprojet.out.
Application « bootable » : Consiste en une application liée à une image du noyau VxWorks.
L’application démarre alors lorsque le noyau a démarré.
Projet : Ensemble des fichiers source, des règles de construction, des
fichiers binaires utilisés pour créer une application téléchargeable,
une image du noyau ou une application « bootable ». Chaque projet
nécessite son propre répertoire.
Espace de travail «workspace » : Un espace de travail peut être considéré comme une « boîte »
de rangement pour un ou plusieurs projets, ceux-ci pouvant alors être
associés.
Composant : Consiste en une facilité Vxworks pouvant être inclue ou exclue de
l’image du noyau. De nombreux composants comportent des
paramètres qui peuvent être spécifiés.
12 Système temps réel – Tornado2 Professeur : Michel MARIE
4.2. Création d’un projet
Les commandes file New project et file Open Workspace permettent respectivement de
créer un projet ou d’ouvrir un espace de travail comportant au moins un projet précédemment
créé. Dans le premier cas la fenêtre suivante apparaît alors :
Si vous choisissez de créer une nouvelle application téléchargeable il vous est ensuite demandé
de préciser :
Le nom du projet
Son emplacement
Son espace de travail
Si vous voulez l’insérer dans un nouveau projet ou un projet existant
Le type de processeur que doit utiliser la chaîne de compilation
Si par exemple le projet créé est nommé application1 situé dans
D:\Tornado\target\proj\Application1 avec pour espace de travail Workspace0 et basé sur un
processeur MC68040 le projet créé apparaît alors comme ci-dessous :
Il est alors possible d’ajouter un fichier au projet par la commande File New en précisant dans la
fenêtre suivante le type de fichier et le projet dans lequel il doit être ajouté.
13 Système temps réel – Tornado2 Professeur : Michel MARIE
Si par contre vous choisissez de créer une image « bootable » du noyau VxWorks il vous est ensuite demandé de préciser :
Le nom du projet
Son emplacement
Son espace de travail
Si vous voulez baser l’image sur un BSP ou un projet existant
Si par exemple le projet créé est nommé image1 situé dans D:\Tornado\target\proj\image1 avec
pour espace de travail Workspace0 et basé sur le BSP BAB40 le projet créé apparaît alors comme
ci-dessous :
La rubrique VxWorks fait apparaître l’ensemble des composants de VxWorks, en caractères gras
s’ils sont inclus au noyau, en caractères « normaux » s’il sont exclus du noyau et en caractères
italiques s’ils ne sont pas utilisables avec le BSP utilisé. En sélectionnant la rubrique propriétés,
des informations sur le composant sont visualisables. Par exemple, en sélectionnant la rubrique
« BSP Memory configuration » on visualise ci-dessous le paramètre NV_RAM_SIZE indiquant
que la taille de la mémoire non volatile de la cible est de 0x0800 soit 2koctets.
14 Système temps réel – Tornado2 Professeur : Michel MARIE
4.3. Utilisation du « debugger »
Une fois que vous avez créé un projet, que vous lui avez ajouté au moins un fichier source et que
vous avez édité votre programme il faut le construire à l’aide de la commande Build, les
dépendances de votre application sont alors calculées puis une fenêtre de construction est ouverte
dans laquelle apparaissent les commandes de compilation et les éventuelles erreurs.
Si des erreurs apparaissent, un double clic sur celles-ci vous ouvre le fichier source en cause et
positionne le curseur sur la ligne de code erronée.
Lorsque plus aucune erreur ne subsiste, il reste à s’attacher à une cible « ToolsTarget server » et
à télécharger le module sur cette cible « ProjectDownload ».
L’ensemble de ces commandes peuvent également être réalisées à l’aide de la barre d’outils de
construction suivante :
Les boutons correspondent dans l’ordre de gauche à droite :
à la commande de construction,
à la commande de reconstruction totale,
à la commande de compilation,
à la commande de construction des dépendances,
à la commande d’arrêt de la construction,
à la commande de téléchargement.
Vous pouvez à présent activer le « debugger » par la commande de menu ToolsDebugger ou par
l’icône du « debugger » dans la barre d’outils du launcher précédemment présentée.
La barre d’outils du « debugger » présentée ci-dessous ou les commandes du menu Debug vous
permettront d’utiliser celui-ci pour tester une tâche.
Les boutons correspondent dans l’ordre de gauche à droite :
à la commande d’arrêt du « debugger »,
à la commande de démarrage d’une tâche,
à la commande de positionnement d’un point d’arrêt,
à la commande d’interruption du « debugger »,
à la commande de redémarrage du « debugger » jusqu’au prochain point d’arrêt,
à la commande d’exécution d’un pas (step in),
à la commande d’exécution d’un pas (step over), une fonction étant vue comme une
seule instruction,
à la commande d’exécution de la fonction en cours jusqu’à sa sortie (step out),
à la commande d’ouverture d’une fenêtre de visualisation de variables,
à la commande d’ouverture de la fenêtre de visualisation des variables locales,
à la commande d’ouverture de la fenêtre de visualisation des registres,
à la commande d’ouverture de la fenêtre de visualisation de l’état de la pile,
à la commande d’ouverture de la fenêtre de visualisation de la mémoire.
Si l’on désire tester la fonction progStart() écrite dans le fichier cobble.c il suffit, après avoir
téléchargé le module correspondant et activé le « debugger », d’activer la commande run du
« debugger ». La fenêtre suivante apparaît alors dans laquelle il faut préciser :
le nom de la fonction à tester ( point d’entrée de la tâche),
15 Système temps réel – Tornado2 Professeur : Michel MARIE
les arguments à passer à la fonction s’il y en a ( les arguments omis sont affectés par
zéro ),
si l’on désire qu’un point d’arrêt soit positionné à l’entrée de la tâche.
Le “debugger” ouvre alors le fichier source de la fonction à tester et positionne le point d’arrêt face
à la première instruction de la fonction.
Pour une utilisation plus large des points d’arrêt il est possible de les installer par la commande
« ProjectDownload ». La boîte de dialogue suivante apparaît dans laquelle il faut préciser le
point d’arrêt à l’aide de la syntaxe nom_de_fichier : numéro de ligne.
Il est également possible en activant le bouton “Advanced” de préciser des conditions d’arrêt. La
boîte de dialogue suivante permet de préciser le nombre de passages sur l’instruction avant arrêt ou
une condition sur une variable, l’arrêt sur l’instruction n’aura alors lieu que si la condition éditée est
vraie.
16 Système temps réel – Tornado2 Professeur : Michel MARIE
L’exemple suivant montre les fenêtres variables (avec visualisation de la variable structure
syncSemId), registres et mémoire (avec visualisation à partir de l’adresse 0x45eef90) ; la fenêtre de
commande du « debugger » se trouve également ouverte en bas de la recopie d’écran.
Attention : Il est seulement possible de « debugger » une tâche à la fois. Si plusieurs
tâches sont simultanément actives, il faut ouvrir une session de « debuggage » par tâche à
« debugger » et passer alternativement de l’une à l’autre à l’aide des commandes d’attachement
et de détachement.
(gdb) attach
(gdb) detach
On imagine bien que cela doit rapidement devenir inextricable !
L’ensemble des fonctions du « debugger » sont accessibles par la commande help, qui retourne
la liste des groupes de commandes.
5. Le « simulator »
Cet outil permet de tester vos applications sur une cible factice qui est en fait simulée sur la
machine host. La cible factice porte le nom vxsim, votre environnement de développement s’y
attache automatiquement lorsque vous démarrez le simulateur par l’icône appropriée ou la
commande de menu ToolsSimulator. Cependant les potentialités de cette cible factice
n’incorporent pas les outils réseau, SNMP, et Wind Foundation Classes.
Il est également possible d’obtenir une cible factice à l’image de votre cible réelle si le BSP le
permet.
Au démarrage du « simulator » la fenêtre suivante est affichée :
17 Système temps réel – Tornado2 Professeur : Michel MARIE
Lorque l’on valide ce choix, il est précisé que votre environnement doit s’attacher à la cible factice
par l’intermédiaire d’un “Target Server”.
Vous pouvez ensuite développer et tester vos applications sur cette cible factice comme sur une
cible réelle, ou presque !
6. Utilisation de « WindView »
6.1. Introduction
WindView est un outil graphique pouvant être considéré comme un analyseur logique pour système
multitâches, en l’occurrence VxWorks.
Il présente sous la forme de chronogrammes sur lesquels sont attachés des symboles, l’état des
tâches, la prise et la restitution de sémaphores, l’arrivée d’événements, l’arrivée d’interruptions, etc.
Pour chacun des événements visualisés, il est possible d’obtenir des renseignements tels que la
cause de l’événement, son contexte, l’instant d’arrivée, la priorité, etc.
De plus il est possible d’utiliser les facilités du « trigger » pour choisir une condition précise de
collecte des événements et pour spécifier la durée de l’acquisition.
6.2. Reconfigurer le noyau avec les outils « WindView »
Utiliser WindView avec le simulateur ne nécessite aucune configuration spécifique ; par contre,
pour pouvoir utiliser WindView avec une cible il faut disposer de l’outil optionnel de complèment
WindView et le noyau VxWorks doit être construit en lui incluant le composant WindView 2.0
instrumentation.
De plus, le BSP de la cible doit être pourvu d’un pilote de timer haute résolution timestamp.
En fonctionnement, l’agent WDB sur la cible se contente de remplir un buffer des événements avec
un minimum d’intrusions sur le système temps réel. Lorsque le buffer est plein, il est transféré à
l’application WindView sur le PC qui se chargera de mettre à jour la fenêtre graphique.
18 Système temps réel – Tornado2 Professeur : Michel MARIE
6.3. Utiliser WindView
Pour lancer WindView, il suffit de cliquer sur le dernier bouton de la barre d’outils du launcher ou
bien de passer par le menu, commande ToolsWindView.
La boîte de dialogue suivante apparaît :
Un choix doit être fait quant au mode de capture des événements, trois modes sont envisageables,
« context switch », « task state transition » et « additional instrumentation » :
Context Switch : dans ce mode WindView représente l’évolution du système en ne présentant que les changements de contexte, commutation de tâches, interruptions, préemption, etc. C’est
le mode de fonctionnement le plus simplifié.
Task State Transition : dans ce mode WindView ajoute aux informations précédentes les changements d’état des tâches et signale les objets qui en sont la cause (prise d’un sémaphore,
attente sur une queue de messages, etc.). Il est important de noter que dans ce mode, tout
événement qui ne cause pas une modification d’état des tâches n’est pas mentionné.
Additionnal instrumentation : c’est le plus complet des modes de fonctionnement de
WindView, toutes les informations sont collectées y compris celles qui ne modifient pas l’état
des tâches, il est alors possible de les filtrer en cochant les cases correspondant à
l’instrumentation désirée dans la fenêtre « collection configuration » précédente.
Exemple : La fenêtre suivante montre une fenêtre d’acquisition WindView en mode Additional instruments.
On distingue les tâches utilisateur tCosmos, tSchlep, tMonitor et tCrunch. Les autres tâches qui
apparaissent sont les tâches utilisées par le noyau, par exemple :
tWdbTask : Tâche de gestion de l’agent WDB.
tNetTask : Tâche de gestion du réseau.
tFtpdTask : Tâche de gestion du protocole client FTP
19 Système temps réel – Tornado2 Professeur : Michel MARIE
Il est alors possible en cochant l’option memLib d’obtenir des informations sur l’usage de la
mémoire. Pour les faire apparaître dans la fenêtre de visualisation il faut activer l’icône Analyse et
valider Memory usage. La fenêtre WindView est alors subdivisée pour faire apparaître l’usage de
la mémoire comme dans la figure ci-dessous.
Il est possible de faire apparaître une fenêtre de renseignements sur l’événement séléctionné par un
clic souris, l’exemple présenté correspond à l’activation d’un delai de 4 tics sur la tâche tCosmos.
Légende des symboles utilisés par WindView.
Quelques uns symboles rencontrées dans WindView sont présentés ci-dessous, pour les autres
consulter la rubrique d’aide Help WindView Help.
7. Le « Trigger »
Le trigger permet de détecter un événement ou une combinaison d’événements qui conditionnera
une action pouvant être le démarrage d’une acquisition Windview, son arrêt ou l’appel d’une
fonction. Le "Trigger" est activé à partir de la commande de menu
Tools Triggering ou à partir de l’icône associée.
Lors d’un démarrage du “Trigger” la fenêtre suivante est affichée
20 Système temps réel – Tornado2 Professeur : Michel MARIE
L’icône de gauche de la barre d’outils "trigger" permet de configurer les événements à détecter et
les actions associées à l’aide de la fenêtre présentée ci-dessous :
L’exemple ci-dessous correspond à l’activation de deux événements trigger qui permettront de
démarrer WindView lorsque la condition sur la variable vxTicks>1000 sera réalisée et de stopper
WindView lorsque vxTicks>1500.
Trigger 0 : Trigger 1 :
21 Système temps réel – Tornado2 Professeur : Michel MARIE
Temps réel multitâches
1. Introduction.
Le temps réel caractèrise un système pour lequel le temps de réponse peut être contrôlé. Un tel
système est dit déterministe. Le temps écoulé entre l’arrivée d’un événement et la réponse associée
est appelé latence.
Pour satisfaire ces conditions un noyau temps réel dispose de fonctionnalités permettant de faire
transiter les tâches entre différents états possibles, afin d'attribuer au mieux l'usage du processeur et
des ressources.
Différents états possibles d’une tâche.
Execution
Prêt
RetardéeAttente
Suspendue
Attente : tâche en attente d’une ressource non disponible.
Retardée : tâche en attente d’expiration d’un délai.
Suspendue : tâche suspendue pour une durée indéterminée.
Prêt : tâche dans la file d’attente du processeur par ordre de priorités puis d'arrivée.
Exécution : tâche disposant du processeur.
2. Fonctionnement de l’ordonnanceur.
Pour basculer une nouvelle tâche en exécution, le noyau doit :
- Sauvegarder le contexte de la tâche en cours d’exécution dans la structure TCB de celle-ci.
- Restaurer le contexte de la nouvelle tâche à exécuter à partir de sa structure TCB.
La structure TCB (Task Control Block) est définie par la structure de données WIND_TCB dans le
fichier taskLib.h. Elle contient les informations sur le contexte CPU, pc, sp, registres cpu, registres
fpu ainsi que sur la tâche, priorité, flags, etc…
Le noyau gère une file particulière pour chacun des états possibles des tâches.
22 Système temps réel – Tornado2 Professeur : Michel MARIE
Noyau
TCB TCBTCB
TCBTCB TCB CPU
Suspendues Prêtes Exécution
En attente Retardées
TCB
Il y a ordonnancement si :
- La tâche en exécution passe dans l’état attente, retardée ou suspendue.
- Une tâche de plus haute priorité passe dans l’état prêt ( préemption ).
- Ecoulement du time-slice si au moins une autre tâche de même priorité est prête.
Les priorités.
La priorité d’une tâche est codée sur un octet :
- 0 = priorité maximale
- 255 = priorité minimale
- 100 = priorité par défaut
VxWorks n’implémente pas la gestion dynamique des priorités. Si une tâche A n’est jamais bloquée
ou en attente ou retardée, alors une tâche B moins prioritaire ne sera jamais exécutée.
Si plusieurs tâches sont de même priorité, il est alors possible de faire fonctionner l’ordonnanceur
en mode “round robin” entre celles-ci. ( ordonnancement par priorités avec files multiples ).
3. Quelques fonctions usuelles.
Ordonnancement.
- int sysClkRateGet() Retourne la fréquence de l’horloge système en Hz, par défaut
60 Hz, soit un “tick” de 16.7 ms.
- STATUS sysClkRateSet(int tps) Fixe la fréquence d’horloge système en Hz.
- STATUS kernelTimeSlice(int ticks) Fixe le time slice en ticks ; si 0, le mode round robin
est dévalidé.
Création d’une tâche à l'état prêt.
- int = taskSpawn( char*nom , int priorité , int options , int pile , FUNCPTR entree,
int arg1,…, int arg10)
Options (les principales) :
VX_UNBREAKABLE (0x2)
VX_FP_TASK (0x8)
VX_NO_STACK_FILL (0x100)
VX_PRIVATE_ENV (0x80)
Création d’une tâche à l'état suspendue.
- STATUS = taskInit( WIND_TCB* ptcb, char* name , int priorité , int options,
23 Système temps réel – Tornado2 Professeur : Michel MARIE
char* pstack , int stacksize, FUNCPTR entree, int arg1,…, int arg10)
Activation d’une tâche créée avec taskInit.
- STATUS taskActivate(int TID)
Suppression d’une tâche.
- STATUS taskDelete(int TID) Supprime la tâche et désalloue le TCB et la pile.
- exit() Supprime la dernière tâche activée et désalloue le TCB et la pile.
- STATUS taskSafe() Protège la tâche en cours contre toute suppression.
- STATUS taskUnSafe() Enlève la précédente protection.
Identification d’une tâche.
- int taskIdSelf() Retourne le TID d’une tâche en cours d’exécution.
- int taskIdListGet(int idlist[], int Nmax) Retourne la liste des TID des tâches en cours.
- STATUS taskIdVerify(int TID) Vérifie si le TID spécifié est valide.
- char* taskName(int TID) Retourne le nom à partir du TID.
- int taskNameToId(char* name) Inverse de taskName.
- STATUS taskShow(int TID, int level) Informations sur la tâche (niveau 0, 1 ou 2).
Modification dynamique des priorités.
- STATUS taskPriorityGet(int TID, int * pprio) Retourne la priorité de la tâche.
- STATUS taskPrioritySet(int TID , int prio) Modifie la priorité de la tâche.
Contrôle des tâches.
- STATUS taskRestart(int TID) La tâche est stoppée puis relancée avec les
mêmes arguments.
- STATUS taskSuspend(int TID) La tâche est suspendue.
- STATUS taskResume(int TID) La tâche est restaurée.
- STATUS taskDelay(int ticks) La tâche est retardée pour n ticks.
- ULONG tickGet() Retourne l’état courant du compteur de ticks.
- tickSet(ULONG val) Positionne le compteur de tics.
Fonctions additionnelles de contrôle des tâches.
- STATUS taskCreateHookAdd(FUNCPTR func) Ajoute une fonction qui est appelée
à chaque création d’une tâche.
- STATUS taskCreateHookDelete(FUNCPTR func) Supprime la précédente fonction.
- STATUS taskSwitchHookAdd(FUNCPTR func) Ajoute une fonction qui est appelée
à chaque commutation de tâche.
- STATUS taskSwitchHookDelete(FUNCPTR func) Supprime la précédente fonction.
- STATUS taskDeleteHookAdd(FUNCPTR func) Ajoute une fonction qui est appelée
à chaque suppression d’une tâche.
- STATUS taskDeleteHookDelete(FUNCPTR func) Supprime la précédente fonction.
24 Système temps réel – Tornado2 Professeur : Michel MARIE
Les sémaphores
1. Introduction.
Le noyau temps réel VxWorks met à notre disposition 3 types de sémaphores:
Le sémaphore binaire utilisé pour la synchronisation.
Le sémaphore MUTEX utilisé pour l’exclusion mutuelle.
Le sémaphore à compte, d’usage moins courant, utilisé par exemple pour établir un rendez-vous.
2. Le sémaphore binaire et la synchronisation.
Une tâche, pour s’exécuter peut avoir besoin d’attendre qu’un événement survienne. La solution
consiste alors à créer un sémaphore binaire. Le sémaphore sera :
- plein si l’événement est survenu.
- vide autrement.
La tâche à synchroniser appelle semTake(), prend le sémaphore et se bloque jusqu’à ce que celui-ci
soit libéré par semGive(). La tâche ou l’interruption qui détecte l’événement appelle semGive() ce
qui libère la tâche bloquée.
Création d’un sémaphore binaire.
SEM_ID = semBCreate( int option , SEM_B_STATE valInit )
Option : ( déclarées dans semLib.h )
- SEM_Q_PRIORITY (1)
- SEM_Q_FIFO (0)
Valeur initiale :
- SEM_FULL ( 1 )
- SEM_EMPTY ( 0 )
Pour la synchronisation, le sémaphore binaire est typiquement initialisé à 0.
Prise d’un sémaphore binaire.
STATUS = semTake( SEM_ID semId , int timeOut )
Time out = temps maximum d’attente sur le sémaphore, en ticks.
- WAIT_FOREVER (-1)
- NO_WAIT (0)
Tâche en attente
jusqu'à sémaphore
donné ou time out
Tâche libérée
semTake()
retourne ERROR
Tâche libérée
semTake()
retourne OK
La tâche continue
semTake()
retourne OK
sémaphoreplein
time out
oui sémaphore donné
non
25 Système temps réel – Tornado2 Professeur : Michel MARIE
Libération d’un sémaphore binaire.
STATUS = semGive( SEM_ID semId )
Libère une des tâches en attente sur le sémaphore. Le sémaphore reste vide (0).
S’il n’y a pas de tâche en attente, le sémaphore est plein (1).
Le sémaphore
devient plein
La tâche en tête de
file d'attente devient
active, le sémaphore
reste vide
tâcheen attente
oui
non
Synchronisation de plusieurs tâches.
STATUS = semFlush( SEM_ID semId )
Libère toutes les tâches en attente sur le sémaphore.
N’affecte pas l’état du sémaphore.
Suppression d’un sémaphore binaire.
STATUS = semDelete( SEM_ID semId )
Débloque toutes les tâches en attente sur le sémaphore et détruit celui-ci.
3. Le sémaphore MUTEX pour l’exclusion mutuelle.
Le sémaphore MUTEX est nécessaire pour protéger l’accès exclusif à une ressource convoitée par
plusieurs tâches.
Contrairement au sémaphore binaire, le sémaphore MUTEX est systèmatiquement initialisé à plein
(1).
Toute tâche voulant accéder à la section protégée devra :
- Prendre le sémaphore.
- Accéder à la section critique ( la plus courte possible )
- Donner le sémaphore.
Création d’un sémaphore MUTEX.
SEM_ID = semMCreate( int options )
Options : ( déclarées dans semLib.h )
- SEM_Q_PRIORITY (1)
- SEM_Q_FIFO (0)
- SEM_DELETE_SAFE (4)
- SEM_INVERSION_SAFE (8)
Remarques :
- Ce sémaphore ne peut pas être utilisé dans une fonction interruption.
- Les options SEM_Q_FIFO et SEM_INVERSION_SAFE sont incompatibles.
- Une tâche qui prend ce sémaphore en devient le propriétaire, elle seule pourra le rendre.
- Une même tâche peut prendre ce sémaphore plusieurs fois, elle devra le restituer d’autant.
L’exemple ci-dessous présente l’intérêt de la validation de l’option SEM_INVERSION_SAFE
lorsque une tâche de faible priorité et une tâche de haute priorité accédent à une même ressource
partagée alors qu’une tâche au moins de priorité intermédiaire devient prète.
26 Système temps réel – Tornado2 Professeur : Michel MARIE
Sans option "inversion de priorité"
Avec option "inversion de priorité"
t1
t1
t2
t2
t3
t3
faible priorité
moyenne priorité
haute priorité
L’exemple ci-dessous présente l’intérêt de l’option SEM_DELETE_SAFE dans le cas de la
suppression d’une tâche propriétaire d’un sémaphore MUTEX. Les 3 tâches présentées ont la même
priorité.
t1
t2
t3
t1
t2
t3
Sans option "protection d'annulation"
Avec option "protection d'annulation"
taskDelete(t1)
taskDelete(t1)
27 Système temps réel – Tornado2 Professeur : Michel MARIE
Prise d’un sémaphore MUTEX.
STATUS = semTake( SEM_ID semId , int timeOut )
Tâche en attente
jusqu'à semaphore
donné ou time out
La tâche continue
semTake() retourne OK
Le compteur du
propriétaire est
incrémenté
La tâche continue
semTake()
retourne OK
Elle en devient
propriétaire
Propriétairedu sémaphore
Cette tâchepersonne
une autre tâche
Libération d’un sémaphore MUTEX.
STATUS = semGive( SEM_ID semId )
Le sémaphore
devient
plein (1)
Décrémentation
du compteur
propriètaire
semGive()
retourne ERROR
compteurpropriètaire
non
aucun
un
La tâche en tête
de file devient active
Elle devient propriétaire
Tâche
en attente
>1 oui
Il est toutefois possible de rendre un sémaphore MUTEX sans se soucier d’en être le propriétaire à
l’aide de la fonction semMGiveForce(). On doit généralement se limiter à l’utiliser pour la mise au
point d’applications.
STATUS = semMGiveForce( SEM_ID semId )
Suppression d’un sémaphore MUTEX.
STATUS = semDelete( SEM_ID semId )
28 Système temps réel – Tornado2 Professeur : Michel MARIE
4. Le sémaphore à compte.
- Le sémaphore à compte diffère du sémaphore binaire par la possibilité d’avoir une valeur
initiale supérieure à 1. Pour le reste il fait appel aux mêmes mécanismes et utilise les mêmes
fonctions, semTake(), semGive() et semDelete().
Création d’un sémaphore à compte.
SEM_ID = semCCreate( int option , int valeurInitiale )
Option :
- SEM_Q_PRIORITY
- SEM_Q_FIFO
5. Liste des tâches en attente sur un sémaphore.
int = semInfo( SEM_ID semId, int List[] , int nmax )
6. Le blocage de l’ordonnanceur.
Si l’action sur une ressource partagée nécessite peu de temps et doit se répéter souvent il peut être
préférable de bloquer l’ordonnanceur plutôt que d’utiliser un sémaphore. On dispose pour cela des
fonctions taskLock() et taskUnlock().
STATUS = taskLock() : bloque l’ordonnanceur.
STATUS = taskUnlock() : redémarre l’ordonnanceur.
Heureusement, si la tâche se bloque l’ordonnanceur est automatiquement redémarré.
Attention : Le blocage de l’ordonnanceur n’interrompt pas les interruptions, aussi il ne faut
pas que l’une d’entre elle puisse accéder à la même ressource partagée. Si cela est le cas il peut être
envisagé de bloquer les interruptions jusqu’à un certain niveau de priorité.
Dévalider les interruptions.
int = intLock ()
La fonction retourne une clé qui devra être passée à la fonction de déblocage des interruptions.
Restaurer les interruptions.
intUnlock ( int lockkey )
Positionner le niveau des interruptions bloquées ou restaurées par les fonctions
précédentes.
intLockLevelSet ( int newlevel)
Il est possible de lire la position de ce masque par la fonction intLockLevelGet().
29 Système temps réel – Tornado2 Professeur : Michel MARIE
Communications inter-tâches
1. Introduction.
Les systèmes multi-tâches nécessitent des outils de communication inter-tâches. Le noyau VxWorks
dispose pour ceci de bibliothèques implémentant deux types de solution :
l’usage de mémoire partagée (listes chaînées et buffer circulaires),
le passage par messages (files de messages et tubes standards).
2. L’usage de mémoire partagée (bibliothèque lstLib.h).
Ceci nécessite l’écriture d’une librairie de fonctions pour accéder à une structure de données.
Les données doivent être protégées par sémaphores.
Le noyau VxWorks met à disposition 2 librairies pour manipuler les structures de données
communes ; les listes chainées et les buffers circulaires.
La librairie lstLib contient les fonctions de manipulation des listes doublement chainées.
Suivant->
<-Précédent
Donnéesutilisateur
Suivant->
<-Précédent
Donnéesutilisateur
Premier->
Dernier->
Nombre = 2
NULL
NULL
Descripteur de liste
Elément n°1De la liste
Elément n°2De la liste
Attention, l’exclusion mutuelle et la synchronisation ne sont pas gérées par ces fonctions.
La librairie rngLib contient les fonctions de manipulation des buffers circulaires.
Buffercirculaire
m1 m2 m3
Pointeur d’écriture
Pointeur de lecture
Tâche lecteur Tâche rédacteur
Attention, la synchronisation et l’exclusion mutuelle s’il y a plus d’un lecteur/rédacteur ne sont
pas gérées par ces fonctions.
3. Le passage de messages.
VxWorks utilise les files de messages et les tubes de communication pour l’échange de messages
inter-tâches.
Ces fonctionnalités intégrent implicitement :
un buffer de type FIFO,
des mécanismes de synchronisation,
30 Système temps réel – Tornado2 Professeur : Michel MARIE
des mécanismes d’exclusion mutuelle.
Ces procédés sont conseillés car plus robustes que les mémoires partagées, celles-ci étant plutôt
réservées aux échanges inter-processeurs.
Ils peuvent être utilisés entre tâches ou entre une tâche et une routine d’interruption.
3.1. Les files de messages.
Tâche lecteurTâche rédacteur
File de messages
Sens de circulation des messages
m1m2m3
Buffer FIFO acceptant des messages de longueur variable
Créer une file de messages.
MSG_Q_ID = msgQCreate (int nbr_max_mes, int lg_max_mes,int option)
Option :
MSG_Q_FIFO
MSG_Q_PRIORITY
Envoyer un message.
STATUS = msgQSend (MSG_Q_ID msgQId, char* pbuffer, UINT nbr_bytes,
int timeOut, int priorité )
TimeOut est le temps d’attente maximum en ticks, si la file est pleine.
WAIT_FOREVER
NO_WAIT
Priorité permet à un message urgent de se retrouver en tête de file.
MSG_PRI_URGENT
MSG_PRI_NORMAL
Recevoir un message.
int = msgQReceive (MSG_Q_ID msgQId, char* pbuffer,
UINT nbr_bytes_max, int timeOut )
Nbr_bytes_max est le nombre maximum d’octets à lire d’un message. Les octets non lus sont
perdus.
Time_out est le temps d’attente maximum en ticks, si la file est vide.
WAIT_FOREVER
NO_WAIT
Supprimer une file de messages.
STATUS = msgQDelete (MSG_Q_ID msgQId)
Les tâches en attente sur la file sont libérées, msgQSend() et msgQReceive() retournent
"ERROR".
31 Système temps réel – Tornado2 Professeur : Michel MARIE
3.2. Les tubes de communication “pipes”.
Les tubes constituent un circuit virtuel d’entrée/sortie géré par un pilote "pipeDrv".
L’avantage sur les files de messages est par conséquent l’utilisation d’un système d’interface
standard (read/write ; open/close ; create/remove ; ioctl).
Créer un tube de communication.
STATUS = pipeDevCreate ( char* nom, int nbr_mes, int nbr_bytes )
Par convention, le nom d’un tube de communication s’écrit : /pipe/monpipe
Remarque : La commande du shell "devs" permet de lister les tubes créés.
Lire et écrire dans un tube de communication.
Il faut tout d’abord ouvrir le tube de communication avec la fonction open, celle-ci retourne un
descripteur de fichier qui sera utilisé pour tous les accès à ce tube.
Fd = open ( char* name, int flags, int mode )
Les flags sont associables par un OU logique :
O_RDONLY (0)
O_WRONLY (1)
O_RDWR (2)
O_CREAT (0x200)
Le mode n’existe que pour les drivers NFS, autrement il ne doit pas apparaître où 0.
Une fois le tube ouvert, la lecture et l’écriture utilisent les fonctions standards read()/write(). De
même le contrôle du tube se fera à l'aide de la fonction standard de configuration et de contrôle
ioctl().
Exemples :
Lire la taille du premier message du tube :
status = ioctl (fd, FIONREAD, &nBytesUnread);
Lire le nombre de messages disponibles dans le tube :
status = ioctl (fd, FIONMSGS, &nMessages);
Vider le tube de tous ses messages :
status = ioctl (fd, FIOFLUSH, 0);
Supprimer un tube de communication.
La suppression d’un tube de communication se fait en associant les fonctions suivantes :
char* tube =(char*)malloc(50);
strcpy( tube, "/monTube");
iosDevDelete ( iosDevFind ( tube,*tube ) )
3.4. Comparaisons queues de messages et tubes de communication “pipes”.
Avantages des queues de messages :
simplicité de positionnement d'un time_out,
gestion de messages urgents,
plus rapide.
Avantages des tubes de communication :
Utilise les fonctions d’entrée/sortie standards (open(), close(), read(), write(), ioctl).
Possibilité de rediriger la sortie à l’aide des fonctions ioTaskStdSet() et
ioGlobalStdSet().
32 Système temps réel – Tornado2 Professeur : Michel MARIE
Gestion des attentes multiples
Rappel sur les descripteurs de fichier :
Sur un système ANSI, les circuits ouverts sont référencés par un descripteur de fichier, fd. Ce
descripteur est un entier court retourné par les primitives open() ou creat() et utilisé par les autres
primitives pour spécifier le fichier visé.
A la fermeture du fichier, le descripteur est désalloué.
Trois descripteurs de fichiers sont réservés et ont une acception spécifique :
- 0 = entrée standard (clavier, port série,…),
- 1 = sortie standard (écran, port série sur terminal,…),
- 2 = sortie d’erreur standard (écran, fichier,…).
Il est possible de rediriger globalement l’un ou l’autre des descripteurs de fichiers à l’aide de la
fonction ioGlobalStdSet().
Par exemple, la commande suivante redirige la sortie standard vers un fichier ouvert de descripteur
fd_file :
ioGlobalStdSet ( 1 , fd_file )
Cette redirection peut également être opérée localement à une tâche à l’aide de la fonction
ioTaskStdSet().
Par exemple, la commande suivante redirige la sortie standard vers un fichier ouvert de descripteur
fd_file vis-à-vis de la tâche qui l’exécute ; les autres tâches ne sont pas affectées par cette
redirection :
ioTaskStdSet ( 1 , fd_file )
1. Attente sur plusieurs descripteurs de fichiers.
Les facilités apportées par la primitive select() de la librairie selectLib nous donne une méthode
d’attente sur plusieurs descripteurs de fichiers ; cette primitive ANSI est compatible avec les
systèmes Windows et UNIX.
La librairie selectLib permet entre autres :
de bloquer une tâche en attente sur plusieurs circuits,
de donner à un pilote la possibilité de détecter les tâches qui sont bloquées en attente d’une entrée/sortie sur le circuit,
de spécifier un time-out relatif à l’attente d’une tâche sur un circuit.
Fonction d'attente sur plusieurs descripteurs
int select
(
int width, /* nombre de bits à examiner dans la structure fd_set */
fd_set *pReadFds, /* structure fd_set de lecture */
fd_set *pWriteFds, /* structure fd_set d’écriture */
fd_set *pExceptFds, /* (non supporté, compatibilité UNIX) */
struct timeval *pTimeOut /* temps d’attente maximum, NULL = infini */
)
Cette fonction réalise la mise en attente de la tâche qui l’exécute jusqu’à ce qu’un des descripteurs
de fichier spécifié soit prêt.
33 Système temps réel – Tornado2 Professeur : Michel MARIE
La structure fd_set utilisée par select() est en fait un tableau de bits ou le bit d’indice n correspond
au descripteur de fichier n. Selon que l’on désire un accès en lecture et/ou en écriture on initialisera
les bits de la structure fd_set pointée par pReadFds et/ou par pWriteFds.
L’argument width doit correspondre à l’indice du plus grand des bits à tester de l’ensemble des
structures fd_set augmenté de un.
La structure timeval définie dans le fichier times.h est utilisée pour positionner un time-out lors
d’une attente en lecture ou en écriture par select().
struct timeval
{
long tv_sec; /* délai du time-out en secondes */
long tv_usec; /* délai du time-out en microsecondes */
};
La fonction select() retourne un entier dont la valeur correspond à la somme des indices des
descripteurs de fichiers prêts, ou "0" si la sortie se fait par time_out, ou -1 (ERROR) si l’exécution
de select a donné lieu à une erreur.
De plus, dans les structures fd_set passées à la fonction, seul les bits pour lesquels les descripteurs
sont prêts restent marqués à 1 après la sortie du select.
Les "macros fonctions" suivantes permettent de positionner les bits de la structure fd_set :
FD_SET ( fd, fd_set*)
FD_CLR ( fd, fd_set*)
FD_ZERO ( fd_set*)
La "macro fonction" suivante permet de tester un bit de la structure fd_set :
int FD_ISSET ( fd, fd_set*)
Exemple d’utilisation de select() pour gérer une attente en lecture sur plusieurs
descripteurs de fichier.
Fonction select :
Soit la création de deux tubes de communication ouverts en lecture/écriture :
/* Création des tubes de communication /*
pipeDevCreate(/pipe/1, 10, 100) ;
pipeDevCreate(/pipe/2, 10, 100) ;
/* Ouverture des tubes en lecture et écriture /*
fd1 = open (/pipe/1, O_RDWR, 0) ;
fd2 = open (/pipe/2, O_RDWR, 0) ;
On désire à présent bloquer une tâche en attente de lecture sur les deux tubes précédemment
ouverts. Pour cela il est nécessaire de positionner à 1, dans une structure de type fd_set, les bits
correspondant aux descripteurs de fichier des tubes précités. On utilise alors les "macros fonctions"
inclues dans la librairie selectLib permettant de manipuler les bits de la structure fd_set.
34 Système temps réel – Tornado2 Professeur : Michel MARIE
/* Mise à 1 des bits de la structure fd_set */
fd_set fdSet ;
int width ;
int result ;
char buffer1[100] ;
char buffer2[100] ;
FD_ZERO ( &fdSet ) ;
FD_SET ( fd1 , &fdSet ) ;
FD_SET ( fd2 , &fdSet ) ;
width = ( fd1 > fd2 ) ? fd1 : fd2 ;
width++ ;
Ensuite, la fonction select() se bloque jusqu’à ce qu’ un des descripteurs testés, au moins, soit prêt.
result = select( width, &fdSet, 0, 0, 0) ;
Lorsque la tâche précédente se termine, il suffit de tester les bits de la structure fdSet pour connaître
le ou les descripteur(s) de fichier actif(s).
if ( FD_ISSET ( fd1, &fdSet )>0)
{
read ( fd1, buffer1 , 100 ) ;
printf( buffer1);
}
if ( FD_ISSET ( fd2, &fdSet )>0 )
{
read ( fd2, buffer2 , 100) ;
printf( buffer2);
}
35 Système temps réel – Tornado2 Professeur : Michel MARIE
Exceptions, interruptions et timers
1. Introduction.
Un signal est d'origine interne (une exception) ou externe (une interruption). Il est émis vers une
tâche indiquant qu’un événement asynchrone est survenu.
Exception : signal d’origine interne au système, généralement lié à la détection d’une erreur
grave d’exécution. (division par zéro, accès mémoire interdit, etc…).
Interruption : signal d’origine externe au système, signalant la nécessité de réaliser une
« tâche immédiate ». (action clavier, action sur un capteur, etc…).
Les Timers : cas particulier d’un signal d’interruption émis périodiquement pour activer
une tâche. (ordonnanceur du noyau temps réel par exemple).
VxWorks dispose de 31 signaux de type exception, chacun représentant un événement particulier.
Une tâche peut attacher une capture de signal “signal handler” pour réaliser une action particulière
lorsque un événement survient. Après exécution de cette action la tâche interrompue est reprise
comme s’il ne s’était rien passé.
Fonction "normale"
{
}
Fonction "signalHandler"
{
}
signal
2. Positionner un “signal handler”.
void* signal ( int num_signal, void* handler )
num_signal : numéro du signal à capturer ( consulter sigLib.h et signal.h )
handler : fonction à invoquer lorsque le signal est capturé.
SIG_IGN si le signal doit être ignoré, SIG_DFL pour la fonction
"handler" par défaut.
Le “signal handler” doit être, au minimum déclaré comme suit :
void sigHandler (int sig )
En fait, les paramètres passés au “signal handler” sont au nombre de trois, pour accéder à ces
paramètres additionnels déclarer le “signal handler” comme suit :
void sigHandler (int sig , int code , struct sigcontext *pSigCtx )
L’argument code permet de distinguer plusieurs exceptions qui donneraient lieu au même signal.
Les codes sont répertoriés dans le fichier sigCodes.h.
L’argument pSigCtx pointe sur une structure où se trouve sauvegardé le contexte de la tâche lors de
la réception du signal.
Signal et exceptions.
Toutes les exceptions logicielles donnent lieu à un signal. Si lors d’une exception le signal est
capturé par un “signal handler”, la fonction attachée devra faire en sorte de “renflouer” la tâche
origine de l’exception.
36 Système temps réel – Tornado2 Professeur : Michel MARIE
Si le signal n’est pas capturé VxWorks suspend la tâche et retourne un message d’erreur sur la
console.
La tâche a installé
la capture du signal
SIGSEGV ?
Fonction "normale"
{
}
La tâche est suspendue
génération d'un "Log error message"Le signal
est renfloué
Typiquement une fonction de capture d’un signal d’exception appelle :
exit() : Pour terminer la tâche.
taskRestart() : Pour redémarrer la tâche.
longjmp() : Pour reprendre l’exécution de la tâche à un endroit sauvegardé
par setjmp().
3. Configurer un signal d’interruption.
Une interruption permet à des circuits périphériques de notifier au CPU qu’un événement extérieur
est survenu.
Une fonction définie par l’utilisateur peut être installée afin de s’exécuter lorsque cet événement
arrive. Cette fonction s’exécute dès réception de l’événement, elle préempte toute tâche quelle que
soit sa priorité, ce n’est pas une tâche logicielle mais une tâche immédiate.
Les fonctions qui permettent à l’utilisateur d’installer et de gérer ses propres fonctions
d’interruption se trouvent dans les librairies intLib.h et intArchLib.h.
Le principe reste identique à l’installation d’un “signal handler” d’exception, la fonction attachée au
“signal handler” se chargeant après sauvegarde des registres du CPU d’appeler une fonction de
traitement.
Le “signal handler” d’interruption peut être installé avec la fonction intConnect().
Remarque : Suivant le CPU de la cible, la pile utilisée par les interruptions est soit une pile dédiée,
soit la pile de la tâche interrompue. Dans ce second cas, si le risque existe il est nécessaire de
prévoir une pile des tâches en conséquence.
Pour le 68040, la pile des interruptions est une pile dédiée, celle-ci est controlée par
INT_STACK_SIZE dans le fichier configAll.h.
Fonctions de la librairie intLib : BOOL intContext (void)
Cette fonction retourne TRUE si le contexte d'exécution courant est le mode "interruption"
et FALSE si c'est le mode "tache".
int intCount (void)
Cette fonction retourne le nombre d'interruptions qui sont en attente de traitement.
Quelques fonctions de la librairie intArchLib : int intLevelSet( int level )
Cette fonction modifie le masque des interruptions dans le registre d'état du processeur SR.
La valeur retournée est le niveau initial du masque.
int intEnable (int level )
37 Système temps réel – Tornado2 Professeur : Michel MARIE
Cette fonction valide les interruptions pour le niveau précisé. La valeur retournée est soit
OK ou ERROR ou encore le contenu du status register selon le processeur utilisé.
int intDisable ( int level )
Cette fonction dévalide les interruptions pour le niveau précisé. La valeur retournée est soit
OK ou ERROR ou encore le contenu du status register selon le processeur utilisé.
int intCRGet (void)
Cette fonction retourne le contenu du registre des causes des interruptions.
void intCRSet(int value )
Cette fonction positionne le contenu du registre des causes des interruptions.
int intSRGet (void)
Cette fonction retourne le contenu du registre d'état du processeur "SR = Status Register".
int intSRSet ( int value )
Cette fonction positionne le contenu du registre d'état du processeur et retourne sa
précédente valeur.
STATUS intConnect
(
VOIDFUNCPTR * vector, /* adresse du vecteur */
VOIDFUNCPTR routine, /* adresse fonction d'IT */
int parameter /* paramètre passé à la fonction d'IT */
)
Cette fonction attache une fonction d'interruption à un vecteur d'interruption. FUNCPTR intHandlerCreate
(
FUNCPTR routine, /* Fonction attachée */
int parameter /* paramètre passé à la fonction */
)
Cette fonction construit un lien vers une fonction qui doit ensuite être attachée à une
interruption par intVecSet(). Elle retourne un pointeur vers le lien créé ou NULL si le lien
n'est pas réalisable.
Remarque : Cette fonction ainsi que intVecSet() et intVecBaseGet() sont utilisées en
interne par intConnect().
void intVecBaseSet
(
FUNCPTR *baseAddr /*Adresse de base de la table des vecteurs*/
)
Cette fonction positionne l'adresse de base de la table des vecteurs. Cette valeur est utilisée
par les fonctions intVecSet() et intVecGet().
FUNCPTR *intVecBaseGet (void)
Cette fonction retourne l'adresse de base de la table des vecteurs.
void intVecSet
(
FUNCPTR * vector, /* offset dans la table des vecteurs */
FUNCPTR function /* addresse à placer dans le vecteur */
)
Cette fonction place un vecteur dans la table en spécifiant son offset.
FUNCPTR intVecGet
(
FUNCPTR * vector /* offset dans la table des vecteurs */
)
Cette fonction retourne l'adresse dans la table des vecteurs en spécifiant son offset.
38 Système temps réel – Tornado2 Professeur : Michel MARIE
Macro procédures pour le traitement des vecteurs d'interruption :
IVEC_TO_INUM(int Vector)
Convertit l'adresse correspondante à un vecteur en son numéro de vecteur.
INUM_TO_IVEC(int Number)
Convertit un numéro de vecteur en l'adresse correspondante.
TRAPNUM_TO_IVEC( int trapNumber)
Convertit un numéro de vecteur TRAP en l'adresse correspondante.
Exemple :
Exemple de connection d'un vecteur d'IT unique entre plusieurs fonctions.
vector = INUM_TO_IVEC(some_int_vec_num);
oldfunc = intVecGet (vector);
newfunc = intHandlerCreate (routine, parameter);
intVecSet (vector, newfunc); /* nouvelle fonction d'IT */
...
intVecSet (vector, oldfunc); /*utilise la fonction initiale*/
...
intVecSet (vector, newfunc); /* reconnecte à la nouvelle
fonction */
4. Restrictions sur les interruptions.
Les fonctions d’interruptions sont restrictives quant aux facilités de VxWorks, en particulier :
Une interruption ne peut pas appeler semTake().
Une interruption de peut pas utiliser malloc().
Une interruption ne peut pas utiliser les fonctions standard d’entrée/sortie système (printf(), etc…).
La seule fonction d’entrée/sortie autorisée sous interruption est write().
Cas particulier : Si un signal d’exception survient au cours d’une interruption cela génére un
redémarrage “à chaud” du noyau VxWorks.
5. Timers et watchdog timers.
Les timers sont utiles pour :
Forcer des programmes utilisateur à être exécutés périodiquement : - Scrutation matérielle.
- Chien de garde pour sortie d’une opération interminable.
VxWorks contient des fonctions d’interface visant à gérer deux timers :
- Un basé sur l’horloge système.
- Un basé sur une horloge auxiliaire.
Créer un watchdog timer.
Utilisé pour exécuter une fonction après un délai spécifié. L’activation de la fonction est réalisée par
une interruption.
WDOG_ID = wdCreate ( )
Retourne un identificateur de watchdog ou NULL si erreur.
WDOG_ID spécifié dans le fichier wdLib.h.
Démarrer ou redémarrer un watchdog timer.
39 Système temps réel – Tornado2 Professeur : Michel MARIE
STATUS = wdStart (WDOG_ID wdId , int délai , FUNCPTR pFonction ,int paramètre )
Le délai doit être précisé en ticks.
Un seul paramètre peut être passé à la fonction à exécuter.
Annuler ou détruire un watchdog timer.
STATUS = wdCancel (WDOG_ID wdId )
Annule la fonction wdStart mais le watchdog existe toujours.
STATUS = wdDelete (WDOG_ID wdId )
Annule la fonction wdStart et détruit le watchdog.
Exemple, utilisation d’un watchdog timer pour sortir d’une fonction trop longue.
WDOG_ID wdId;
void fonction_appel (void)
{
wdId = wdCreate( );
FOREVER
{
wdStart ( wdId , 10*sysClkRateGet(), fonction_sortir, 0);
fonction_trop_longue();
}
}
void fonction_sortir ( int param )
{
…….
}
Utilisation de l’horloge auxiliaire.
Pour une scrutation rapide il est préconisé d’utiliser l’horloge auxiliaire du système. 5 fonctions
permettent d’utiliser cette horloge.
sysAuxClkEnable : Pour démarrer l’horloge.
sysAuxClkDisable : Pour stopper l’horloge.
sysAuxClkRateGet : Pour connaître la fréquence horloge.
sysAuxClkRateSet : Pour initialiser la fréquence horloge.
sysAuxClkConnect : Pour connecter une fonction d'IT à cette horloge.
40 Système temps réel – Tornado2 Professeur : Michel MARIE
La programmation objet C++ avec VxWorks
Tornado comprend les outils de compilation « GNU C++ ToolKit’s » et de débuggage CrossWind
permettant de développer des programmes objet.
1. Généralités du langage C++.
Pour pouvoir utiliser les fonctionnalités du langage objet il est nécessaire de déclarer à la création
de l’image du noyau VxWorks l’une des deux définitions suivantes :
INCLUDE_CPLUS : Ajoute au noyau l’intégralité des outils C++ dont dispose le système. Cette déclaration est nécessaire pendant la phase de
développement de l’application.
INCLUDE_CPLUS_MIN: Ajoute au noyau uniquement les outils C++ nécessaires à
l’application. Cette déclaration est à réaliser lors de la construction
définitive de l’application.
Les modules écrits en langage objet C++ disposent de constructeurs et de destructeurs. Il est
possible de choisir d’appeler ceux-ci soit manuellement soit automatiquement à l’aide de la fonction
cplusXtorSet( choix ).
choix = 0 : Le constructeur et le destructeur doivent-être appelés manuellement.
choix = 1 : Le constructeur est appelé automatiquement lors du chargement du module d’extension .out ld() et le destructeur est appelé automatiquement lors du
déchargement du module par unld().
Si le choix d’un appel manuel a été effectué, les fonctions cplusCtors() et cplusDtors() permettent
respectivement d’appeler le constructeur et le destructeur d’un module.
cplusCtors (monModule ) : Appel du constructeur du module monModule.
cplusCtors () : Appel du constructeur de tous les modules.
cplusDtors (monModule ) : Appel du destructeur du module monModule.
cplusDtors () : Appel du destructeur de tous les modules.
cplusCtorsLink () : Appel du constructeur de tous les modules liés au noyau.
cplusDtorsLink () : Appel du destructeur de tous les modules liés au noyau.
Attention, WindShell ne connaît ni les instructions à point, ni les instructions à flèche. Par exemple pour appeler avec l’argument 3 la fonction membre cri() de l’objet chien, instance de la
classe animal, la syntaxe est la suivante :
cri ( &chien , 3)
Wouaf , Wouaf , Wouaf
Si la fonction cri est surchargée, le Shell vous demande alors de préciser quelle fonction il doit
utiliser (pas terrible !). Exemple :
cri ( &chien , 3)
0 : animal : :cri ( int )
1 : humain : :cri ( int )
2 : inhumain : :cri ( int )
Choose the number of the symbol to use : 0
Wouaf , Wouaf , Wouaf
2. Librairie des entrées/sorties standard C++.
Il est également possible d’inclure au noyau la librairie standard des entrées/sorties formatées.
Cette librairie ajoute entre autres les objets cin, cout, cerr, clog, les opérateurs surchargés << et >>.
Pour ajouter cette librairie il faut définir INCLUDE_CPLUS_IOSTREAMS dans le noyau.
41 Système temps réel – Tornado2 Professeur : Michel MARIE
Systèmes de fichiers
1. Introduction.
Les systèmes de fichiers sur VxWorks permettent :
D’utiliser des RAM disques.
D’utiliser un système de fichiers DOS sur un disque local.
D’utiliser un système de fichier UNIX sur un disque local.
D’utiliser des circuits SCSI.(Small Computer System Interface )
Pour gérer les systèmes de fichiers il est nécessaire d’avoir :
Un “pilote ou driver” de blocs.
Lecture/écriture de fichiers.
Formatage de disque.
Des fonctions de gestion des systèmes de fichiers.
Les “pilotes” de blocs sont :
RamDrv : pour la création d’une RAM disque.
ScsiLib : pour les accés aux circuits SCSI.
Les librairies de fonctions associées sont :
dosFsLib : pour la compatibilité avec les fichiers DOS.
RawFsLib : pour la compatibilité avec UNIX.
2. Création d’une RAM disque.
BLK_DEV* = ramDevCreate (char* addr_ram, int bytes/bloc, int blocs/piste,
int nbr_blocs , int bloc_offset)
Retourne un pointeur sur une structure BLK_DEV décrivant la RAM disque.
Il est nécessaire de définir INCLUDE_RAMDRV dans la configuration du noyau VxWorks afin
d’y inclure le pilote de RAM disque ramDrv au noyau.
3. Système de fichiers DOS.
Dans ce cas le système de fichiers est hiérarchisé, les noms de fichiers sont limités au format 8.3,
les disques sont interchangeables avec les disques PC utilisant MS_DOS.
Configuration du système de fichiers DOS.
Il faut pour ceci définir INCLUDE_DOSFS dans la configuration du noyau VxWorks.
Dans le fichier configAll.h, NUM_DOSFS_FILES défini le nombre maximum de systèmes de
fichiers dos pouvant être ouverts, 20 par défaut.
Pour créer un système de fichiers DOS.
42 Système temps réel – Tornado2 Professeur : Michel MARIE
DOS_VOL_DESC* = dosFsMkfs ( char* rootDir, BLK_DEV* pDisque )
pDisque : Pointeur du type BLK_DEV sur le pilote du disque obtenu par la fonction
xxDevCreate(). La fonction retourne un pointeur sur une structure de description du système de
fichiers créé.
la fonction dosFsMkfsOptionsSet(UINT options) peut être appelée avant la précédente pour
spécifier les options de volume du disque.
Les fonctions dosFsConfigShow(char * name) et dosFsConfigGet(DOS_VOL_DESC* pdesc,
DOS_VOL_CONFIG* pResultConfig) affichent et retournent respectivement les informations de
configuration sur le système de fichiers créé.
Les commandes du Shell diskFormat(char* name) et diskInit(char* name) permettent
respectivement de formatter le disque et de réécrire le système de fichiers.
Pré-allocation d’espace sur le disque. Appel de la primitive standard
ioctl(int fd, int fonction, int arg)
Il est possible de pré-allouer un espace contigu de mémoire sur le disque pour un fichier, ceci afin
de diminuer le temps d’accès.
STATUS = ioctl ( fd , FIOCONTIG , nbr_bytes )
Cette fonction doit être appelée avant toute écriture du fichier ! Si l’espace demandé n’est pas
réalisable la fonction retourne ERROR.
Pour pré-allouer un espace non contigu la même fonction est utilisée avec la commande
FIOTRUNC.
STATUS = ioctl ( fd , FIOTRUNC , nbr_bytes )
Elle permet également de connaître la taille maximale d’espace contigu avec la commande
FIONCONTIG.
STATUS = ioctl ( fd , FIONCONTIG , &max_cont_bytes )
Ou encore de formatter le disque ou d’initialiser le système de fichier avec les commandes
FIODISKFORMAT et FIODISKINIT. De nombreuses autres commandes sont disponibles,
consulter l'aide de dosFsLib.
STATUS = ioctl ( fd , FIODISKFORMAT , 0 )
STATUS = ioctl ( fd , FIODISKINIT , 0 )
4. Pilote de système de fichier UNIX.
Il faut pour ceci définir INCLUDE_RAWFS dans la configuration du noyau VxWorks.
Dans le fichier configAll.h, NUM_RAWFS_FILES défini le nombre maximum de systèmes de
fichiers raw pouvant être ouverts, 5 par défaut.
Pour créer un système de fichier UNIX.
RAW_VOL_DESC* = rawFsDevInit ( char* nom_sys , BLK_DEV* pDisque )
pDisque : Pointeur du type BLK_DEV sur le pilote du disque obtenu par la fonction
xxDevCreate().
43 Système temps réel – Tornado2 Professeur : Michel MARIE
A partir de ce moment les fonctions de gestion du disque sont les mêmes, que ce soit un système de
fichiers DOS ou un système de fichiers UNIX.
5. Gestion de bus SCSI.
Le bus SCSI permet de connecter jusqu’à 8 périphériques différents, disque dur, lecteur CD_ROM,
etc…
Les fonctions VxWorks supportent les standards SCSI-1 et SCSI-2. Les librairies contenant ces
fonctions sont scsiLIb et scsiSeqLib.
Pour utiliser le bus SCSI plusieurs définitions sont à ajouter au fichier config.h.
- INCLUDE_SCSI : Fonctions de base du bus SCSI-1.
- INCLUDE_SCSI2 : Fonctions de base du bus SCSI-2.
- INCLUDE_SCSI_BOOT : Pour booter VxWorks sur le bus SCSI.
- INCLUDE_TAPEFS : Pour l’utilisation de SCSI-2 uniquement, accés
séquentiels.
Installation d’un périphérique SCSI.
La fonction scsiPhysDevCreate() permet d’initialiser un périphérique SCSI.
La fonction scsiBlkDevCreate() retourne un pointeur sur une structure de contrôle permettant la
gestion logique du périphérique.
Exemple : Configuration d’un disque dur.
pScsi = scsiPhysDevCreate ( pSysScsiCtrl , budId , 0 , 0 ,NONE , 0 , 0 , 0 );
pBlk = scsiBlkDevCreate ( pScsi , taille , offset );
dosFsDevInit ( “\DOS\ , pBlk , NULL );
44 Système temps réel – Tornado2 Professeur : Michel MARIE
Les communications réseau
1. Les bases réseau.
Le noyau VxWorks implémente le protocole d’échange entre machines distantes TCP/IP 4.4. Il
supporte les communications réseau au travers :
De l’interface Ethernet.
Du bus VME.
Du bus série. Les services réseau de VxWorks incluent :
L’accès aux fichiers distants.
La connexion à distance.
L’exécution de commandes à distance.
Si les machines à mettre en communication sont situées sur des réseaux différents, les données
transférées doivent être routées.
Si l’adresse de destination n’est pas sur le réseau courant l’algorithme de routage utilise une table de
routage.
Les adresses Internet :
L'adresse Internet (TCP/IP) d'une machine ou "nœud réseau" est une adresse codée sur 4 octets (IP
v4) ou dorénavant sur 16 octets (IP v6). Dans sa notation courante chaque octet est représenté dans
sa notation décimale séparé par un point, la "Dot Notation", ex: "192.168.10.33". Chaque adresse
est séparée en deux éléments, un champ réseau (numéro de réseau auquel appartient la machine,
attribué par l'Internic pour un réseau public), et un champ machine (numéro de la machine sur son
réseau). Selon leur taille, les réseaux sont repérés par 3 classes :
Classe A 0 Réseau 7 bits Machine 24 bits
Classe B 0 Réseau 14 bits Machine 16 bits1
Classe C 1 Réseau 21 bits Machine 8 bits1 0
Réseau 1.X.X.X à 127.X.X.X
Réseau 128.X.X.X à 191.X.X.X
Réseau 192.X.X.X à 223.X.X.X
La classe D (adresse réseau de 224.X.X.X à 239.X.X.X) est réservée aux adressages « multicast ».
La classe E (adresse réseau de 240.X.X.X à 247.X.X.X) est réservée aux expérimentations.
- Une adresse Internet avec le champ machine à 0 fait référence à un réseau.
- Une adresse Internet avec le champ machine entièrement à 1 est l’adresse « broadcast » qui
adresse tous les nœuds du réseau.
- Certains systèmes obsolètes utilisant l’adresse 0 comme « broadcast », il est possible de le
spécifier à l’aide de la fonction ifBroadcastSet().
- Le réseau classe A 0.0.0.0 est réservé pour les routes par défaut, tous les paquets destinés à un
réseau inconnu seront dirigés vers cette route.
- Le réseau classe A 127.0.0.0 est réservé pour le traffic IP local à la machine. Une interface
locale dite "loopback" porte en général l'adresse 127.0.0.1, utilisation du réseau en rebouclage
local.
- Si le réseau est connecté à l’Internet, son préfixe réseau doit être attribué par l’InterNIC
(Internet Network Information Center).
45 Système temps réel – Tornado2 Professeur : Michel MARIE
Certaines adresses de réseau peuvent également être librement utilisées pour un réseau privé :
classe A : 10.0.0.0
classe B : 172.16.0.0 à 172.31.0.0
classe C : 192.168.0.0 à 192.168.255.0
Aucun paquet à destination ou provenant de ces réseaux ne peut être routé sur l'internet.
D'un point de vue configuration, la séparation entre le champ machine et le champ réseau d'une
adresse IP se fait à l'aide d'un masque de réseau "NetMask", pour lequel tous les bits marqués à 1
signalent le champ réseau.
Exemple, réseau de classe C, NetMask = 255.255.255.0
2. Fonctions de manipulation des adresses internet.
Pour utiliser les fonctions de connexion réseau, les adresses IP devront faire l’objet de conversions.
VxWorks mémorise les adresses IP à points comme des entiers.
Par exemple la notation à points 90.0.0.70 est stockée en interne sous la forme 0x5a000046.
u_long inet_addr(char* inetstring) : convertit une notation à point en un entier.
int inet_Inaof(int inetAdr) : retourne le champ machine de l’adresse internet.
int inet_netof(struct in_addr inetAdr) : retourne le champ réseau de l’adresse internet.
inet_netof_string(char* inetstr, char *netstr) : retourne le champ réseau de l’adresse sous la forme d’une chaîne de caractères.
inet_ntoa_b(struct in_addr inetAdr, char* adr pStr) : convertit l’adresse Internet dans
sa notation ASCII à point.
La structure in_addr représente une machine par son adresse internet :
typedef struct in_addr {
union {
struct {
u_char s_b1,s_b2,s_b3,s_b4;
} s_un_b;
struct {
u_short s_w1,s_w2;
} s_un_w;
u_long s_addr;
} s_un;
} in_addr;
Pour associer un nom à une adresse Internet :
STATUS hostAdd (char* host_name , char* host_adresse )
Pour afficher l’ensemble des noms associés aux adresses Internet (table des adresses)
hostShow( )
Pour ajouter une route à la table de routage :
STATUS routeAdd ( char* destination , char* gateway )
Pour supprimer une route de la table de routage :
STATUS routeDelete ( char* destination , char* gateway)
Pour visualiser la table de routage :
routeShow ( char* destination , char* gateway )
Exemple :
ROUTE NET TABLE
destination gateway flags Refcnt Use Interface
46 Système temps réel – Tornado2 Professeur : Michel MARIE
150.39.0.0 147.11.54.254 3 0 0 enp0
ROUTE HOST TABLE
destination gateway flags Refcnt Use Interface
127.0.0.1 127.0.0.1 5 0 0 Io0
Les flags courants sont :
0x1 Route utilisable
0x2 La destination est une porte d’accès.
0x4 Entrée spécifique de routage.
0x10 Route créée dynamiquement (par redirection)
0x20 Route modifiée dynamiquement (par redirection)
Refcnt indique le nombre de connexions ouvertes utilisant la route.
Use est le nombre total de paquets envoyés au travers de la route.
Interface est le nom de l’interface réseau utilisé.
Pour tester les routes il est pratique d’utiliser la fonction ping.
STATUS = ping ( char* pdestination ,int nbr_paquets ,int options )
3. Connexion à distance sur une cible VxWorks.
Deux protocoles sont envisageables pour ce type de connexion :
rlogin : à partir d’un poste UNIX, définir INCLUDE_RLOGIN lors de la construction du noyau VxWorks.
telnet : utilise le protocole Internet, définir INCLUDE_TELNET lors de la
construction du noyau VxWorks.
La liaison peut être sécurisée pour restreindre les accès à l’aide d’un nom d’utilisateur et d’un mot
de passe.
Pour ceci, définir INCLUDE_SECURITY et spécifier LOGIN_USER_NAME et
LOGIN_PASSWORD.
Il est ensuite possible d’ajouter des utilisateurs avec la fonction loginUserAdd().
Attention, la connexion à distance nécessite que le Shell soit résidant à la cible, pour ceci définir INCLUDE_SHELL lors de la construction du noyau.
4. Exécution de commandes à distance.
Les programmes VxWorks peuvent invoquer des commandes RSH “Remote Shell” sur un poste
UNIX distant. Une "socket" est alors créée, VxWorks sera dès lors toujours vu comme client.
Exécution d’une commande à distance.
socket = rcmd ( char* destination ,int remote_port ,char* local_user ,char* remote_user
,char* cmd ,int* pFd )
Le "remote_port" est typiquement 514 pour le Shell.
Exemple : Utilisation de la commande Date sous UNIX.
unixDate = calloc (100,1)
socket = rcmd (“serveur_UNIX”, 514 , ”Arthur” , “Martin” , “date” , 0 )
read ( socket , unixDate , 100 )
printf ( “%s\n”, unixDate )
47 Système temps réel – Tornado2 Professeur : Michel MARIE
5. Accès à des fichiers distants.
Le noyau VxWorks intègre deux pilotes permettant les accès à des fichiers situés sur les machines
distantes.
nfsDrv,
netDrv. Les protocoles utilisables sont NFS (Network File System) et FTP (File Transfert Protocol).
5.1. Protocole NFS.
NFS est un protocole développé par Sun Microsystems. Le noyau VxWorks permet d’inclure un
client ou un serveur NFS. Le serveur NFS est inclus au noyau par défaut, pour inclure le client il
faut définir INCLUDE_NFS.
Monter un “file system” NFS.
STATUS = nfsMount (char* destination ,char* file_system ,char* local_name )
destination : nom de la machine distante.
file_system : nom du “file system” à monter (devant être exporté par la machine distante).
local_name : nom du circuit local d’accès au file system, par défaut filesystem si NULL.
Démonter un “file system” NFS.
STATUS = nfsUnmount (char* local_name )
Examiner un “file system” NFS.
nfsDevShow ( )
Authentification NFS.
nfsAuthUnixSet (char* destination ,int uid ,int gid ,int nbr_gids ,int* aup_gids )
uid : Nom d’utilisateur machine distante.
gid : Groupe d’utilisateur sur machine distante.
nbr_gids : Nombre de groupes dans aup_gids.
aup_gids : Tableau des groupes additionnels autorisés.
Exemple :
hostAdd ( “serveurNFS”,“10.83.16.10”)
nfsMount (“serveurNFS” , ”/root” , ”/nfsDev” )
nfsAuthUnixSet (“serveurNFS” , uid , gid , 0 , 0 )
fd = open(“/nfsDev/fichierx” , 2 )
………….
close ( fd )
5.2. Protocole FTP.
Le client FTP est résident à la cible puisqu’utilisé pour télécharger le noyau. Pour installer un
serveur FTP il est nécessaire de définir INCLUDE_FTP_SERVER et INCLUDE_SYM_TBL.
Le serveur FTP est sécurisé, pour entrer un nom d’utilisateur et un mot de passe à partir de vos
applications, il est nécessaire d’utiliser les fonctions loginInit() pour initialiser la table de login et
loginUserAdd() pour y ajouter un utilisateur. Le mot de passe doit auparavant être encrypté avec la
fonction loginDefaultEncrypt().
48 Système temps réel – Tornado2 Professeur : Michel MARIE
Exemple:
loginInit ();
mdpe=malloc(80);
loginDefaultEncrypt (“montsaint” , mdpe )
loginUserAdd (“michel” , mdpe )
Exemple: Echange via FTP entre un serveur VxWorks et un client UNIX.
Serveur VxWorks:
pRAM = ramDevCreate ( 0 , 512 , 0 , 400 , 0 )
dosFsMkfs ( “/RAM” , pRAM )
fd = creat (“/RAM/fichier” , 2 )
write ( fd , pData , nbr_bytes )
close ( fd )
Client UNIX:
% ftp VxServer
ftp> cd /RAM
ftp> get fichier
ftp> quit
Exemple: Echange via FTP entre un serveur VxWorks et un client VxWorks.
Serveur VxWorks:
pRAM = ramDevCreate ( 0 , 512 , 0 , 400 , 0 )
dosFsMkfs ( “/RAM” , pRAM )
fd = creat (“/RAM/fichier” , 2 )
write ( fd , pData , nbr_bytes )
close ( fd )
Client VxWorks:
netDevCreate ( “Serveur:” , “IP_Serveur” , 1 )
fd = open (“Serveur:/RAM/fichier” , 2 ,0)
pData = malloc ( nbr_bytes_max )
read ( fd , pData , nbr_bytes_max )
close ( fd )
49 Système temps réel – Tornado2 Professeur : Michel MARIE
La programmation réseau
1. Les composants réseau sur VxWorks. (Modèle DoD en 4 couches)
Ethernet SLIP/PPPMémoire partagée
TCP
IP
UDP
SocketsZbuf
API
NFS
RPC
ftprshtelnetrloginTornado
tgtsvr
pilotes réseau
Les communications réseau peuvent se faire au travers d’un transceiver Ethernet, du bus VME via
une mémoire partagée, ou d’une liaison série en utilisant un protocole SLIP, CSLIP ou PPP.
zbufAPI est un buffer utilisé pour accélérer les transactions sur le réseau.
2. Les sockets.
La socket est une interface logicielle pour le protocole Internet. Elle retourne un descripteur de
fichier qui sera utilisé par la suite pour les accès au réseau.
L’attachement de la socket au réseau se fait ensuite par un port de communication spécifié par la
fonction bind().
Le port est identifié par un numéro codé sur un entier court, les conventions d’usage des numéros de
port sur VxWorks sont :
0 à 1023 : Réservés pour les services du système ( rlogin, telnet etc…)
1024 à 5000 : Ports alloués dynamiquement.
>5000 : Ports définis par l’utilisateur.
Pour la gestion des adresses IP avec les sockets, deux structures de données sont utilisées:
La structure générique :
Struct sockaddr
{
u_short sa_family;
char sa_data[14];
};
La structure utilisée pour le protocole internet.
struct sockaddr_in
50 Système temps réel – Tornado2 Professeur : Michel MARIE
{
short sin_family;
u_short sin_port;
struct in_addr.sin_addr;
char sin_zero[8];
};
L’ordre de rangement des octets d’une adresse internet ou d’un numéro de port différe entre la
machine et le protocole réseau, certaines fonctions dites “network byte ordering” effectuent les
conversions nécessaires :
htonl() : Machine vers réseau, long.
htons() : Machine vers réseau, court.
ntohl() : Réseau vers machine, long.
ntohs() : Réseau vers machine, court.
2.1. Création d’une “socket”.
int = socket ( domaine , type , protocole )
Le domaine doit être PF_INET ou AF_INET ce qui ne change rien au fonctionnement de la
socket.
Le type est :
SOCK_DGRAM pour le protocole UDP.
SOCK_STREAM pour le protocole TCP.
Le protocole est 0.
Retourne un descripteur de socket ou ERROR.
2.2. Couplage d’une “socket” à un port de communication.
STATUS = bind ( sock , pAdr , Adr_long )
Sock : Descripteur de socket retourné par socket().
pAdr : Pointeur sur une structure sockaddr spécifiant l’adresse sur laquelle la socket
doit se coupler.
Adr_long : Taille de la structure sockaddr.
3. Programmation des sockets UDP.
UDP est un protocole sans connexion ce qui implique la perte possible de paquets. Le serveur crée
une socket et attache celle-ci à un port de communication connu. Il lit ensuite la demande du client
dans une boucle infinie et répond à ses requêtes.
Envoi de données sur une socket UDP.
Int = sendto ( sock , pBuff , buff_len , flags , pDestAdr , dest_len )
sock : Descripteur de socket retourné par socket().
pBuff : Adresse du buffer de données à envoyer.
buff_len : Longueur des données en octets.
flags : MSG_OOB (sans effet en UDP, envoi du caractère urgent en TCP),
MSG_DONTROUTE (sans consultation de la table de routage).
pDestAdr : Pointeur sur une structure sockaddr contenant l’adresse de destination.
dest_len : Taille de la structure sockaddr.
Réception de données sur une socket UDP.
Int = recvfrom ( sock , pBuff , buff_len , flags , pAdr , plen )
sock : Descripteur de socket retourné par socket().
51 Système temps réel – Tornado2 Professeur : Michel MARIE
pBuff : Adresse du buffer de stockage des données reçues.
buff_len : Longueur maximale en octets des données à lire.
flags : MSG_OOB ou MSG_PEEK (lecture sans consommation).
PAdr : Pointeur sur une structure sockaddr de stockage de l’adresse client.
plen : Pointeur sur un entier contenant la taille de la structure sockaddr.
Exemple : Serveur UDP.
#define PORT (u_short) 5001;
struct sockaddr_in monAdr;
int masocket;
bzero (&monAdr , sizeof( struct sockaddr_in ) );
monAdr.sin_family = PF_INET;
monAdr.sin_port = htons(PORT);
monAdr.sin_addr.s_addr = INADDR_ANY;
masocket = socket ( PF_INET , SOCK_DGRAM , 0);
if (masocket = = ERROR) return (ERROR);
if ( bind ( masocket , (struct sockaddr *)&monAdr, sizeof ( monAdr))<0)
{
close (masocket);
return( ERROR );
}
FOREVER
{
recvfrom ( ……..)
sendto (………)
}
4. Programmation des sockets TCP.
TCP est un protocole connecté. Il est possible d’implémenter des serveurs concurrents à l’aide
d’une socket maître et de sockets esclaves. Le serveur peut ainsi servir concuremment plusieurs
clients.
Les demandes de connexion des clients sont reçues par la socket maître qui crée alors
dynamiquement une socket esclave pour chaque client.
Après avoir créé la socket maître, fonction socket() et lui avoir attaché un port de communication,
fonction bind(), le serveur TCP définit une queue des connexions en attente d’acceptation avec la
fonction listen(). Une boucle infinie se chargera ensuite d’accepter les connexions clients, fonction
accept() et de créer une tâche de service du client.
52 Système temps réel – Tornado2 Professeur : Michel MARIE
socket
maître
socket
esclave
socket
esclave
gestion des demandes
de connexion
Création dynamique d'une socket pour chaque connexion
servie par une tâche
SERVEUR TCP CONCURRENT
Création d’une queue de connexions.
STATUS = listen ( sock , queue_len )
sock : Descripteur de socket retourné par socket().
Queue_len : Longueur de la queue de connexions, 5 max.
Acceptation d’une connexion.
int = accept ( sock , pAdr , plen )
sock : Descripteur de socket retourné par socket().
pAdr : Pointeur sur une structure sockaddr de stockage de l’adresse client.
plen : Pointeur sur un entier contenant la taille de la structure sockaddr.
Retourne un descripteur de socket esclave connectée au client ou ERROR.
Emission et réception des données.
Il est possible d’utiliser :
soit les fonctions read() / write().
soit les fonctions send() / recv().
Exemple : Serveur TCP concurrent.
master_socket = socket ( PF_INET , SOCK_STREAM , 0);
if (master_socket = = ERROR) return (ERROR);
if ( bind ( master_socket , (struct sockaddr *)&monAdr, sizeof ( monAdr))<0)
{
close (master_socket);
return( ERROR );
}
if ( listen ( master_socket , queue )<0)
{
close (master_socket);
return( ERROR );
}
FOREVER
{
53 Système temps réel – Tornado2 Professeur : Michel MARIE
slave_socket = accept( master_socket , (struct sockaddr *)&Addr_client);
taskSpawn ( …. , Service_client , slave_socket , …..);
}
Service_client ( slave_socket , …..)
{
read ( slave_socket , …..);
…..
write ( slave_socket , …..);
close ( slave_socket );
}
Le client quant à lui doit créer une socket puis effectuer une demande de connexion au serveur à
l’aide de la fonction connect().
Demande de connexion client.
STATUS = connect ( sock , pAdr , plen )
STATUS = connectWithTimeout ( sock , pAdr , plen, pTimeval )
sock : Descripteur de socket retourné par socket().
pAdr : Pointeur sur une structure sockaddr contenant l’adresse du serveur.
plen : Pointeur sur un entier contenant la taille de la structure sockaddr.
pTimeval : Pointeur sur une structure timeval fixant le time-out de connexion.
Exemple : Client TCP.
masocket = socket ( PF_INET , SOCK_STREAM , 0);
if (masocket = = ERROR) return (ERROR);
if ( connect ( masocket , (struct sockaddr *)&Adr_serveur, sizeof ( Adr_serveur))= =0)
{
close (masocket);
return( ERROR );
}
write (masocket , ……);
read ( masocket , …….);
close ( masocket );
Fermeture d’une connexion.
La fermeture par close() est immédiate et ferme la connexion en émission et en réception.
La fermeture par shutdown() permet de paramètrer les conditions de fermeture.
STATUS = shutdown ( sock , comment )
sock : Descripteur de socket retourné par socket().
comment : 0 = Fermeture de la réception
1 = Fermeture de l’émission
2 = Fermeture de l’émission et de la réception.
5. Paramétrage des sockets.
Il est possible d'accéder aux paramètres des sockets par l'intermédiaire de deux primitives :
int getsockopt( sock, level, option, pArg, taille)
int setsockopt( sock, level, option, pArg, taille)
54 Système temps réel – Tornado2 Professeur : Michel MARIE
Le paramètre LEVEL fixe le niveau d’application de l’option, pour les sockets il doit être égal à
SOL_SOCKET.
Le paramètre option définit le type d'information que l'on veut extraire, par exemple :
SO_TYPE : extraction du type de la socket,
SO_SNDBUF : fixe la taille du tampon d'émission,
SO_RCVBUF : fixe la taille du tampon de réception,
SO_SNDTIMEO : fixe le time-out d'émission,
SO_RCVTIMEO : fixe le time-out de réception,
Exemple de fonctions pour initialiser et afficher une structure sockaddr :
#include "vxWorks.h"
#include "fioLib.h"
#include "hostLib.h"
#include "inetLib.h"
#include "netinet/in.h"
#include "stdio.h"
#include "string.h"
#include "sys/socket.h"
#include "sys/types.h"
STATUS inetAddrInit(struct sockaddr_in* pAddr, char* pHost,
int port)
{
u_long inet;
bzero ((char *) pAddr, sizeof (struct sockaddr_in));
if ((inet = hostGetByName (pHost)) == ERROR)
{
if ((inet = inet_addr (pHost)) == ERROR) return (ERROR);
}
pAddr->sin_family = AF_INET;
pAddr->sin_port = htons (port);
pAddr->sin_addr.s_addr = inet;
return (OK);
}
void sockAddrShow (struct sockaddr_in * pSockAddr)
{
printf("sin_family = %d\n", pSockAddr->sin_family);
printf("sin_addr = %s\n",
inet_ntoa(pSockAddr- >sin_addr.s_addr)
printf("sin_port = %d\n", ntohs (pSockAddr->sin_port));
}
Exemple de serveur UDP-IP :
/* Serveur UDP
Affiche l'adresse IP, le numéro de port et le contenu du message.
Retourne un accusé de réception */
#include "vxWorks.h"
#include "sockLib.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "inetLib.h"
#include "ioLib.h"
55 Système temps réel – Tornado2 Professeur : Michel MARIE
#include "string.h"
#include "stdio.h"
#include "taskLib.h"
char buff[100];
typedef int SOCK_FD;
LOCAL void error (char * str);
void vxServeur (u_short port)
{
int clientAddrLength;
SOCK_FD sockFd;
struct sockaddr_in clientAddr;
struct sockaddr_in srvAddr;
char inetAddr[INET_ADDR_LEN];
u_short clientPort;
char *reply = "Accuse de reception du serveur\n";
routeAdd(10.0.2.0,10.0.1.200) ; clientAddrLength = sizeof (clientAddr);
/* Creation de la socket */
if ( (sockFd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
error ("Probleme Socket");
bzero ((char *)&srvAddr, sizeof(srvAddr));
srvAddr.sin_family = AF_INET;
srvAddr.sin_port = htons(port);
srvAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockFd,(struct sockaddr*)&srvAddr,sizeof(srvAddr))<0)
{
close (sockFd);
error ("Probleme Bind");
}
FOREVER
{
if (recvfrom (sockFd, buff, 100, 0,(struct sockaddr*)
&clientAddr, &clientAddrLength) < 0)
{
close (sockFd);
error ("Probleme Recvfrom");
}
inet_ntoa_b (clientAddr.sin_addr, inetAddr);
clientPort = ntohl (clientAddr.sin_port);
printf("Message recu du client(port=%d, inet= %s):\n",
clientPort, inetAddr);
printf (buff);
if (sendto (sockFd, reply, strlen(reply) + 1, 0,
(struct sockaddr *) &clientAddr, clientAddrLength) < 0)
{
close (sockFd);
error ("Probleme sendto");
}
}
}
void error (char * str)
56 Système temps réel – Tornado2 Professeur : Michel MARIE
{
perror (str);
exit (1);
}
Exemple de serveur TCP-IP :
#include "vxWorks.h"
#include "sockLib.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "inetLib.h"
#include "ioLib.h"
#include "string.h"
#include "stdio.h"
#include "taskLib.h"
#include "routeLib.h"
#define MAX_MSG_SIZE 80
#define LOCAL static
typedef int SOCK_FD;
void doRequest ();
LOCAL void error ();
void vxServeur (u_long port)
{
int clientAddrLength;
SOCK_FD sockFd;
SOCK_FD newSockFd;
struct sockaddr_in clientAddr;
struct sockaddr_in srvAddr;
clientAddrLength = sizeof (clientAddr);
routeAdd("10.0.2.0","10.0.1.200");
if ( (sockFd = socket (PF_INET, SOCK_STREAM,0)) < 0 )
error ("Probleme socket");
bzero ((char *)&srvAddr, sizeof (srvAddr));
srvAddr.sin_family= AF_INET;
srvAddr.sin_port= htons (port);
srvAddr.sin_addr.s_addr = INADDR_ANY;
if(bind(sockFd,(struct sockaddr*)&srvAddr, sizeof(srvAddr))<0)
{
close (sockFd);
error ("probleme Bind");
}
if (listen (sockFd, 1) < 0)
{
close (sockFd);
error ("probleme Listen");
}
FOREVER
{
newSockFd= accept (sockFd,(struct sockaddr *) &clientAddr,
57 Système temps réel – Tornado2 Professeur : Michel MARIE
&clientAddrLength);
if (newSockFd < 0)
{
close (sockFd);
error ("Probleme Accept");
}
doRequest (newSockFd, &clientAddr);
printf ("Fermeture de la connexion par le client\n\n")
close (sockFd);
exit (0);
}
}
void doRequest(SOCK_FD sock,struct sockaddr_in* pClientAddr)
{
char* reply="Accuse de reception du serveur\n";
char buf [MAX_MSG_SIZE];
int msgSize;
char * pClientInet;
u_short clientPort;
pClientInet = inet_ntoa(pClientAddr->sin_addr);
clientPort = ntohs(pClientAddr->sin_port);
printf("Client connecte d'adresse IP %s, et de port %d\n",
pClientInet, clientPort );
FOREVER
{
msgSize = read (sock, buf, MAX_MSG_SIZE - 1);
if (msgSize < 0)
{
close (sock);
error ("Probleme Read");
}
else if (msgSize == 0)
{
close (sock);
break;
}
else
{
printf("Message de %d octets: \n%s\n", msgSize, buf);
if (write (sock, reply, strlen(reply)) < 0)
{
close (sock);
error ("Probleme Write");
}
}
}
}
LOCAL void error (pStr)
char * pStr;
{
perror (pStr);
exit (1);
}
58 Système temps réel – Tornado2 Professeur : Michel MARIE
Reconfiguration de VxWorks
Une fois la mise au point d’une application achevée, tout n’est pas fini pour autant ! En effet, un
certain nombre d’actions restent à effectuer :
Exclure les fonctionnalités du noyau inutiles à notre application afin « d’alléger » celui-ci.
Lier le code de l’application avec VxWorks.
Modifier le code de lancement de VxWorks pour démarrer sur la tâche initiale de l’application.
Ensuite l’application peut-être :
Soit téléchargée sur la cible par le réseau.
Soit « ROMEE » sur la cible.
Soit chargée à partir d’un disque local à la cible.
Certaines fonctionnalités sont interdépendantes, par exemple NFS nécessite RPC. Un programme
utilitaire c:tornado\wind\target\src\config\usrDepend.c recherche ces dépendances et inclus les
fonctionnalités que vous auriez pu éventuellement oublier ; mais je n’ose y croire !
Remarque : Pour connaître l’ensemble des fonctionnalités d’un noyau il est possible d’utiliser la
commande nm ou d’utiliser le browser pour connaître les symboles du fichier objet.
nm68k VxWorks | findstr \.o
1. Modifier le code de démarrage.
Pour indiquer la fonction à exécuter par activation d’une tâche de démarrage il faut :
Ajouter la définition INCLUDE_USER_APPL et la macro USER_APPL_INIT dans le fichier
config.h de votre BSP.
Ecrire le code de la précédente macro en spécifiant les paramètres de la tâche à activer et la fonction associée.
Exemple :
#define INCLUDE_USER_APPL
#define USER_APPL_INIT \
{ \
extern void monApplication(void) ; \
taskSpawn ( tmonApps , 60 , 0 , 3000 , (FUNCPTR)monApplication , \
0,0,0,0,0,0,0,0,0,0 ) ; \
}
2. Lier l’application avec VxWorks.
Pour cela il faut modifier le code du fichier « makefile » situé dans le répertoire du BSP comme
suit :
59 Système temps réel – Tornado2 Professeur : Michel MARIE
Tous les modules à ajouter doivent être spécifiés par une ligne commande avec
ADDED_MODULES afin d’être ajoutés à la liste des dépendances du noyau VxWorks et liés avec
celui-ci.
Les règles de fabrication du fichier objet à partir du fichier source doivent être précisées, ainsi que
l’emplacement du fichier source.
Exemple :
ADDED_MODULES = monApplication.o
|
|
|
monApplication.o : c:\tornado\Application\monApplication.c
@ $(RM) $@
$(CC) –c $(CFLAGS) c:\tornado\Application\monApplication.c
Il ne reste plus qu’à reconstruire l’image du noyau VxWorks avec la commande :
ProjectMake BAB40VxWorks TargetsVxWorks.
La commande VxWorks.st construit l’image du noyau avec le Shell résidant ainsi que la table
des symboles.
La commande VxWorks_rom.hex construit l’image du noyau ROMmable avec recopie en RAM au démarrage.
La commande VxWorks.res_rom.hex construit l’image du noyau ROMmable en résidant.
La commande VxWorks.st_rom.hex construit l’image du noyau avec le Shell résident ainsi que la table des symboles, ROMmable en résidant.
La commande VxWorks.res_rom_nosym.hex construit l’image du noyau sans table des
symboles, ROMmable en résidant.