嵌入式操作系统内核原理和开发(消息队列)消息队列是线程交互的一种方法,任务可以通过消息队列来实现数据的沟通和交换。
在嵌入式系统上,这可以说这是用的最多的一种方法。
通过消息队列,无论是发送者,还是接受者都可以循环地处理各种消息。
而我们知道,存储消息最好的方式就是循环队列,如果消息已满,那么发送者可以把自己pend到等待队列上;而如果此时没有消息,那么接受者也可以把自己pend到等待队列上。
当然实现消息队列的方法很多,甚至用户可以自己利用互斥量和信号量来实现,而嵌入式系统常常会默认提供这样的功能函数,我想主要的目的还是为了方便用户,让他们可以更多地从业务的角度来看问题,而不是把重点关注在这些底层的细节上面。
首先,我们还是看看rawos上面关于消息队列的数据结构是怎么定义的,1typedef struct RAW_MSG_Q {23 RAW_VOID **queue_start; /* Pointer to start of queue data*/4 RAW_VOID **queue_end; /* Pointer to end of queue data*/5 RAW_VOID **write; /* Pointer to where next message willbe inserted in the Q */6 RAW_VOID **read; /* Pointer to where next message will beextracted from the Q */7 RAW_U32 size; /* Size of queue (maximum number of entries)*/8 RAW_U32 current_numbers; /* Current number of entries in thequeue */9 RAW_U16 blocked_send_task_numbers; /*number of blocked send tasknumbers */10 RAW_U16 blocked_receive_task_numbers; /*number of blocked send tasknumbers */1112 } RAW_MSG_Q;1314typedef struct RAW_QUEUE15 {16 RAW_COMMON_BLOCK_OBJECT common_block_obj;17 RAW_MSG_Q msg_q;1819 } RAW_QUEUE;上面的代码中有两段数据结构,第一段主要表示循环队列的内容,其中包括了队列首地址、队列末尾地址、当前队列读取地址、当前队列插入地址、队列大小、消息个数、阻塞的发送线程数据、阻塞的接受线程数目。
而第二段数据结构就比较简单,它把通用等待结构和循环队列合在了一起,共同构成了消息队列的数据结构。
根据我们以前的经验,互斥同步数据结构的操作都会分成几个部分,当然消息队列也不例外,也会分成初始化、发送消息、接受消息、清除消息、删除消息队列等几种操作函数。
当然,消息队列还是增加了一个新的选项,那就是插入消息的时候可以插入在队列的前方,还是插入在队列的尾部,这在某种程度上决定了消息的优先级。
说到这,我们还是看看消息队列是怎么初始化的,20RAW_U16 raw_queue_create(RAW_QUEUE *p_q, RAW_U8 *p_name, RAW_VOID**msg_start, RAW_U32 number)21 {2223 #if (RAW_QUEUE_FUNCTION_CHECK > 0)2425if (p_q == 0) {2627return RAW_NULL_OBJECT;28 }2930if ( msg_start == 0) {3132return RAW_NULL_POINTER;33 }3435if (number == 0) {3637return RAW_ZERO_NUMBER;38 }3940 #endif4142 list_init(&p_q->common_block_obj.block_list);4344 p_q->common_block_ = p_name;45 p_q->common_block_obj.block_way = 0;46 p_q->msg_q.queue_start = msg_start; /* Initializethe queue */47 p_q->msg_q.queue_end = &msg_start[number];48 p_q->msg_q.write = msg_start;49 p_q->msg_q.read = msg_start;50 p_q->msg_q.size = number;51 p_q->msg_q.current_numbers = 0;52 p_q->msg_q.blocked_send_task_numbers = 0;53 p_q->msg_q.blocked_receive_task_numbers = 0;54return RAW_SUCCESS;55 }56虽然相比较之前的互斥函数,消息队列的初始化内容好像多一些。
但是大家如果对循环队列的知识比较了解的话,其实也不是很复杂的。
我们看到,函数除了对通用阻塞结构进行初始化之外,就是对这些循环队列进行初始化。
接着,我们就可以看看消息发送函数是怎么样的,57static RAW_U16 internal_msg_post(RAW_QUEUE *p_q, RAW_VOID *p_void, RAW_U8 opt_send_method, RAW_U8 opt_wake_all, RAW_U32 wait_option)58 {59 RAW_U16 error_status;60 LIST *block_list_head;61 RAW_U8 block_way;6263 RAW_SR_ALLOC();6465 #if (RAW_QUEUE_FUNCTION_CHECK > 0)6667if (raw_int_nesting) {6869if (wait_option != RAW_NO_WAIT) {7071return RAW_NOT_CALLED_BY_ISR;72 }73 }7475if (p_q == 0) {7677return RAW_NULL_OBJECT;78 }7980if (p_void == 0) {8182return RAW_NULL_POINTER;83 }8485 #endif8687 block_list_head = &p_q->common_block_obj.block_list;89 RAW_CRITICAL_ENTER();9091/*queue is full condition, there should be no received task blocked on queue object!*/92if (p_q->msg_q.current_numbers >= p_q->msg_q.size) {9394if (wait_option == RAW_NO_WAIT) {95 RAW_CRITICAL_EXIT();96return RAW_MSG_MAX;97 }9899else {100101/*system is locked so task can not be blocked just return immediately*/ 102if (raw_sched_lock) {103 RAW_CRITICAL_EXIT();104return RAW_SCHED_DISABLE;105 }106/*queue is full and SEND_TO_FRONT method is not allowd*/107if (opt_send_method == SEND_TO_FRONT) {108109 RAW_CRITICAL_EXIT();110return RAW_QUEUE_FULL_OPT_ERROR;111 }112113 p_q->msg_q.blocked_send_task_numbers++;114 raw_task_active->msg = p_void;115 block_way = p_q->common_block_obj.block_way;116 p_q->common_block_obj.block_way = RAW_BLOCKED_WAY_FIFO;117/*there should be no blocked received task beacuse msg exits*/118 raw_pend_object(&p_q->common_block_obj, raw_task_active,wait_option);119 p_q->common_block_obj.block_way = block_way;120121 RAW_CRITICAL_EXIT();122123 raw_sched();124125 error_status = block_state_post_process(raw_task_active, 0);126127return error_status;128129 }131 }132133/*Queue is not full here, there should be no blocked send task*/134/*If there is no blocked receive task*/135if (is_list_empty(block_list_head)) {136137 p_q->msg_q.current_numbers++; /* Update the nbr of entries in the queue */138139if (opt_send_method == SEND_TO_END) {140141 *p_q->msg_q.write++ = p_void;142143if (p_q->msg_q.write == p_q->msg_q.queue_end) {144145 p_q->msg_q.write = p_q->msg_q.queue_start;146147 }148149 }150151else {152153if (p_q->msg_q.read == p_q->msg_q.queue_start) { 154 p_q->msg_q.read = p_q->msg_q.queue_end;155 }156157 p_q->msg_q.read--;158 *p_q->msg_q.read = p_void; /* Insert message into queue */159160 }161162 RAW_CRITICAL_EXIT();163164return RAW_SUCCESS;165 }166167/*wake all the task blocked on this queue*/168if (opt_wake_all) {169170while (!is_list_empty(block_list_head)) {171 wake_send_msg(list_entry(block_list_head->next, RAW_TASK_OBJ,task_list), p_void);172 }173174 p_q->msg_q.blocked_receive_task_numbers = 0;175 }176177/*wake hignhest priority task blocked on this queue and send msg to it*/ 178else {179180 wake_send_msg(list_entry(block_list_head->next, RAW_TASK_OBJ,task_list), p_void);181 p_q->msg_q.blocked_receive_task_numbers--;182 }183184 RAW_CRITICAL_EXIT();185186 raw_sched();187return RAW_SUCCESS;188 }189这里消息发送函数稍显冗长,这主要是因为消息发送的情况比较复杂,方方面面考虑的情况比较多。