Noyau RTLinux Noyau RTLinux 1
Principes de programmation RTLinux est implémenté sur la base du Minimal Real-time System Profile (POSIX 1003.13 draft 9) ou «système d'exploitation temps réel minimal», et sa structure de fonctionnement interne respecte les spécifications POSIX. Par conséquent, les fonctions POSIX de base sont utilisables, mais la plupart sont réécrites pour RTLinux. l'api n'est pas celle de POSIX, mais bien une API particulière RTLinux. Fonctions init et cleanup int init_module( ) { } void cleanup_module( ) { } 2
Les threads #include <pthread.h> int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void*), void *arg) ; Gestion de la priorité #include <rtl_sched.h> int pthread_setschedparam(pthread_t *thread, int policy, const struct sched_param *param) ; 3
Périodicité #include <rtl_sched.h> pthread_make_periodic_np(pthread_t *thread, hrtime_t start_time, hrtime_t period) ; Destruction pthread_cancel(pthread_t thread) ; pthread_join() ; 4
Gestion du temps RTLinux fourni plusieurs horloges qui peuvent être utilisées pour des opérations à limitation temporelle (timer), ou pour obtenir des temps de réponse #include <rtl_time.h> int clock_gettime(clockid_t clock_id, struct timespec *ts) ; hrtime_t clock_gethrtime(clockid_t clock) ; struct timespec { time_t tv_sec ; //temps en secondes. long tv_nsec ; //temps en nanosecondes. }; Mécanismes de synchronisation Le premier est bien sûr l'exclusion mutuelle (mutex) puis les sémaphores qui sont implémentés suivant la norme POSIX: pthread_mutex_init( ), pthread_mutex_lock( ), pthread_mutex_unlock( )... sem_init( ), sem_destroy( ), sem_wait( )... Sur RTLinux, il existe aussi les barrières pour synchroniser des threads. Cet outil permet de réaliser simplement des opérations de rendez-vous. Leur utilisation est proche de celle des sémaphores : 5
Mécanismes de synchronisation Initialisation : pthread_barrierattr_init(*pthread_barrierattr_t barrier_attr) ; Création : pthread_barrier_init (*pthread_barrier_t barrier, *pthread_barrierattr_t barrier_attr, int nbthread); Le troisième argument indique le nombre de thread et/ou processus qui vont butter sur cette barrière. Attente jusqu'à ce que tous les thread concernés aient atteint leur barrière: pthread_barrier_wait (*pthread_barrier_t barrier); Destruction : pthread_barrier_destroy(*pthread_barrier_t barrier); Communications avec l espace utilisateur Le moyen le plus simple pour envoyer des informations à l utilisateur est l impression dans le kernel ring buffer, à l aide de la fonction suivante : void rtl_printf(string) ; 6
Communications avec l espace utilisateur Rappelons que les modules RTLinux ne peuvent pas utiliser l'allocation de mémoire dynamique (fonctions malloc et free) et ne peuvent donc faire du stockage de masse sur le disque dur. Il faut donc envoyer les données à enregistrer à la partie non temps réel du système. Pour cela il existe plusieurs méthodes : Le principal moyen de transmettre des données depuis les tâches temps réel vers les processus utilisateurs est basé sur des fifos, vus et utilisés avec les fonctions classiques de gestion des fichiers (open, close, read...) du côté utilisateur, et avec des fonctions fournies dans une bibliothèque spécifique du coté RTLinux. Ces fonctions (rtf_create, rtf_destroy, rtf_put...) sont spécifiques à RTLinux. Création d'un fifo int rtf_create(unsigned int fifo, int size) ; 7
Lecture fifo int rtf_get(unsigned int fifo, char * buf, int count) ; Ecriture fifo int rtf_put(unsigned int fifo, char * buf, int count) ; 8
Destruction fifo rtf_destroy(unsigned int fifo) ; Routine sur I/O rtf_create_handler(unsigned int fifo, int (* handler)()); 9
Interruptions #include <rtl_sync.h> rtl_no_interrupts(rtl_irqstate_t state) ; Sauvegarde les indicateurs d'interruption puis désactive ces dernières. Dans tout système temps réel, les interruptions ont une grande importance. Les fonctions de gestion sont spécifiques à RTLinux : #include <rtl_sync.h> rtl_no_interrupts(rtl_irqstate_t state) ; Sauvegarde les indicateurs d'interruption puis désactive ces dernières. rtl_restore_interrupts(rtl_irqstate_t state) ; Rétabli les interruptions avec les indicateurs sauvés précédement. rtl_stop_interrupts() ; Désactive les interruptions. rtl_allow_interrupts() ; Active les interruptions. #include <rtl_core.h> int rtl_request_irq (unsigned int irq, unsigned int (*handler)(unsigned int irq, struct pt_regs *regs)); Définit une routine de traitement des interruptions : irq est l'id de l'interruption et *handler est l'adresse de la routine qui sera exécutée à chaque interruption. Il ne faut jamais appeler la fonction rtl_allow_interrupt pour réactiver les interruptions en fin de routine, mais rtl_hard_enable_irq (voir plus bas). Interruptions int rtl_free_irq(unsigned int irq) ; Libère l'affectation de la routine à l'interruption irq. Pour activer et désactiver une interruption irq précise : int rtl_hard_enable_irq(unsigned int irq) ; int rtl_hard_disable_irq(unsigned int irq) ; 10
Exemple Interruptions sur le port parallèle d un PC : demande it Status hardware Le module a pour but de répondre à une interruption sur la broche 10 du port parallèle en mettant à 1 les broches 2 et 3 de ce même port. De plus, il compte les interruptions qu'il reçoit et un thread périodique envoie le nombre d'interruptions dans un fifo toutes les 5 secondes. Exemple Ces deux actions permettent de connaître la latence sur les interruptions et le temps de traitement de ces dernières en observant la broche 2 du port parallèle, mais aussi de pouvoir comparer le nombre d'impulsions envoyées au nombre d'interruptions effectivement réalisées par le système. 11
Init module int init_module(void) { int status; rtl_irqstate_t f; /* défini une structure qui stocke les états de l'irq */ /******** initialisation des interuptions: *************************** } rtl_no_interrupts(f); /*sauve les flags de l'état actuel et désactive les interuptions.*/ status=rtl_request_irq(7, intr_handler); /* vérifie que la routine d'interruption est prête...*/ if(status<0) { rtl_printf("echec de la mise ne place de l'interuption: %d\n",status); return -1; } outb_p(inb_p(0x37a) 0x10, 0x37A); /* active la broche 10 du port parallele */ rtl_hard_enable_irq(7); /* enable IRQ 7 */ rtl_restore_interrupts(f);/* réactive les interruptions avec les flags sauvegardé avec rtl_no_interupts */ return -1; } ISR /*********** Fonction activee lors de chaque IT: **********************/ unsigned int intr_handler(unsigned int irq, struct pt_regs *regs) { /* les deux fonctions outb genere un creneau sur les broches 2 et 3 du port parallele. Doit etre desactive pour des IT de frequence superieure a 75KHz */ outb(3, 0x378); outb(0, 0x378); rtl_hard_enable_irq(7); /* reactive l'it */ return 0; } 12
Clean up void cleanup_module(void) { rtl_free_irq(7); /* desactivation des IT. */ } Sortie temps de réponse : 8 μs temps de traitement : 4 μs 13