在多任务同步的章节里面我们看到多个任务可以阻塞在同一个内核对象上。例如多个任务可以阻塞在信号量以及队列或者事件标志上。在这个一章节会阐述单个任务如何阻塞在多个内核对象上。目前raw os支持任务同时阻塞在队列(queue)或者信号量(semaphore)上。
raw os实现多对象阻塞的功能主要是通过函数通知的功能来实现的。通过此项技术能够简洁的实现单任务阻塞在多个内核对象上的功能。 函数通知的功能能够通过注册一个回调函数,使得释放信号量以及发送消息到队列上的时候调用这个注册的函数。
怎么样注册一个函数通知功能的回调函数呢?举例如下: raw_semphore_send_notify(&sem1,notify_function)通过此函数调用可以注册一个回调函数notify_function到信号量sem1上,当用户使用函数raw_semaphore_put的时候就可以触发此回调函数。
同样对于队列也有此功能。raw_queue_send_notify(&queue1, notify_function)通过此函数可以注册一个回调函数notify_function到queue1上。当用户使用函数raw_queue_end_post时会触发此注册的回调函数。 下图中演示的是一个实际中的案例:
上图(1)处任务1需要等待queue1或者queue2有消息才能继续运行。queue1或者queue2没有消息的话,任务1就阻塞住。(2)和(3)处说明queue1 或者queue2来消息了,任务1需要去接收,(4)和(5)处说明任务1接收了消息后继续运行。 下图演示的是raw os 对于上面问题的解决方式,不同的操作系统有不同的解决方式,无疑通过函数通知这种实现是最简洁的。
上图(1)处任务1阻塞在网关处,当sem1的内部计数器值为0时说明queue1和queue2都没有消息,当不为0时说明queue1或者queue2有消息。(2)和(3)处说明queue1 或者queue2来消息了,任务1需要去接收,(4)和(5)处说明任务1接收了消息后继续运行。 以上过程只是比前面一个图多了一个sem1的网关,确是解决问题的关键所在。下面总结描述下解决方案。 1 queue1 以及queue2上注册一个通知函数,通知函数的内容是发送一个信号量。 2 任务阻塞在sem1上,当queue1以及queue2获得消息时,会唤醒任务1,然后接收相应的消息。
具体的代码演示如下: queue1以及queue2上注册一个通知函数notify_queue,代码如下所示: raw_queue_send_notify(&queue1, notify_queue); raw_queue_send_notify(&queue2, notify_queue);
通知函数内容如下: void notify_queue(RAW_QUEUE *ptr) { raw_semaphore_put(&sem1); }
可以看到只要queue1和queue2里面一有消息就会触发notify_queue回调函数。回调函数会释放一个信号量,同时会打开网关sem1,进而唤醒任务1。
任务1的代码如下:
raw_semaphore_get(&sem1, RAW_WAIT_FOREVER); ret = raw_queue_receive (&queue1, RAW_NO_WAIT, (RAW_VOID**)&msg_app2); if (ret == RAW_SUCCESS) { /*Access queue1 msg*/; }
ret = raw_queue_receive (&queue2, RAW_NO_WAIT, (RAW_VOID**)&msg_app2); if (ret == RAW_SUCCESS) { /*Access queue2 msg*/; }
上述代码中可以看到任务1阻塞在sem1上,当queue1以及queue2上有消息的时候会触发notify_queue这个回调函数,回调函数内会释放一个信号量,进而唤醒任务1。需要注意的是调用raw_queue_receive时需要采用没有消息立马返回的功能,即超时参数为RAW_NO_WAIT。 以上从理论到实际演示了一个完整的任务阻塞在多内核对象上的过程,这个过程是挺复杂的,具体的验证可以上VC或者keil平台验证。
[ 本帖最后由 jorya_txj 于 2013-11-10 16:28 编辑 ]
|