4.4 Mailboxes(邮箱)
ti.sysbios.knl.Mailbox模块提供了一系列函数管理mailboxes。Mailboxes可用于在相同处理器的两个task中传递缓冲。一个Mailbox实例可用于多个readers和writers。
Mailbox模块拷贝缓冲到合适尺寸的内部缓冲。这些缓冲的尺寸和个数在Mailbox实例创建时指定。拷贝在通过Mailbox_post()发送缓冲时建立。另外一份拷贝在通过Mailbox_pend()接收缓冲时建立。
Mailbox_create()和Mailbox_delete()分别用于创建和删除mailboxes。你也可以静态地创建mailbox对象。Mailbox可用于确保输入缓冲流不会超过系统处理那些缓冲的能力。本节稍后给出的例子演示了这种设计。
在创建mailbox时,你可以为每个缓冲指定其尺寸以及内部mailbox缓冲的个数。在创建Mailbox时确定尺寸之后,所有Mailbox的发送和接收缓冲必须使用这个相同的尺寸。
Mailbox_Handle Mailbox_create(SizeT bufsize,
UInt numBufs, Mailbox_Params *params, Error_Block *eb) Void Mailbox_delete(Mailbox_Handle *handle); |
Mailbox_pend()用于从mailbox中读取一个缓冲。如果无缓冲可用(也就是mailbox是空的),Mailbox_pend()阻塞。timeout参数为允许task等待的时间,无限期等待(BIOS_WAIT_FOREVER),或根本不等待(BIOS_NO_WAIT)。时间单位为系统时钟ticks。
Bool Mailbox_pend(Mailbox_Handle handle,
Ptr buf, UInt timeout); |
Mailbox_post()用于提交缓冲给mailbox。如果无缓冲槽可用(也就是mailbox满了),Mailbox_post()阻塞。timeout参数为允许task等待的时间,无限期等待(BIOS_WAIT_FOREVER),或根本不等待(BIOS_NO_WAIT)。
Bool Mailbox_post(Mailbox_Handle handle,
Ptr buf, UInt timeout); |
Mailbox提供了配置参数允许你将事件关联到mailbox。这使得你可以在同一时间可等待mailbox消息和其它事件。Mailbox提供两个配置参数以支持mailbox的reader的事件----readerEvent和readerEventId。这使得mailbox reader可以使用事件对象去等待mailbox消息。Mailbox还提供了两个配置参数用于mailbox writer---writerEvent和writerEventId。这使得mailbox writer可使用事件对象等待mailbox的room
(room可理解为缓冲槽)。
注意,这些事件句柄的名称可能会有误导性。readerEvent是Mailbox reader请求的事件,但它是在Mailbox_post()调用中被Mailbox writer提交的。writeEvent是Mailbox writer请求等待Mailbox有空位的事件,所以在Mailbox不满时它可以成功执行Mailbox_post()。无论何时,只要Mailbox成功读取,Mailbox reader都会提交writerEvent(也就是说Mailbox_pend()返回TRUE)。
当使用事件时,一个线程调用Event_pend()并等待数个事件。在从Event_pend()返回的时候,线程必须调用Mailbox_pend()或Mailbox_post()---取决于它是reader还是writer---timeout参数为BIOS_NO_WAIT。参考4.2.1节的代码实例,它包含返回Event_pend()后的相应的Mailbox资源。
4.5 Queues(队列)
ti.sysbios.knl.Queue模块提供了对创建对象列表的支持。队列实现为双向链表,所以元素在列表中可以插入和删除,另外,队列没最大尺寸限制。
4.5.1 基础FIFO(先进先出)队列操作
添加一个结构体进入队列,第一个字段需要是Queue_Elem类型。下例显示了一个可被加入Queue的结构体。
队列有一个“头”,在列表的最前端。Queue_enqueue()将元素添加到列表未尾,Queue_dequeue()移除并返回列表前端元素,这些函数支持队列的FIFO操作。
运行时例子:下例演示了基于队列的操作---Queue_enqueue()和Queue_dequeue()。它也使用了Queue_empty()函数,当队列中没有元素时返回true。
/* This structure can be added to a Queue because the first field is a Queue_Elem. */
typedef struct Rec { Queue_Elem elem; Int data; } Rec; Queue_Handle myQ; Rec r1, r2; Rec *rp; r1.data = 100; r2.data = 200; // No parameters or Error block are needed to create a Queue. myQ = Queue_create( NULL, NULL); // Add r1 and r2 to the back of myQ. Queue_enqueue(myQ, &(r1.elem)); Queue_enqueue(myQ, &(r2.elem)); // Dequeue the records and print their data while (!Queue_empty(myQ)) { // Implicit cast from (Queue_Elem *) to (Rec *) rp = Queue_dequeue(myQ); System_printf( "rec: %d\n", rp->data); } |
此例打印结果如下:
rec:
100
rec: 200 |
4.5.2 队列的迭代
队列模块还提供了几个APIs用于循环访问队列。Queue_head()返回队列的前端元素且不移除它,Queue_next()和Queue_prev()分别返回队列中的下一个和前一个元素。
运行时实例:以下代码演示了一种遍历队列的方法。此例中,“myQ”是一个Queue_Handle。
Queue_Elem *elem;
for (elem = Queue_head(myQ); elem != (Queue_Elem *)myQ; elem = Queue_next(elem)) { ... } |
4.5.3 插入和移除队列元素
使用Queue_insert()和Queue_remove()可以在队列中的任何位置插入或移除元素。Queue_insert()在指定元素前插入一个元素,Queue_remove()删除一个指定元素,无论它在队列中的哪个位置。注意,队列不提供任何APIs用于在给定索引入插入或移除元素。
运行时例子:下例演示了Queue_insert()和Queue_remove()。
Queue_enqueue(myQ, &(r1.elem));
/* Insert r2 in front of r1 in the Queue. */ Queue_insert(&(r1.elem), &(r2.elem)); /* Remove r1 from the Queue. Note that Queue_remove() does not * require a handle to myQ. */ Queue_remove(&(r1.elem)); |
4.5.4 原子队列操作
队列通常在系统中通常由多个线程共享,这可能导致不同线程同时更改队列,这将破坏队列。上述讨论的队列APIs不保护这些。然而,队列提供了两个原子APIs,它可以在操作队列前禁用中断。这些APIs是Queue_get(),它是Queue_dequeue()和Queue_put()的原子操作版本,也是Queue_enqueue()的原子版本。