320 likes | 521 Views
Real-Time Systems Inter-Process Communication in RT-Linux Professor : Wei-Kuan Shih Student : g914344 陸婉珍 g914367 洪吉亮. Introduction. rt_ipc is a Linux loadable module for use with Real-Time Linux
E N D
Real-Time Systems Inter-Process Communication in RT-Linux Professor : Wei-Kuan Shih Student : g914344 陸婉珍 g914367 洪吉亮
Introduction • rt_ipc is a Linux loadable module for use with Real-Time Linux • Currently rt_ipc provides semaphores (binary and counting), message queues, and an enhancement to the RT-FIFO mechanism that provides blocking and timeouts
prev prev prev next next next task task task Task List Data Structure • RT_TASK_ENTRY(wait list)
int magic int val RT_SEMTYPE type RT_TASK_ENTRY wait_list Semaphore Data Structure • rt_sem_t
magic val type wait_list FIFO Data Structure • RT_IPC_FIFO rt_sem_tsem
int magic char q int msg_size int max_msgs char f char r RT_MQ_STATUS status RT_TASK_ENTRY wait_list Message Queue Data Structure • rt_mq_t
RT_TASK rtl_task magic q int magic msg_size RT_TASK_ENTRY rte max_msgs rt_sem_t sem_at f rt_mq_t mq_at r magic int timed_out status val wait_list type wait_list RT-Linux Task Data Structure for IPC • RT_TASK_IPC
rt_task_ipc_init(rt_ipc) • The function initializes some rt_ipc variables int rt_task_ipc_init(RT_TASK_IPC *task, void (*fn)(int data), int data, int stack_size, int priority) { int ret; task->sem_at = NULL; task->magic = RT_TASK_IPC_MAGIC; ret = rt_task_init(MAKE_RT_TASK(task), fn, data, stack_size, priority); if (ret < 0) { return ret; } (*MAKE_RT_TASK(task))->user[IPC_DATA_INDEX] = task; return 0; } This allows you to check at run time whether (a) a structure has been clobbered, or (b) you've passed the wrong structure to a routine Returns 0 if successful, -EINVAL if the 'task' structure is already in * use by another task, or -ENOMEM if a memory allocation error occurred
rt_task_ipc_delete(rt_ipc) • The function removes the task from any semaphore or message queue int rt_task_ipc_delete(RT_TASK_IPC *task) { int ret = 0; if (task->magic != RT_TASK_IPC_MAGIC) ret = EINVAL; else { int flags; rtl_critical(flags); if (task->sem_at != NULL) unlink_sem_task(&(task->rte), task->sem_at);
rt_task_ipc_delete(rt_ipc) (cont.) else if (task->mq_at != NULL) unlink_mq_task(&(task->rte), task->mq_at); rtl_end_critical(flags); ret = rt_task_delete(MAKE_RT_TASK(task)); } return ret; }
rt_task_delay • The function delays the calling task until the time specified in ‘duration’ int rt_task_delay(RTIME duration) { int ret = 0; int flags; rtl_critical(flags); pthread_self()->period = 0; RTL_MARK_SUSPENDED(pthread_self()); __rtl_setup_timeout(pthread_self(), HRT_FROM_8254(duration)); rtl_schedule(); rtl_end_critical(flags); return ret; } mark the task as delayed set the time at which execution may resume
rt_sem_init • The function initializes a real-time semaphore int rt_sem_init(rt_sem_t *sem, RT_SEMTYPE type, int init_val) { int ret = 0; if (init_val < 0 || (type == RT_SEM_BINARY && init_val > 1)) ret = -EINVAL; else { sem->magic = RT_SEM_MAGIC; sem->val = init_val; sem->type = type; sem->wait_list = NULL; } return ret; } binary sem must have 0 or 1
rt_sem_destroy • The function removes a real-time semaphore previously created with rt_sem_init() int rt_sem_destroy(rt_sem_t *sem) { int ret = 0; if (sem->magic != RT_SEM_MAGIC) ret = -EINVAL; else while (sem->val < 0) rt_sem_post(sem); return ret; } unblock any tasks blocked on this sem
rt_mq_init • initialize a real-time message queue int rt_mq_init(rt_mq_t *mq, int max_msgs, int msg_size) { int ret = 0; if (max_msgs <= 0 || msg_size < 0) ret = -EINVAL; /* must be positive */ else { mq->magic = RT_MQ_MAGIC; mq->wait_list = NULL; mq->max_msgs = max_msgs; mq->msg_size = msg_size; if ((mq->q = kmalloc(max_msgs * msg_size, GFP_KERNEL)) == NULL) ret = -ENOMEM; else { mq->status = RT_MQ_EMPTY; mq->f = mq->r = mq->q; /* initialize queue pointers */ } } return ret; }
rt_mq_destroy • remove a real-time message queue int rt_mq_destroy(rt_mq_t *mq) { int ret = 0; if (mq->magic != RT_MQ_MAGIC) ret = -EINVAL; else { /* unblock any tasks blocked on this message queue */ while (rt_mq_send(mq, NULL, RT_MQ_NORMAL, RT_NO_WAIT) != 0) ; while (rt_mq_receive(mq, NULL, RT_NO_WAIT) != 0) ; kfree_s(mq->q, mq->max_msgs * mq->msg_size); } return ret; }
unlink_sem_task • The function removes a task from the list of tasks waiting on a semaphore static void unlink_sem_task(RT_TASK_ENTRY *to_unlink, rt_sem_t *sem) { if (to_unlink->next != NULL) to_unlink->next->prev = to_unlink->prev; if (to_unlink->prev == NULL) sem->wait_list = to_unlink->next; else to_unlink->prev->next = to_unlink->next; }
unlink_mq_task • The function removes a task from the list of tasks waiting on a message queue static void unlink_mq_task(RT_TASK_ENTRY *to_unlink, rt_mq_t *mq) { if (to_unlink->next != NULL) to_unlink->next->prev = to_unlink->prev; if (to_unlink->prev == NULL) mq->wait_list = to_unlink->next; else to_unlink->prev->next = to_unlink->next; }
rtf_ipc_create • The function creates an rt_ipc rt-fifo int rtf_ipc_create(unsigned int fifo, int size, int rtl_to_linux) { int ret = 0; if (fifo >= IPC_RTF_NO) ret = -ENODEV; else if ((ret = rtf_create(fifo, size)) >= 0) { if ((ret = rt_sem_init(&ipc_fifos[fifo].sem, RT_SEM_BINARY, rtl_to_linux)) >= 0) ret = rtf_create_handler(fifo, &rtf_ipc_handler); } return ret; } init the semaphores -- initially no data is ready to receive, but data can be sent
rtf_ipc_destroy • The function destroy an rt_ipc rt-fifo int rtf_ipc_destroy(unsigned int fifo) { int ret; if ((ret = rt_sem_destroy(&ipc_fifos[fifo].sem)) >= 0) ret = rtf_destroy(fifo); return ret; }
Semaphore Operation: Wait (Blocking)-1 int rt_sem_wait(rt_sem_t *sem, RTIME timeout) { int ret = 0; int flags; if (sem->magic != RT_SEM_MAGIC) ret = -EINVAL; else { rtl_critical(flags); if (sem->val <= 0) { … } else --sem->val; rtl_end_critical(flags); } return ret; } invalid rt_sem_t structure sem not available -- task must wait
Semaphore Operation: Wait (Blocking)-2 RT_TASK_ENTRY *to_add = &(((RT_TASK_IPC *)rtl_current)->rte); to_add->task = rtl_current; to_add->prev = NULL; to_add->next = sem->wait_list; if (to_add->next != NULL) to_add->next->prev = to_add; sem->wait_list = to_add; ((RT_TASK_IPC *)rtl_current)->sem_at = sem; --sem->val; if (timeout == RT_WAIT_FOREVER) rt_task_suspend(rtl_current); else { ((RT_TASK_IPC *)rtl_current)->timed_out = 1; rt_task_delay(timeout); if (((RT_TASK_IPC *)rtl_current)->timed_out) { unlink_sem_task(to_add, sem); ++sem->val; ret = -ETIME; } } Put the task into the head of the wait list and mark the task as blocked by the semaphore Block the task
Semaphore Operation: Wait (Unblocking) • The function returns immediately whether or not the semaphore is available int rt_sem_trywait(rt_sem_t *sem) { int ret = 0; int flags; if (sem->magic != RT_SEM_MAGIC) ret = -EINVAL; else { rtl_critical(flags); if (sem->val <= 0) ret = -EAGAIN; else --sem->val; rtl_end_critical(flags); } return ret; } invalid rt_sem_t structure sem not available -- task must wait
Semaphore Operation: Signal(Post) int rt_sem_post(rt_sem_t *sem) { int ret = 0; int flags; if (sem->magic != RT_SEM_MAGIC) ret = -EINVAL; else { … } return ret; } check to see if this semaphore is valid
Semaphore Operation: Signal(Post) (cont.) There’re one or more tasks waiting for this semaphore, the task with the highest priority is chosen RT_TASK_ENTRY *to_run = NULL; rtl_critical(flags); if (sem->val < 0) { RT_TASK_ENTRY *t; for (t=sem->wait_list ; t!=NULL ; t=t->next) if (to_run == NULL || GET_PRIO(t->task) < GET_PRIO(to_run->task)) to_run = t; unlink_sem_task(to_run, sem); ((RT_TASK_IPC *)(to_run->task))->sem_at = NULL; } if (sem->val < 1 || sem->type == RT_SEM_COUNTING) ++sem->val; if (to_run != NULL) { ((RT_TASK_IPC *)(to_run->task))->timed_out = 0; rt_task_wakeup(to_run->task); } rtl_end_critical(flags); Wake up the chosen task
FIFO Operation: get data • The function gets data from the rt-fifo 'fifo'. The data, up to size 'count', is put * into ‘buf’ int rtf_receive(unsigned int fifo, void *buf, int count, RTIME timeout) { int ret = 0, bytes_still_to_get=count; for ( ; bytes_still_to_get > 0 ; ) { if ((ret = rtf_get(fifo, buf, bytes_still_to_get)) < 0) break; bytes_still_to_get -= ret; buf += ret; if (bytes_still_to_get != 0) if ((ret = rt_sem_wait(&ipc_fifos[fifo].sem, timeout)) < 0) break; } if (ret == -ETIME || ret == -EAGAIN) return count - bytes_still_to_get; else if (ret < 0) return ret; else { if ((ret = rt_sem_post(&ipc_fifos[fifo].sem)) < 0) return ret; else return count; } } Exit this loop on read error Upon being pre-empted, waiting for being woken up. Exit this loop when timeout
FIFO Operation: send data • The function sends data to the rt-fifo 'fifo'. The data, of size ‘count‘, is taken from ‘buf’ and put to ‘fifo’. If the entire block of data can’t be sent, then no data is sent int rtf_send(unsigned int fifo, void *buf, int count, RTIME timeout) { int ret = 0, bytes_still_to_send=count; for ( ; bytes_still_to_send > 0 ; ) { ret = rtf_put(fifo, buf, bytes_still_to_send); if (ret == -ENOSPC) ret = 0; else if (ret < 0) break; ret = (bytes_still_to_send - ret); bytes_still_to_send -= ret; buf += ret; if (bytes_still_to_send != 0) if ((ret = rt_sem_wait(&ipc_fifos[fifo].sem, timeout)) < 0) break; } If rtf_put() can’t place all bytes on the fifo, then return 0, meaning that no data is sent to fifo. Other error codes is returned according to different error conditions Do this due to the fact that rtf_put() returns the number of bytes not written Upon being pre-empted, waiting for being woken up. Exit this loop when timeout
FIFO Operation: send data (cont.) if (ret == -ETIME || ret == -EAGAIN) return count - bytes_still_to_send; else if (ret < 0) return ret; else { if ((ret = rt_sem_post(&ipc_fifos[fifo].sem)) < 0) return ret; else return count; } }
Message Queue Operation: enqueue • The function enqueues a block of data on the queue 'mq' with priority 'prio‘ static void enqueue(rt_mq_t *mq, char *msg, RT_MQ_PRIO prio) { if (prio == RT_MQ_NORMAL) { if (msg != NULL) memcpy(mq->r, msg, mq->msg_size); if ((mq->r += mq->msg_size) == mq->q + mq->msg_size * mq->max_msgs) mq->r = mq->q; }else{ if (mq->f == mq->q) mq->f = mq->q + mq->msg_size * (mq->max_msgs- 1) ; else mq->f -= mq->msg_size; if (msg != NULL) memcpy(mq->f, msg, mq->msg_size); } if (mq->f == mq->r) mq->status = RT_MQ_FULL; else mq->status = RT_MQ_NEITHER; } check for wraparound check for wraparound queue is now full
Message Queue Operation: dequeue • The function dequeues a block of data from the queue 'mq‘ static void dequeue(rt_mq_t *mq, char *msg) { if (msg != NULL) memcpy(msg, mq->f, mq->msg_size); if ((mq->f += mq->msg_size) == mq->q + mq->msg_size * mq->max_msgs) mq->f = mq->q; if (mq->r == mq->f) mq->status = RT_MQ_EMPTY; else mq->status = RT_MQ_NEITHER; } check for wraparound queue is now empty
Message Queue Operation: send • The function enqueues the data 'msg' on the message queue 'mq'. The data is assumed * to be of the size with which rt_mq_init() was called int rt_mq_send(rt_mq_t *mq, char *msg, RT_MQ_PRIO prio, RTIME wait) { int ret = 0; if (mq->magic != RT_MQ_MAGIC) ret = -EINVAL; else { int flags; rtl_critical(flags); switch (mq->status) { … enter the critical section and operate according to the status of the message queue
Message Queue Operation: send(cont.) Immediate return case RT_MQ_FULL: { if (wait == RT_NO_WAIT) ret = -EAGAIN; else { RT_TASK_ENTRY *to_add = &(((RT_TASK_IPC *)rtl_current)->rte); to_add->task = rtl_current; to_add->prev = NULL; to_add->next = mq->wait_list; if (to_add->next != NULL) to_add->next->prev = to_add; mq->wait_list = to_add; ((RT_TASK_IPC *)rtl_current)->mq_at = mq; if (wait == RT_WAIT_FOREVER) rt_task_suspend(rtl_current); else { ((RT_TASK_IPC *)rtl_current)->timed_out = 1; rt_task_delay(wait); if (((RT_TASK_IPC *)rtl_current)->timed_out) { unlink_mq_task(to_add, mq); ret = -ETIME; break; } } enqueue(mq, msg, prio); } break; } put task on wait_list indicate which mq the task is blocked at suspend until receive If waiting time is unbounded, then the task is suspended until being unblocked. Otherwise wait for unblocking until timeout. If timeout, then remove the this task from the waiting list The task is unblocked and the data is enqueued
Message Queue Operation: send(cont.) case RT_MQ_EMPTY: { RT_TASK_ENTRY *t, *to_run; enqueue(mq, msg, prio); for (t=mq->wait_list, to_run=NULL ; t!=NULL ; t=t->next) if (to_run == NULL || GET_PRIO(t->task) < GET_PRIO(to_run->task)) to_run = t; if (to_run != NULL) { unlink_mq_task(to_run, mq); ((RT_TASK_IPC *)(to_run->task))->mq_at = NULL; ((RT_TASK_IPC *)(to_run->task))->timed_out = 0; rt_task_wakeup(to_run->task); } break; } case RT_MQ_NEITHER: enqueue(mq, msg, prio); break; } rtl_end_critical(flags); } return ret; }