1 / 63

Programmazione RTAI

E.Mumolo, DEEI mumolo@units.it. Programmazione RTAI . Programmazione in RTAI. Approccio RTAI: Uno schedulatore Real Time rimpiazza lo schedulatore di Linux Intercetta gli interrupt (time e dispositivi) Esegue il codice di servizio degli interrupt in tempo reale

joie
Télécharger la présentation

Programmazione RTAI

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. E.Mumolo, DEEI mumolo@units.it Programmazione RTAI

  2. Programmazione in RTAI Approccio RTAI: Uno schedulatore Real Time rimpiazza lo schedulatore di Linux Intercetta gli interrupt (time e dispositivi) Esegue il codice di servizio degli interrupt in tempo reale Esegue Linux nel tempo rimanente (background) Task in tempo reale: moduli di kernel non codice Linux  ogni errore provoca un crash del kernel  non hanno accesso a funzioni di I/O (terminale, disco…)  necessità di una comunicazione kernel/utente per I/O I MODULI DEL KERNEL SONO CARICATI DINAMICAMENTE!! insmod rmmod

  3. Programmazione in RTAI Codifica in C Ogni modulo del kernel ha un’entry point (init_module) e un exit point (cleanup_module) In definitiva: la struttura utilizza 3 parti principali scritte dall’utente Funzione che inizializza il sistema, definisce le caratteristiche dei vari task e IPC Definizione della funzione real time Funzione che rilascia le risorse

  4. Programmazione in RTAI Esempio della funzione di rilascio risorse int cleanup_module(void) { //ferma il timer stop_rt_timer(); rt_busy_sleep(10000000); //chiude la fifo rtf_destroy(0); //cancella la struttura rt rt_task_delete(&hiprio_task); }

  5. Programmazione in RTAI Primo esempio: #include <linux/kernel.h> #include <linux/module.h> MODULE_LICENSE("GPL"); int init_module(void) //entry point { printk("Hello world!\n"); // printk = scrive in /var/log/messages return 0; } void cleanup_module(void) //exit point { printk("Goodbye world!\n"); return; }

  6. Programmazione in RTAI Compilazione sorgenti: usare il Makefile del kernel make –f Makefile –C <kernel path> M=$PWD Genera il file <modulo.ko> Nel nostro caso: EXTRA_CFLAGS += -I/usr/realtime/include -D_IN_RTAI_ obj-m += prog1.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean Per eseguire il modulo: insmod <modulo.ko> Per terminare : rmmod <modulo.ko>

  7. Programmazione in RTAI Attenzione (1): printk scrive nel ‘kernel ring buffer Il buffer viene scritto periodicamente sul file di log /var/log/messages Attenzione (2): printk può non essere predicibile: rt_printk versione RT Scrive sul kernel Ring buffer. Per leggere/cancellare il buffer: chiamate di sistema: syslog, klogctl - read and/or clear kernel message ring buffer; set console_loglevel comandi Linux $tkadmin dump ringbuffer ringbuffer.out In definitiva:dmesg –c|insmod hello.ko|dmesg|rmmod hello.ko|dmesg Oppure: tail -f /var/log/messages

  8. Programmazione in RTAI Definizione di un task (thread in tempo reale): int rt_task_init(RT_TASK *task, void(*rt_thread)(int), int data, int stack_size, int priority, int uso_fpu, void(*signal)(void)); Attenzione: definisce il task, non l’esegue! Il task si trova nello stato SUSPENDED Argomenti della funzione: Primo argomento: descrittore del task Secondo argomento: entry point della funzione Terzo argomento: un intero passato dal genitore al thread Quarto argomento: dimensione dello stack Quinto argomento: priorità . Da rtai_sched.h: #define RT_SCHED_HIGHEST_PRIORITY 0 #define RT_SCHED_LOWEST_PRIORITY 0x3fffFfff #define RT_SCHED_LINUX_PRIORITY 0x7fffFfff Sesto argomento: flag per l’uso della fpu Settimo argomento: funzione per gestire il segnale inviato ogni volta che il thread diventa running

  9. Programmazione in RTAI Schedulazione in RTAI: periodica – aperiodica (one-shot) Modalità di funzionamento: void rt_set_oneshot_mode(void);//imposta lo schedulatore I task possono essere eseguiti in istanti qualsiasi void rt_set_periodic_mode(void); );//imposta lo schedulatore Ogni richiesta non multiplo del periodo viene soddisfatta nel periodo di base del timer più vicino. È il default. La schedulazione è associata al timer: RTIME start_rt_timer(int period);//se aperiodic il periodo è ignorato RTIME rt_get_time();//ticks se periodico, TSC se aperiodico void stop_rt_timer(void); Esempio: rt_set_oneshot_mode(); start_rt_timer(1); RTIME Internal count units; // misura il tempo trascorso in ticks Tempi: 1 tick=838 ns (timer del PC) Inoltre: Time Stamp Clock (TSC): clock del PC

  10. Programmazione in RTAI Attivazione timer: periodico void rt_set_periodic_mode(void); RTIME start_rt_timer(RTIME period); one-shot void rt_set_oneshot_mode(void); RTIME start_rt_timer(RTIME period); La funzione RTIME nano2counts(int nanoseconds); converte da ns a ticks

  11. Programmazione in RTAI Esecuzione di un task in tempo reale: due modalità Rende un processo RTAI periodico ed avvia la prima esecuzione all’istante <start_time>: int rt_task_make_periodic(RT_TASK *task, RTIME start_time, RTIME period); Task aperiodico (one shot): esecuzione immediata int rt_task_resume(RT_TASK *task); esecuzione ad un istante assoluto start_time int rt_task_make_periodic(RT_TASK *task, RTIME start_time, RTIME period); //period non usato esecuzione ad un istante relativo start_delay int rt_task_make_periodic_relative_ns (RT_TASK *task, RTIME start_delay, RTIME period);

  12. Programmazione in RTAI In definitiva per creare un task aperiodico: rt_set_oneshot_mode(); start_rt_timer(1); retval =rt_task_init(&task, function, 0, 1024, RT_SCHED_LOWEST_PRIORITY, 0, 0); retval = rt_task_resume(&task); Gestione della schedulazione: Nel caso di task periodico int rt_task_wait_period(void); sospende l’esecuzione del thread corrente fino al prossimo periodo Nel caso di task aperiodico int rt_task_yield(void); int rt_task_suspend(RT_TASK *task); task_yield Fa assumere al processo chiamante lo stato READY task_suspend sospende l’esecuzione, che verrà ripresa con resume o con make_periodic Programma d’esempio: crea un task aperiodico

  13. #include <linux/kernel.h> /* dichiarazioni richieste dai moduli del kernel */ #include <linux/module.h> /* dichiarazioni richieste dai moduli del kernel */ #include <linux/version.h> #include <linux/errno.h> /* EINVAL, ENOMEM */ #include "rtai.h" #include "rtai_sched.h" #include <rtai_sem.h> MODULE_LICENSE("GPL"); static RT_TASK print_task; void print_function(long arg) { rt_printk("Hello world!\n"); return; } int init_module(void) { int retval; rt_set_oneshot_mode(); start_rt_timer(1); //parte l’esecuzione retval = /* crea il tread real time */ rt_task_init(&print_task, print_function, 0, 1024, RT_SCHED_LOWEST_PRIORITY, 0, 0); if ( retval != 0) { if (-EINVAL == retval) { printk("task: task structure is invalid\n"); } else {printk("task: error starting task\n");} return retval; } retval = rt_task_resume(&print_task); /* punta alla nostra struttura */ if (0 != retval) { if (-EINVAL == retval) {printk("struttura task invalida\n");} else { printk("task: error starting task\n"); } return retval; } return 0; } void cleanup_module(void) { return; }

  14. Programmazione in RTAI Funzioni di utilità per la schedulazione: void rt_sleep(RTIME delay); void rt_sleep_until(RTIME time); sospendono il thread in esecuzione e lo mettono in stato DELAYED void rt_busy_sleep(int nanosecs); addormenta in thread mandando in loop la CPU per il tempo indicato void rt_sched_lock(void); void rt_sched_unlock(void); blocca/sblocca lo schedulatore pe evitare corse critiche int rt_get_prio(RT_TASK *task); int rt_change_prio(RT_TASK *task, int priority; determina/setta la priorità di base int rt_get_inher_prio(RT_TASK *task); Determina la priorità ereditata a causa dell’accesso a risorse condivise (protocolli priority inheritance)

  15. Programmazione in RTAI Altre funzioni di utilità per la schedulazione: int rt_get_task_state(RT_TASK *task); RT_TASK *rt_whoami(void); int rt_task_use_fpu(RT_TASK *task, int use_fpu_flag); int rt_task_delete(RT_TASK *task); rimuove is task dal sistema

  16. Programmazione in RTAI: Schedulazione periodica Non c’è particolare limite al numero di task I task sono thread: condividono lo spazio di indirizzamento!! Attenzione alla mutua esclusione Dobbiamo determinare il periodo di base. Il periodo dei thread è un multiplo del periodo di base Programma d’esempio: setto il timer a 1 ms definisco la funzione schedulazione

  17. #include <linux/kernel.h> #include <linux/module.h> #include <linux/version.h> #include <linux/sched.h> #include <linux/errno.h> #include <asm/io.h> #include "rtai.h" #include "rtai_sched.h" MODULE_LICENSE("GPL"); static RT_TASK sound_task; static RT_TASK delay_task; static RTIME sound_period_ns = 1e5; /* in nanoseconds, -> 10 kHz */ static RTIME delay_period_ns = 1e9; /* in nanoseconds, -> 1 Hz */ #define SOUND_PORT 0x61 /* indrizzo altoparlante */ #define SOUND_MASK 0x02 /* bit da cambiare */ static int delay_count = 2; // condivisa tra i due threa: onda quadra per altoparlante int init_module(void) { RTIME sound_period_count, delay_period_count, timer_period_count; int retval; rt_set_periodic_mode(); sound_period_count = nano2count(sound_period_ns); delay_period_count = nano2count(delay_period_ns); timer_period_count = start_rt_timer(sound_period_count); printk("sound task: requested %d counts, got %d counts\n",(int) sound_period_count, (int) timer_period_count); retval = //struttura del thread ‘sound’ rt_task_init(&sound_task, sound_function, 0, 1024, RT_LOWEST_PRIORITY - 1, 0, 0); if (0 != retval) { if (-EINVAL == retval) {printk("sound task: task structure already in use\n");} else if (-ENOMEM == retval) {printk("sound task: can't allocate stack\n"); } else {printk("sound task: error initializing task structure\n"); } return retval; }

  18. retval = //struttura del thread ‘delay’ rt_task_init(&delay_task, delay_function, 0, 1024, RT_LOWEST_PRIORITY, 0, 0); if (0 != retval) { if (-EINVAL == retval) {printk(“errore: gia’ in uso\n"); } else if (-ENOMEM == retval) {printk(“errore di stack\n"); } else {printk(“error di inizializzazione\n"); } return retval; } retval = //esegue il thread ‘sund’ rt_task_make_periodic(&sound_task, rt_get_time() + sound_period_count, sound_period_count); if (0 != retval) { if (-EINVAL == retval) {printk("sound errore\n"); } else {printk("sound errore task\n"); } return retval; } retval = //esegue il thread ‘delay’ rt_task_make_periodic(&delay_task, rt_get_time() + delay_period_count, delay_period_count); if (0 != retval) { if (-EINVAL == retval) {printk(“errore delay \n"); } else {printk("delay task: error starting task\n"); } return retval; } return 0; /* success! */ }

  19. void sound_function(int arg) { int delay_left = delay_count; /* decrementato ad ogni ciclo */ unsigned char sound_byte, toggle = 0; while (1) { if (delay_left > 0) { //ritardo restante? delay_left--; } else { sound_byte = inb(SOUND_PORT); if (toggle) { sound_byte = sound_byte | SOUND_MASK; } else { sound_byte = sound_byte & ~SOUND_MASK; } outb(sound_byte, SOUND_PORT); toggle = ! toggle; delay_left = delay_count; /* ricarico il ritardo*/ } rt_task_wait_period(); } /* non arriviamo mai qui */ return; } void delay_function(int arg) { while (1) { delay_count++; rt_task_wait_period(); } /* non arriviamo mai qui */ return; }

  20. void cleanup_module(void) { int retval; retval = rt_task_delete(&sound_task); if (0 != retval) { if (-EINVAL == retval) { /* invalid task structure */ printk("sound task: task structure is invalid\n"); } else { printk("sound task: error stopping task\n"); } } retval = rt_task_delete(&delay_task); if (0 != retval) { if (-EINVAL == retval) { /* invalid task structure */ printk("delay task: task structure is invalid\n"); } else { printk("delay task: error stopping task\n"); } } outb(inb(SOUND_PORT) & ~SOUND_MASK, SOUND_PORT); //toggle il bit return; }

  21. Programmazione in RTAI Politiche di schedulazione RTAI offre la possibilità di usare le seguenti politiche: FIFO (default), Round Robin. Abilitazione delle politiche: void rt_set_sched_policy(struct rt_task_struct *task, int policy, int rr_quantum_ns); Politiche: RT_SCHED_RR - RT_SCHED_FIFO Esempio: 3 task: appena creati sono bloccati da un semaforo che viene aperto appena possono continuare. Ogni task esegue per EXECTIME unità temporali, realizzato come segue:starttime = rt_get_cpu_time_ns();while(rt_get_cpu_time_ns() < (starttime + EXECTIME)); Vene contato il numero di context switch mediante un semaforo

  22. Programmazione in RTAI In definitiva, la sched. FIFO si realizza con queste istruzioni: … void func1(); void func2(); int init_module() { rt_set_periodic_mode(); rt_task_init(&t1,func1,…); rt_task_init(&t2,func2,…); rt_task_make_periodic(&t1,start1,periodo1); rt_task_make_periodic(&t2,start1,periodo2); … } NB: FIFO può essere facilmente anche RM

  23. #include <linux/kernel.h>#include <linux/kernel.h>#include <linux/module.h>#include <rtai.h>#include <rtai_sched.h>#include <rtai_sem.h>MODULE_LICENSE("GPL");#define STACK_SIZE 2000#define EXECTIME 400000000#define RR_QUANTUM 10000000#define NTASKS 3#define PRIORITY 100static SEM sync, RT_TASK tasks[NTASKS], int switchesCount[NTASKS];static void fun(long indx) //funzione eseguita dai task { RTIME starttime, endtime;; rt_printk("Resume task #%d (%p) on CPU %d.\n", indx, &tasks[indx], hard_cpu_id()); rt_sem_wait(&sync); rt_printk("Task #%d (%p) inizia on CPU %d.\n", indx, &tasks[indx], hard_cpu_id()); starttime = rt_get_cpu_time_ns(); //esegue per EXECTIME while(rt_get_cpu_time_ns() < (starttime + EXECTIME)); endtime = rt_get_cpu_time_ns()-starttime; //segnala il nr di context switch tasks[indx].signal = 0; rt_printk("Task #%d (%p) terminates after %d.\n", indx, &tasks[indx],endtime);}static void signal(void) //esegue quando c’e’ un context switch{ RT_TASK *task; int i; for (i = 0; i < NTASKS; i++) { if ((task = rt_whoami()) == &tasks[i]) { switchesCount[i]++; rt_printk("Switch al task #%d (%p) on CPU %d.\n", i, task, hard_cpu_id()); break; } }}

  24. int init_module(void){ int i; printk("INSMOD on CPU %d.\n", hard_cpu_id()); rt_sem_init(&sync, 0); rt_set_oneshot_mode(); start_rt_timer(1); for (i = 0; i < NTASKS; i++) { rt_task_init(&tasks[i], fun, i, STACK_SIZE, PRIORITY, 0, signal); } for (i = 0; i < NTASKS; i++) { rt_task_resume(&tasks[i]); } rt_sem_broadcast(&sync); return 0;}void cleanup_module(void){ int i; stop_rt_timer(); rt_sem_delete(&sync); for (i = 0; i < NTASKS; i++) { printk("nr di context switches task # %d -> %d\n", i, switchesCount[i]); rt_task_delete(&tasks[i]); }}

  25. Programmazione in RTAI Schedulazione prioritaria: I thread hanno una priorità tra 0 (RT_SCHED_HIGHEST_PRIORITY) e 1,073,741,823 (RT_SCHED_LOWEST_PRIORITY ) Quando si crea un task con rt_task_init(..)  uno degli argomenti è la priorità Dopo che viene eseguito un task può variare priorità con int rt_change_prio( RT_TASK *task, intpriority) ; La politica prioritaria è RT_SCHED_FIFO Esempio: creazione di 3 task a diverse priorità. Provae a cambiare le priorità

  26. #include <linux/kernel.h> #include <linux/module.h> #include <rtai.h> #include <rtai_sched.h> #include <rtai_sem.h> MODULE_LICENSE("GPL"); #define STACK_SIZE 2000 #define EXECTIME 400000000 #define RR_QUANTUM 10000000 #define NTASKS 3 #define HIGH 100 #define MID 101 #define LOW 102 static SEM sync, RT_TASK tasks[NTASKS], int switchesCount[NTASKS]; static void fun(long indx) { RTIME starttime, endtime; rt_printk("Resume task #%d (%p) on CPU %d.\n", indx, &tasks[indx], hard_cpu_id()); rt_sem_wait(&sync); rt_printk("Task #%d (%p) inizia su CPU %d.\n", indx, &tasks[indx], hard_cpu_id()); starttime = rt_get_cpu_time_ns(); //esegue per EXECTIME while(rt_get_cpu_time_ns() < (starttime + EXECTIME)); endtime = rt_get_cpu_time_ns()-starttime; tasks[indx].signal = 0; rt_printk("Task #%d (%p) terminates after %d.\n", indx, &tasks[indx],endtime); }

  27. static void signal(void) //per segnalare il nr di context switches { RT_TASK *task; int i; for (i = 0; i < NTASKS; i++) { if ((task = rt_whoami()) == &tasks[i]) { switchesCount[i]++; rt_printk(“passa a #%d (%p) su %d.\n", i, task, hard_cpu_id()); break; } } } int init_module(void) { int i; printk("INSMOD on CPU %d.\n", hard_cpu_id()); rt_sem_init(&sync, 0); rt_set_oneshot_mode(); start_rt_timer(1); rt_task_init(&tasks[0], fun, 0, STACK_SIZE, LOW, 0, signal); rt_task_init(&tasks[1], fun, 1, STACK_SIZE, MID, 0, signal); rt_task_init(&tasks[2], fun, 2, STACK_SIZE, HIGH, 0, signal); for (i = 0; i < NTASKS; i++) { rt_task_resume(&tasks[i]); while(!(rt_get_task_state(&tasks[i]) & RT_SCHED_SEMAPHORE)); } rt_sem_broadcast(&sync); return 0; } void cleanup_module(void) { int i; stop_rt_timer(); rt_sem_delete(&sync); for (i = 0; i < NTASKS; i++) { printk("number of context switches task # %d -> %d\n", i, switchesCount[i]); rt_task_delete(&tasks[i]); } }

  28. Programmazione in RTAI Schedulazione RateMonotonic (RM)

  29. #include <linux/kernel.h> /* decls needed for kernel modules */ #include <linux/module.h> /* decls needed for kernel modules */ #include <linux/version.h> /* LINUX_VERSION_CODE, KERNEL_VERSION() */ #include <linux/errno.h> /* EINVAL, ENOMEM */ #include "rtai.h" #include "rtai_sched.h" #include <rtai_sem.h> MODULE_LICENSE("GPL"); #define NTASKS 3 #define STACK_SIZE 10000 static RT_TASK tasks[NTASKS]; static int task_arg[NTASKS]; #define BASE_PERIOD_NS 1000000 static RTIME base_period; // in internal units static RTIME base_period_us; // in microseconds static RTIME base_period_ns; // in nanoseconds static int task_computation_time[NTASKS] = { 30 , 20 , 20 }; //timer periodici static int task_period[NTASKS] = { 60 , 90 , 110 } ; static int task_priority[NTASKS] = { 10 , 20 , 30 } ; static int ggd=1980; // 6*9*11/3 RTIME resumetime[NTASKS], deadlinetime[NTASKS]; int nodeadlinemiss=1; static SEM sync; static RTIME tasks_starttime=0; static int switchesCount[NTASKS]; #define CALIBRATION_PERCENTAGE 100 //parametro per l’attesa attiva #define CALIBRATION_LOOP_SIZE 1e7 //più alto (non troppo) più accurato static RT_TASK init_task_str; static RTIME count_need_for_one_ms_busysleep; static int task_period_counter[NTASKS] = { 0 , 0 , 0 } ; inline RTIME loop(RTIME count) { //esegue un loop da 0 a count unsigned long int i; // ritorna il tempo in ns per eseguire un loop RTIME starttime, sleeptime_ns; starttime = rt_get_time_ns(); for (i = 0; i < count ; i++) {} sleeptime_ns=rt_get_time_ns()-starttime; return sleeptime_ns; }

  30. /* function to callibrate busysleep function */ void calibrate_busysleep(void) { RTIME sleeptime_ns; RTIME x; volatile RTIME loop_size=CALIBRATION_LOOP_SIZE; // loop with global CALIBRATION_LOOP_SIZE sleeptime_ns=loop(loop_size); rt_printk("sleeptime_ns=%lld\n",sleeptime_ns); // // sleeptime_in_us = sleeptime_ns/1000 // count_need_for_one_us_busysleep=calibration_loop_size/sleeptime_in_us; // -> somma fattore di calibrazione : // sleeptime_in_us -> sleeptime_in_us * calibrationpercentage/100 // -> in definitiva // counterbusysleepns= calibration_loop_size*10*calibrationpercentage/sleeptime_ns x=CALIBRATION_LOOP_SIZE*10*CALIBRATION_PERCENTAGE*1000; do_div(x,sleeptime_ns); // fattore di calibrazione ttale count_need_for_one_ms_busysleep=x; }

  31. //tiene il processore occuato per sleeptime_us, ritorna il tempo passato RTIME busysleep(RTIME sleeptime_us ) { RTIME temp; RTIME sleeptime_ns; RTIME sleep_count; sleep_count= count_need_for_one_ms_busysleep*sleeptime_us; do_div(sleep_count,1000); sleeptime_ns=loop(sleep_count); return sleeptime_ns; } // calibrazione della attesa attiva { rt_printk("------------------------------------------ init task started\n",arg); calibrate_busysleep(); rt_printk("count_need_for_one_ms_busysleep=%lld\n", count_need_for_one_ms_busysleep); rt_printk("------------------------------------------ init task ended\n",arg); return; } // time_in_base_periods=(rt_get_time()-starttime )/base_period // -> integer part : RTIME time_int(RTIME temp) { do_div(temp,base_period); return temp; } // -> first two digits : RTIME time_digits(RTIME temp) { RTIME rest; rest=do_div(temp,base_period); temp=rest*100; do_div(temp,base_period); return temp; }

  32. RTIME get_time() //calcola il tempo di esecuzione { RTIME temp; temp=rt_get_time()-tasks_starttime; return temp; } void print_info(int currenttask,char *msg) { RTIME now=get_time(); rt_printk("T%d %s - time %3lld.%02lld - computation %3d - period %3d - interval %d-%d \n",currenttask, msg, time_int(now),time_digits(now), task_computation_time[currenttask], task_period[currenttask], task_period_counter[currenttask]*task_period[currenttask], (task_period_counter[currenttask]+1)*task_period[currenttask]); } void dummy_task(long t) /calibrazione { RTIME cur_task_sleeptime_ns, cur_task_sleeptime_us, base_periods_passed, cur_task_period; cur_task_period=task_period[t]*base_period; cur_task_sleeptime_us=base_period_us*task_computation_time[t]; task_period_counter[t]=0; base_periods_passed=0; if (!tasks_starttime) tasks_starttime=rt_get_time(); while( time_int(get_time()) < ggd && nodeadlinemiss ) { resumetime[t]=tasks_starttime + (task_period_counter[t]*cur_task_period); deadlinetime[t]=resumetime[t]+cur_task_period; print_info(t,"start "); cur_task_sleeptime_ns=busysleep(cur_task_sleeptime_us); if ( rt_get_time() >= deadlinetime[t] + base_period ) { rt_printk("\n\n\n"); rt_printk(" TASK %d missed deadline %d", t, (task_period_counter[t]+1)*task_period[t] ); rt_printk("\n\n\n"); nodeadlinemiss=0; } print_info(t,"stop "); rt_task_wait_period(); task_period_counter[t]++; } print_info(t,"ended ");if (nodeadlinemiss) rt_printk("\n\n SCHEDULABILE\n\n ", t); return; }

  33. int init_module(void) //parte il task con chedulaione specifica { int i; RTIME temp,starttime; printk(“inizia init_module\n"); printk("INSMOD on CPU %d.\n", hard_cpu_id()); rt_sem_init(&sync, 0); rt_set_periodic_mode(); //configura il modo base_period = start_rt_timer(nano2count(BASE_PERIOD_NS)); rt_printk("base_period : %lld.\n\n",base_period); base_period_ns=count2nano(base_period); rt_printk("base_period_ns : %lld.\n\n",base_period_ns); temp=base_period_ns; do_div(temp,1000); base_period_us=temp; // base_period_us=count2nano(base_period)/1000; rt_printk("base_period_us : %lld.\n\n",base_period_us); rt_task_init(&init_task_str, init, 0, STACK_SIZE, 100, 0, 0); rt_task_resume(&init_task_str); for (i = 0; i < NTASKS; i++) { task_arg[i]=i; // il task fittizio parte subito rt_task_init(&tasks[i], dummy_task, task_arg[i], STACK_SIZE, task_priority[i], 1, 0); } starttime=rt_get_time()+1000000; for (i = 0; i < NTASKS; i++) { rt_task_make_periodic(&tasks[i], starttime,task_period[i]*base_period); } printk("End of init_module\n"); return 0; }

  34. void cleanup_module(void) { int i; stop_rt_timer(); rt_sem_delete(&sync); printk("\n\n"); for (i = 0; i < NTASKS; i++) { rt_task_delete(&tasks[i]); } rt_task_delete(&init_task_str); }

  35. Schedulazione EarliestDeadlineFirst (EDF) #include <linux/module.h> #include <asm/io.h> #include <asm/rtai.h> #include <rtai_sched.h> #define ONE_SHOT #define TICK_PERIOD 10000000 #define STACK_SIZE 2000 #define LOOPS 3 #define NTASKS 8 static RT_TASK thread[NTASKS]; static RTIME tick_period; static int cpu_used[NR_RT_CPUS]; static void fun(long t) { unsigned int loops = LOOPS; while(loops--) { cpu_used[hard_cpu_id()]++; rt_printk("TASK %d with priority %d in loop %d \n", t, thread[t].priority,loops); rt_task_set_resume_end_times(-NTASKS*tick_period, -(t + 1)*tick_period); } rt_printk("TASK %d with priority %d ENDS\n", t, thread[t].priority); }

  36. EDF (cont.) int init_module(void) { RTIME now; int i; #ifdef ONE_SHOT rt_set_oneshot_mode(); #endif for (i=0;i<NTASKS;i++) rt_task_init(&thread[i],fun,i,STACK_SIZE,NTASKS-i-1,0,0); tick_period = start_rt_timer(nano2count(TICK_PERIOD)); now = rt_get_time() + NTASKS*tick_period; for (i = 0; i < NTASKS; i++) { rt_task_make_periodic(&thread[NTASKS - i - 1], now, NTASKS*tick_period); } return 0; } void cleanup_module(void) { int i, cpuid; stop_rt_timer(); for (i = 0; i < NTASKS; i++) {rt_task_delete(&thread[i]);} printk("\n\nCPU USE SUMMARY\n"); for (cpuid = 0; cpuid < NR_RT_CPUS; cpuid++) { printk("# %d -> %d\n", cpuid, cpu_used[cpuid]); } printk("END OF CPU USE SUMMARY\n\n"); }

  37. Programmazione in RTAI: IPC RTAI usa sistemi di IPC simili a Linux ma implementati separatamente: rt_fifo: scambio dati tra i thread in tempo reale, tra processi Linux, shared memory, tra thread in tempo reale e processi Linux mailbox semafori RPC

  38. Programmazione in RTAI: IPC rt_fifo Per creare una rt_fifo: int rtf_create(unsigned int fifo, int size); Per dimensionare una rt_fifo: int rtf_resize(int fd, int size); Per creare/aprire una rt_fifo dallo spazio utente si usa file_descriptor = open("/dev/rtf0", O_RDONLY); Per creare/aprire una rt_fifo dallo spazio kernel si usa: int rtf_open_sized(const char *dev, int perm, int size); Le rt_fifo possono essere associate a dei command handler che vanno in esecuzione ogni volta che un processo nello spazio utente esegue una read() o una write() sulla fifo: int rtf_create_handler(unsigned int minor, int (*handler)(unsigned int fifo)););

  39. Programmazione in RTAI: IPC rt_fifo: esempio d’uso dei command handler int rtf_create_handler(fifo_numver, X_FIFO_HANDLER(x_handler); con, ad esempio, come x_handler: int x_handler(unsigned int fifo, int rw) { if(rw==‘r’){ //quello che bisogna fare in relazione ad una read } else{ //quello che bisogna fare in relazione ad una write } }

  40. Programmazione in RTAI: IPC rt_fifo: per evitare bloccaggi indeterminati int rtf_read_all_at_once(int fd, void *buf, int count); int rtf_read_timed(int fd, void *buf, int count, int ms_delay); int rtf_write_timed(int fd, void *buf, int count, int ms_delay); rt_fifo: uso dei semafori int rtf_sem_init(unsigned int fifo, int value); int rtf_sem_wait(unsigned int fifo); //solo dallo spazio utente int rtf_sem_trywait(unsigned int fifo); //solo dallo spazio utente ...

  41. Programmazione in RTAI: IPC Per accedere una rt_fifo Dal lato real time num_read = rtf_get(0, &buffer_in, sizeof(buffer_in)); num_written = rtf_put(1, &buffer_out, sizeof(buffer_out)); Dal lato Linux num_read = read(read_descriptor, &buffer_in, sizeof(buffer_in)); num_written = write(write_descriptor, &buffer_out,sizeof(buffer_out)); Letture bloccanti: Unix supporta sia lettura bloccanti che non In sistemi real time sono preferibili le letture non bloccanti: 'rtf_get()' ritorna immediatamente se non ci sono dati da leggere

  42. //FIFO monodirezionali #include <linux/kernel.h> /* decls needed for kernel modules */ #include <linux/module.h> /* decls needed for kernel modules */ #include <linux/version.h> /* LINUX_VERSION_CODE, KERNEL_VERSION() */ #include <linux/errno.h> /* EINVAL, ENOMEM */ #include <rtai.h> #include <rtai_sched.h> #include <rtai_fifos.h> MODULE_LICENSE("GPL") ; static RT_TASK t1; static RT_TASK t2; void taskOne(long arg); void taskTwo(long arg); #define MAX_MESSAGES 100 #define MAX_MESSAGE_LENGTH 50 void message(void) /* function to create the message queue and two tasks */ { int retval; rtf_create (0,MAX_MESSAGES*MAX_MESSAGE_LENGTH); //crea una FIFO con numero 0 retval = rt_task_init(&t1,taskOne, 0, 1024, 0, 0, 0); retval = rt_task_init(&t2,taskTwo, 0, 1024, 0, 0, 0); retval = rt_task_resume(&t1); //esegue i thread: attenzione all’ordine retval = rt_task_resume(&t2); }

  43. void taskOne(long arg) { int retval; char message[] = "Received message from taskOne"; rt_printk("taskOne starts sending message\n"); retval = rtf_put(0, &message, sizeof(message)); //trasmette rt_printk("taskOne continues after sending message\n"); } void taskTwo(long arg) { int retval; char msgBuf[MAX_MESSAGE_LENGTH]; rt_printk("taskTwo ready to receive\n"); retval = rtf_get(0, &msgBuf, sizeof(msgBuf)); //riceve if (retval>0){ rt_printk("TaskTwo: %s\n", msgBuf); rt_printk(" lunghezza: %d\n", retval); } else { printk("FIFO queue is empty\n"); } } int init_module(void) { printk("start of init_module\n"); rt_set_oneshot_mode(); start_rt_timer(1); message(); printk("end of init_module\n"); return 0; } void cleanup_module(void) { stop_rt_timer(); rt_task_delete(&t1); rt_task_delete(&t2); return; }

  44. Altro esempio: FIFO bidirezionali #include <linux/kernel.h> #include <linux/module.h> #include <linux/version.h> #include <linux/errno.h> #include <rtai.h> #include <rtai_sched.h> #include <rtai_fifos.h> MODULE_LICENSE("GPL"); static RT_TASK t1; static RT_TASK t2; void taskOne(long arg); void taskTwo(long arg); #define MAX_MESSAGES 100 #define MAX_MESSAGE_LENGTH 50 static RTIME delay_count, delay_ns = 1e6; /* in nanoseconds, -> 1 msec */ void message(void) /* function to create the message queue and two tasks */ { int retval; rtf_create (1,MAX_MESSAGES*MAX_MESSAGE_LENGTH); //crea FIFO con id 0 rtf_create (2,MAX_MESSAGES*MAX_MESSAGE_LENGTH); rt_set_oneshot_mode(); start_rt_timer(1); delay_count = nano2count(delay_ns); //specifica il ritardo retval = rt_task_init(&t1,taskOne, 0, 1024, 0, 0, 0); retval = rt_task_init(&t2,taskTwo, 0, 1024, 0, 0, 0); retval = rt_task_resume(&t1); retval = rt_task_resume(&t2); //esegue i thread }

  45. void taskOne(long arg) { int retval=0; char message[] = “Messaggio dal taskOne"; char msgBuf[MAX_MESSAGE_LENGTH]; msgBuf[0]=0; rt_printk("taskOne inizia a mandare un messaggio al taskTwo via FIFO\n"); retval = rtf_put(2, &message, sizeof(message)); //manda messaggio a taskTwo rt_printk("taskOne continua\n"); t_sleep(delay_count); //aspetta per far partire taskTwo retval = rtf_get(1, &msgBuf, sizeof(msgBuf)); //riceve il messaggio if ( retval < 0 ) { rt_printk("problem with fifo \n"); } //test: cambia id in rtf_get else { rt_printk("taskOne riceve: %s con lunghezza: %d \n", msgBuf, retval); } } void taskTwo(long arg) { int retval=0; char msgBuf[MAX_MESSAGE_LENGTH]; char message[] = " Messaggio dal taskTwo "; msgBuf[0]=0; rt_printk(" taskTwo inizia a mandare un messaggio al taskOne via FIFO\n "); retval = rtf_put(1, &message, sizeof(message)); //manda messaggio a taskOne rt_printk("taskTwo continua\n"); rt_printk("taskTwo pronto per ricevere\n"); retval = rtf_get(2, &msgBuf, sizeof(msgBuf)); //riceve if ( retval < 0 ) { rt_printk("problem with fifo \n"); } else { rt_printk("taskTwo receive: %s con lunghezza %d\n", msgBuf, retval); } }

  46. int init_module(void) { printk("start of init_module\n"); message(); printk("end of init_module\n"); return 0; } void cleanup_module(void) { stop_rt_timer(); rt_task_delete(&t1); rt_task_delete(&t2); return; }

  47. Programmazione in RTAI: IPC Mailbox: è un buffer gestito dal SO per scambio di messaggi tra processi e task Possono essere inizializzati per messaggi di lunghezza variabile Supportano più lettori/scrittori su base prioritaria Gestione prioritaria: un task che invia può essere interrotto se il task che aspetta è a priorità più alta Usabili dallo spazio utente e dallo spazio kernel Possono sostituire le rt_fifo ma sono più lente Implementate nello schedulatore di RTAI Invio e ricezione di messaggi: Incondizionato Su un certo numero di byte Con scadenza relativa/assoluta Per inizializzare/creare (messaggi di lunghezza arbitraria) int rt_mbx_init(MBX *mbx, int size);//size del buffer Inizializzare/creare una mailbox tipizzata int rt_typed_mbx_init(MBX *mbx, int size, int type)

  48. Programmazione in RTAI: IPC Mailbox: per inviare/ricevere dati senza/con condizioni int rt_mbx_send(MBX *mbx, int size); int rt_mbx_receive(MBX *mbx, int size); int rt_mbx_send_wp(MBX *mbx, int size); int rt_mbx_receive_wp(MBX *mbx, int size); int rt_mbx_send_if(MBX *mbx, int size); int rt_mbx_receive_if(MBX *mbx, int size); Programma d’esempio: taskOne manda un messaggio via mailbox a taskTwo che lo scrive

  49. #include <linux/kernel.h> /* decls needed for kernel modules */ #include <linux/module.h> /* decls needed for kernel modules */ #include <linux/version.h> /* LINUX_VERSION_CODE, KERNEL_VERSION() */ #include <linux/errno.h> /* EINVAL, ENOMEM */ #include <rtai.h> #include <rtai_sched.h> #include <rtai_mbx.h> MODULE_LICENSE("GPL"); static RT_TASK t1; static RT_TASK t2; void taskOne(long arg); void taskTwo(long arg); #define MAX_MESSAGES 100 #define MAX_MESSAGE_LENGTH 50 static MBX mailboxId; void message(void) /* function to create the message queue and two tasks */ { int retval; retval = rt_typed_mbx_init (&mailboxId, MAX_MESSAGES, FIFO_Q); //crea mailbox if (0 != retval) { if (-ENOMEM == retval) { printk(“errore ENOMEM"); } else { printk(“errore sconosciuto\n"); } } retval = rt_task_init(&t1,taskOne, 0, 1024, 0, 0, 0); retval = rt_task_init(&t2,taskTwo, 0, 1024, 0, 0, 0); //init retval = rt_task_resume(&t1); retval = rt_task_resume(&t2); //exec }

  50. void taskOne(long arg) /* task che scrive nella mailbox */ { int retval; char message[] = “ricvuto messaggio da TaskOne"; retval = rt_mbx_send(&mailboxId, message, sizeof(message)); //spedisce if (0 != retval) { if (-EINVAL == retval) { rt_printk("mailbox invalida\n"); } else { rt_printk(“errore sconosciuto\n"); } } else { rt_printk("taskOne ha inviato messaggio\n");} } void taskTwo(long arg) /* tasks che legge dalla mailbox */ { int retval; char msgBuf[MAX_MESSAGE_LENGTH]; retval = rt_mbx_receive_wp(&mailboxId, msgBuf, 50); if (-EINVAL == retval) { rt_printk("mailbox invalida\n"); } else { rt_printk("taskTwo receive : %s con lunghezza %d\n",msgBuf, 50-retval); } /* cancella la mailbox */ rt_mbx_delete(&mailboxId); }

More Related