RTAI API Inter Process Communication Antonio Barbalace antonio.barbalace@unipd.it Overview Semaphores Shared Memory FIFOs Mailbox Typed Mailbox Messages POSIX Message Queue Antonio Barbalace 2009 RTOS - RTAI course 2
Semaphores A semaphore can be used for communication and synchronization among real time tasks. RTAI supports different kind of semaphores: CNT_SEM counting semaphores; BIN_SEM binary semaphores; RES_SEM resource semaphores; All orred combination of such semaphores. Mutexes are implemented by semaphores. RTAI supports different queuing policy: FIFO_Q for a fifo queuing; PRIO_Q for a priority queuing. Resource semaphores will enforce a PRIO_Q policy anyhow. Negative values of a semaphore show how many tasks are blocked on the semaphore queue, waiting to be awaken. Antonio Barbalace 2009 RTOS - RTAI course 3 Semaphores Prototypes in rtai_sem.h void rt_sem_init(sem *sem, int value) int rt_sem_delete(sem *sem) int rt_sem_signal(sem *sem) int rt_sem_broadcast(sem *sem) int rt_sem_wait(sem *sem) int rt_sem_wait_if(sem *sem) int rt_sem_wait_until(sem *sem, RTIME time) int rt_sem_wait_timed(sem *sem, RTIME delay) Antonio Barbalace 2009 RTOS - RTAI course 4
Semaphores Example file sem_example.c /* sem_example.c */ #define TASK_PRIORITY 1 #define TASK_PERIOD 2E9 #define STACK_SIZE 2000 #include <rtai.h> #include <rtai_sem.h> #include <rtai_sched.h> MODULE_LICENSE( GPL ); static RT_TASK rt_task_send; static RT_TASK rt_task_recv; static int global_rt_data; static SEM rt_sem; void task_sender(int t) { while(1) { rt_sem_wait(&rt_sem); global_rt_data++; rt_printk("sender (INC) %d\n", global_rt_data); rt_sem_signal(&rt_sem); void task_receiver(int t) { while(1) { rt_sem_wait(&rt_sem); rt_busy_sleep(1000); rt_printk("receiver (read) %d\n", global_rt_data); rt_sem_signal(&rt_sem); static int module_init(void) { rt_sem_init(&rt_sem, 1); rt_task_init(&rt_task_send, task_sender, 0, STACK_SIZE, TASK_PRIORITY, 0, 0); rt_task_init(&rt_task_recv, task_receiver, 0, STACK_SIZE, TASK_PRIORITY, 0, 0); rt_set_periodic_mode(); int period = start_rt_timer(nano2count(task_period)); RTIME time = rt_get_time(); rt_task_make_periodic(&rt_task_send, (time + period + 2), period); rt_task_make_periodic(&rt_task_recv, (time + period), period); static void module_exit(void) { rt_sem_delete(&rt_sem); stop_rt_timer(); rt_busy_sleep(nano2count(1e7)); rt_task_delete(&rt_task_send); rt_busy_sleep(nano2count(1e7)); rt_task_delete(&rt_task_recv); Antonio Barbalace 2009 RTOS - RTAI course 5 Semaphores Semaphores code in RTAI also implements: Barriers Conditional Variables Readers-Writers Locks Recursive Spinlocks Antonio Barbalace 2009 RTOS - RTAI course 6
Shared Memory The allocation of a shared memory between tasks in kernel space is trivial: since the address space is the same, common memory allocation functions are used. RTAI has a specific module that allows the overall symmetric allocation and sharing of memory inter/intra-kernel/user space; any pair of memory sharing is possible between the following: RTAI proper tasks, Linux kernel threads, Linux user space processes, LXRT tasks. All memory expansion requests are managed dynamically, but they will not impact real time performance, since they are deferred to be serviced when the Linux context is restored. Prototypes in rtai_shm.h void *rt_shm_alloc(unsigned long name, int size, int suprt); void *rt_shm_alloc_adr(void *start, unsigned long name, int size, int suprt); int rt_shm_free(unsigned long name); Antonio Barbalace 2009 RTOS - RTAI course 7 Shared Memory Example file shm_example.c /* shm_example.c */ #include <rtai.h> #include <rtai_sched.h> #include <rtai_shm.h> #include shm_example.h" MODULE_LICENSE( GPL ); static RT_TASK rt_task; static struct data_str *data; static void fun(int t) { unsigned int count = 0; int sen,cos; while (1) { data->indx_counter = count; sen = count%20*((-1)<<count)*24; cos = -count%20*((-1)<<count)*24; data->sin_value = sen; data->cos_value = cos; count++; int module_init(void) { RTIME tick_period; rt_set_periodic_mode(); rt_task_init(&rt_task, fun, 1, STACK_SIZE, TASK_PRIORITY, 1, 0); data = rt_shm_alloc(nam2num(shmnam), sizeof(struct data_str), USE_VMALLOC); tick_period = start_rt_timer(nano2count(tick_period)); rt_task_make_periodic(&rt_task, rt_get_time() + tick_period, tick_period); void cleanup_module(void){ stop_rt_timer(); rt_task_delete(&rt_task); rt_shm_free(nam2num(shmnam)); return; file shm_example.h file shm_usr.c /* shm_usr.c */ #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <rtai_shm.h> #include shm_example.h" static int end; static void endme(int dummy) { end=1; int main (void){ struct data_str *data; signal(sigint, endme); data = rt_shm_alloc(nam2num(shmnam),sizeof(struct data_str), USE_VMALLOC); while (!end) { printf(" Counter : %d Sine : %d Cosine : %d \n", data->indx_counter, data->sin_value, data- >cos_value); rt_shm_free(nam2num(shmnam)); /* shm_example.h */ #define TICK_PERIOD 1000000 #define TASK_PRIORITY 1 #define STACK_SIZE 10000 #define SHMNAM "MIRSHM" struct data_str{ int indx_counter; int sin_value; int cos_value; ; Antonio Barbalace 2009 RTOS - RTAI course 8
FIFO A RT FIFO is a point-to-point link connecting one real-time task to one Linux process. It s very much like a Unix pipe. The implementation allows a FIFO to be bidirectional, but in practice that rarely makes sense. Suppose, for example, that one end of the FIFO writes a command and then immediately tries to read the result of the command it just wrote. Chances are it would just read back the command it wrote. So in practice, FIFOs are unidirectional where the direction is established by the programmer. In the example just cited, you would create two FIFOs, one to send the command and the other to read the response. User Space processes treat a RT FIFOs as character devices, /dev/rtf0 to /dev/rtf63. A process opens a FIFO for reading or writing and then uses read() or write() on the file descriptor to transfer data. Real-time tasks access the FIFO through an RTAI-specific API. Antonio Barbalace 2009 RTOS - RTAI course 9 FIFO In Linux, FIFOs have to be created by $ mknod /dev/rtf<x> c 150 <x> where <x> is the minor device number, from 0 to 63. What is important to remember is that in user space you address fifos through the file descriptor you get at fifo device opening, while in kernel space you directly address them by their minor number. So you will mate the file descriptor you get in user space by using open(/dev/rtfxx,...) to the integer xx you will use in kernel space. RTAI fifos should be used just with applications that use only real time interrupt handlers, so that no RTAI scheduler is installed. To monitor the FIFO usage, RTAI has the /proc/rtai/fifo interface. Antonio Barbalace 2009 RTOS - RTAI course 10
FIFO Prototypes in rtai_fifos.h int rtf_create(unsigned int fifo, int size); int rtf_destroy(unsigned int fifo); int rtf_getfifobyname(const char *name); int rtf_reset(unsigned int fifo); int rtf_resize(unsigned int minor, int size); int rtf_put(unsigned int fifo, /* RT-FIFO */ void * buf, /* buffer address */ int count /* number of bytes to write */); int rtf_get(unsigned int fifo, /* RT-FIFO */ void * buf, /* buffer address */ int count /* number of bytes to read */); Antonio Barbalace 2009 RTOS - RTAI course 11 FIFO Example file fifo_example.c /* fifo_example.c */ #include <rtai.h> #include <rtai_sched.h> #include <rtai_fifos.h> #define TICK_PERIOD 1000000000 #define TASK_PRIORITY 1 #define STACK_SIZE 10000 #define FIFO 0 MODULE_LICENSE( GPL ); static RT_TASK rt_task; static void fun(int t) { int counter = 0; int sin_value = 3; while (1) { sin_value++; rtf_put(fifo, &counter, sizeof(counter)); rtf_put(fifo, &sin_value, sizeof(sin_value)); counter++; static int module_init(void) { int result; RTIME tick_period; rt_set_periodic_mode(); if (result = rt_task_init(&rt_task, fun, 0, STACK_SIZE, TASK_PRIORITY, 0, 0)!=0) { printk("<0> Errore init\n"); return EIO; rtf_create(fifo, 8000); tick_period = start_rt_timer(nano2count(tick_period)); rt_task_make_periodic(&rt_task, rt_get_time() + tick_period, tick_period); static void module_exit(void) { stop_rt_timer(); rtf_destroy(fifo); rt_task_delete(&rt_task); return; file fifo_usr.c /* fifo_usr.c */ #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> static int end; static void endme(int dummy) { end=1; int main (void){ int fifo, counter; int sin_value; if ((fifo = open("/dev/rtf/0", O_RDONLY)) < 0) { fprintf(stderr, "Error opening /dev/rtf/0\n"); exit(1); signal(sigint, endme); while (!end) { read(fifo, &counter, sizeof(counter)); read(fifo, &sin_value, sizeof(sin_value)); printf(" Counter : %d Sin : %d \n", counter, sin_value); Antonio Barbalace 2009 RTOS - RTAI course 12
Mailbox Using mailboxes is a flexible method for inter task communications. A mailbox is created with an initial size. Tasks are allowed to send arbitrarily sized messages by using any mailbox buffer size. There is even no need to use a buffer sized at least as the largest message you envisage, even if efficiency is likely to suffer from such a decision. However if you expect a message larger than the average message size very rarely, then you can use a smaller buffer without much loss of efficiency. This allows you to set up your own mailbox usage protocol; e.g. using fixed sized messages, with a buffer size that is a multiple of the size of messages, guarantees maximum efficiency by having each message sent/received atomically to/from the mailbox. Multiple senders and receivers are allowed and each will get the service it requires in turn, according to its priority. Thus mailboxes provide a flexible mechanism to allow you to freely implement your own policy. Antonio Barbalace 2009 RTOS - RTAI course 13 Mailbox Prototypes in rtai_mbx.h int rt_mbx_init(struct rt_mailbox *mbx, int size) int rt_mbx_delete(struct rt_mailbox *mbx) int rt_mbx_send(struct rt_mailbox *mbx, void *msg, int msg_size) int rt_mbx_send_wp(struct rt_mailbox *mbx, void *msg, int msg_size) int rt_mbx_send_if(struct rt_mailbox *mbx, void *msg, int msg_size) int rt_mbx_send_until(struct rt_mailbox *mbx, void *msg, int msg_size, RTIME time) int rt_mbx_send_timed(struct rt_mailbox *mbx, void *msg, int msg_size, RTIME delay) int rt_mbx_ovrwr_send(struct rt_mailbox *mbx, void *msg, int msg_size) int rt_mbx_receive(struct rt_mailbox *mbx, void *msg, int msg_size) int rt_mbx_receive_wp(struct rt_mailbox *mbx, void *msg, int msg_size) int rt_mbx_receive_if(struct rt_mailbox *mbx, void *msg, int msg_size) int rt_mbx_receive_until(struct rt_mailbox *mbx, void *msg, int msg_size, RTIME time) int rt_mbx_receive_timed(struct rt_mailbox *mbx, void *msg, int msg_size, RTIME delay) Antonio Barbalace 2009 RTOS - RTAI course 14
Mailbox Example file mbx_example.c /* mbx_example.c */ #define TASK_PRIORITY 0 #define TASK_PERIOD 2E9 #define JITTER 10 #define STACK_SIZE 2000 #include <rtai.h> #include <rtai_sched.h> #include <rtai_mbx.h> MODULE_LICENSE( GPL ); typedef struct message { int count; RTIME sender; RTIME receiver; MSG; static RT_TASK rt_task_send; static RT_TASK rt_task_recv; MBX mbx; void task_sender(int t) { int result; int counter = 0; MSG send_msg = {0, 0, 0; while(1) { send_msg.count = counter++; send_msg.sender = rt_get_time(); result = rt_mbx_send(&mbx, &send_msg, sizeof(msg)); if (!(result==sizeof(msg) result==0)) { rt_printk( send error\n ); continue; ; rt_printk("%ssender: msg SENT count: %d time: %d\n", DEBUG_P, send_msg.count, send_msg.sender); void task_receiv(int t) { int result; MSG recv_msg = {0, 0, 0; while(1){ result = rt_mbx_receive(&mbx, &recv_msg, sizeof(msg)); if (!(result==sizeof(msg) result==0)) { rt_printk( recv error\n ); continue; recv_msg.receiver = rt_get_time(); rt_printk("%sreceiver: msg RECV count: %d times: %d timer: %d\n", DEBUG_P, recv_msg.count, recv_msg.sender, recv_msg.receiver); static int module_init(void) { int result; if((result = rt_mbx_init(&mbx, sizeof(msg)))!=0) { printk("%sinit ERROR no too space for buffer mailbox\n", DEBUG_P); return EIO; rt_task_init(&rt_task_send, task_sender, 0, STACK_SIZE, TASK_PRIORITY, 0, 0); rt_task_init(&rt_task_recv, task_receiv, 0, STACK_SIZE, TASK_PRIORITY, 0, 0); printk("%speriodic MODE enabled\n", DEBUG_P); rt_set_periodic_mode(); int period = start_rt_timer(nano2count(task_period)); RTIME time = rt_get_time(); rt_task_make_periodic(&rt_task_send, time + period, period); rt_task_make_periodic(&rt_task_recv, time + period + JITTER, period); static void cleanup_module(void) { stop_rt_timer(); rt_busy_sleep(nano2count(1e7)); rt_task_delete(&rt_task_send); rt_busy_sleep(nano2count(1e7)); rt_task_delete(&rt_task_recv); Antonio Barbalace 2009 RTOS - RTAI course 15 Typed Mailbox Typed mailboxes (TBX) are an alternative to the default RTAI ones. Typed mailboxes offer: 1. Message broadcasting, that is sending a message to ALL the tasks that are pending on the same TBX. 2. Urgent sending of messages: these messages are not queued, but are inserted at the head of the queue, bypassing all the other messages already present in TBX. 3. The possibility to set up the PRIORITY/FIFO wakeup policy at runtime, when creating the TBX. Features (1) and (2) are achieved by adding a type field (1 byte) to every message inserted in a TBX: this byte makes it possible to discriminate normal, urgent and broadcast messages, when they are received. The type field is removed by the receiving functions, so from the user point of view it is not visible. Users must consider type fields only when specifying the TBX sizes. Antonio Barbalace 2009 RTOS - RTAI course 16
int rt_tbx_init(tbx, size, flags) int rt_tbx_delete(tbx) Typed Mailbox Prototypes in rtai_tbx.h int rt_tbx_send(tbx, msg, msg_size) * int rt_tbx_receive(tbx, msg, msg_size) * int rt_tbx_broadcast(tbx, msg, msg_size) * int rt_tbx_urgent(tbx, msg, msg_size) * rt_tbx_*_if rt_tbx_*_until rt_tbx_*_timed Antonio Barbalace 2009 RTOS - RTAI course 17 Typed Mailbox Example file tbx_example.c #include <rtai.h> #include <rtai_sched.h> #include <rtai_tbx.h> static RT_TASK mtask_0, mtask_1, btask, wdog; static TBX smbx, rmbx[2]; static unsigned long long name [2] = { 0xaaaaaaaaaaaaaaaaLL, 0xbbbbbbbbbbbbbbbbLL; static int stop, alarm; void wfun (int t) { while(stop) { if (alarm &&!stop) { stop = 1; rt_printk("locked\n"); alarm = 1; void mfun(int t) { unsigned long long msg; while(stop) { alarm = 0; rt_tbx_send_timed(&smbx, &name[t], sizeof(long long), nano2count(1000000)); msg = 0; rt_tbx_receive_timed(&rmbx[t], &msg, sizeof(msg), nano2count(1000000)); if(msg!= 0xccccccccccccccccLL) rt_printk(">em %d %d<\n", t, stop); rt_busy_sleep(1000000); void bfun(int t){ int result = 0; unsigned long long msg; unsigned long long name = 0xccccccccccccccccLL; while (stop) { alarm = 0; if((result = rt_tbx_receive(&smbx, &msg, sizeof(msg)))!=sizeof(msg)) { rt_printk( bfun error %x \n", result); if (msg == 0xaaaaaaaaaaaaaaaaLL) t = 0; else if (msg==0xbbbbbbbbbbbbbbbbll) t = 1; else { rt_printk(">eb %x %x<\n", ((int *) &msg)[0], ((int *) &msg)[1]); t = 0; ; rt_tbx_send(&rmbx[t], &name, sizeof(name)); static int module_init(void) { RTIME period; int result; if ((result = rt_tbx_init(&smbx, 5, 0))!=0) { rt_printk("error smbx %u\n", result); if ((result = rt_tbx_init(&rmbx[0], 1, 0))!=0) { rt_printk("error rmbx 0 %u\n", result); if ((result = rt_tbx_init(&rmbx[1], 3, 0))!=0) { rt_printk("error rmbx 1 %u\n", result); rt_task_init(&wdog, wfun, 0, 2000, 0, 0, 0); rt_task_init(&mtask_0, mfun, 0, 2000, 0, 0, 0); rt_task_init(&mtask_1, mfun, 1, 2000, 0, 0, 0); rt_task_init(&btask, bfun, 0, 2000, 0, 0, 0); alarm = 0; stop = 1; rt_set_oneshot_mode(); period = start_rt_timer(nano2count(2e9)); rt_task_make_periodic(&wdog, rt_get_time() + period, period); rt_task_resume(&btask); rt_task_resume(&mtask_0); rt_task_resume(&mtask_1); static module_exit(void){ stop = 0; rt_busy_sleep(nano2count(1e7)); stop_rt_timer(); rt_tbx_delete(&smbx); rt_tbx_delete(&rmbx[0]); rt_tbx_delete(&rmbx[1]); rt_task_delete(&mtask_0); rt_task_delete(&mtask_1); rt_task_delete(&btask); rt_task_delete(&wdog); Antonio Barbalace 2009 RTOS - RTAI course 18
Messages Messages are the native way to communicate between RTAI task. The RTAI task descriptor is used as the holder of the message. Antonio Barbalace 2009 RTOS - RTAI course 19 Messages Prototypes in rtai_msg.h struct rt_task_struct *rt_send(struct rt_task_struct *task, unsigned long msg); struct rt_task_struct *rt_send_if(struct rt_task_struct *task, unsigned long msg); struct rt_task_struct *rt_send_until(struct rt_task_struct *task, unsigned long msg, RTIME time); struct rt_task_struct *rt_send_timed(struct rt_task_struct *task, unsigned long msg, RTIME delay); struct rt_task_struct *rt_evdrp(struct rt_task_struct *task, void *msg); struct rt_task_struct *rt_receive(struct rt_task_struct *task, void *msg); struct rt_task_struct *rt_receive_if(struct rt_task_struct *task, void *msg); struct rt_task_struct *rt_receive_until(struct rt_task_struct *task, void *msg, RTIME time); struct rt_task_struct *rt_receive_timed(struct rt_task_struct *task, void *msg, RTIME delay); Antonio Barbalace 2009 RTOS - RTAI course 20
Messages Example file msg_example.c #define TASK_PRIORITY 0 #define TASK_PERIOD 2E9 #define STACK_SIZE 2000 #include <rtai.h> #include <rtai_sched.h> MODULE_LICENSE( GPL ); int jitter = 1; typedef struct message { int count; int sender; int receiver; MSG; RT_TASK rt_task_send; RT_TASK rt_task_recv; void task_sender(int t); void task_receiv(int t); void task_sender(int t){ int result; int counter = 0; MSG send_msg = {0, 0, 0; while(1) { send_msg.count = counter++; send_msg.sender = rt_get_time(); if(((rt_task*)result = rt_send(&rt_task_recv, &send_msg))!=&rt_task_recv) rt_printk( task_sender sender ERROR\n"); rt_printk("%ssender: msg SENT count: %d time: %u r: %d ptr: %d\n", DEBUG_P, send_msg.count, send_msg.sender, result, &rt_task_recv); void task_receiv(int t){ int result; int time0, time1; void *msg; MSG recv_msg={0, 0, 0; while(1) { time0 = rt_get_time(); if(((rt_task*)result = rt_receive(&rt_task_send, msg))!=&rt_task_send) rt_printk( task_recv receiv ERROR\n"); time1 = rt_get_time(); rt_return(&rt_task_send, 0);//? memcpy(&recv_msg, msg, sizeof(msg)); recv_msg.receiver = time1; rt_printk("%sreceiver: msg RECV count: %d times: %u timer: %u time: %ucount time: %u elapsed: %d\n", DEBUG_P, recv_msg.count, recv_msg.sender, recv_msg.receiver, recv_msg.receiver - recv_msg.sender, time0, time0 - recv_msg.receiver); static int module_init (void) { rt_task_init(&rt_task_send, task_sender, 0, STACK_SIZE, TASK_PRIORITY, 0, 0); rt_task_init(&rt_task_recv, task_receiv, 0, STACK_SIZE, TASK_PRIORITY+1, 0, 0); printk("%speriodic MODE enabled assuming jitter: %d\n", DEBUG_P, jitter); rt_set_periodic_mode(); int period = start_rt_timer(nano2count(task_period)); RTIME time = rt_get_time(); rt_task_make_periodic(&rt_task_send, time + period, period); rt_task_make_periodic(&rt_task_recv, time + period + jitter, period); static void module_exit(void) { stop_rt_timer(); rt_busy_sleep(nano2count(1e7)); rt_task_delete(&rt_task_send); rt_busy_sleep(nano2count(1e7)); rt_task_delete(&rt_task_recv); Antonio Barbalace 2009 RTOS - RTAI course 21 Message Queue Added in RTAI to support POSIX Message Queue standard. Antonio Barbalace 2009 RTOS - RTAI course 22
Message Queue Prototypes in rtai_mq.h mqd_t mq_open(char *mq_name, int oflags, mode_t permissions, struct mq_attr *mq_attr) int mq_close(mqd_t mq) int mq_getattr(mqd_t mq, struct mq_attr *attrbuf) int mq_setattr(mqd_t mq, const struct mq_attr *new_attrs, struct mq_attr *old_attrs) int mq_notify(mqd_t mq, const struct sigevent *notification) int mq_unlink(char *mq_name) size_t mq_receive(mqd_t mq, char *msg_buffer, size_t buflen, unsigned int *msgprio) int mq_send(mqd_t mq, const char *msg, size_t msglen, unsigned int msgprio) size_t mq_timedreceive(mqd_t mq, char *msg_buffer, size_t buflen, unsigned int *msgprio, const struct timespec *abstime) int mq_timedsend(mqd_t mq, const char *msg, size_t msglen, unsigned int msgprio, const struct timespec *abstime) Antonio Barbalace 2009 RTOS - RTAI course 23 Message Queue Example See POSIX.1b for examples... Antonio Barbalace 2009 RTOS - RTAI course 24