第四章 同步模块
4.1 信号量(Semaphores)
SYS/BIOS在semaphores的基础上提供了一系列用于任务间同步和通信的函数。Semaphore通常用于协调访问一系列竞争任务间的共享资源。Semaphore模块提供的函数通过Semaphore_Handle类型句柄来操作semaphore对象的访问。参阅
无论是简单的(FIFO)或是优先级semaphores,Semaphore对象即可声明为计数semaphore,也可声明为二元semaphore。Semaphores可被用于task同步或互斥。相同的APIs可同时用于计数和二元semaphores。
默认情况下,semaphores是简单的计数semaphores。
计数semaphores保持一个相应可用资源内部计数的count。当count大于0,tasks在获取一个semaphore后将不再阻塞。semaphores的最大count值加1就是一个计数semaphore可协调的tasks的数量。
配置一个计数semaphore,按以下方式设置mode参数:
semParams.mode = Semaphore_Mode_COUNTING;
|
二元semaphores是可用或不可用的。它们的值不能超过1,因此,仅能用于不超过两个tasks的共享资源的协调访问。二元semaphores相对于计数semaphores来说,拥有更好的性能。
配置二元semaphores,设置mode参数如下:
semParams.mode = Semaphore_Mode_BINARY;
|
配置一个计数或二元优先级semaphore,使用以下常量之一配置mode参数:
semParams.mode = Semaphore_Mode_COUNTING_PRIORITY;
semParams.mode = Semaphore_Mode_BINARY_PRIORITY; |
注意,使用优先级semaphores会在系统中增加中断延迟,因为当tasks列表在等待扫描适当插入点时会禁止中断。这通常是每一个等待任务的十几个指令。例如,如果有10个更高优先级tasks在等待,在新task加入列表前,会禁止中断以扫描所有10个tasks。
Semaphore_create()和Semaphore_delete() 函数分别用于创建和删除semaphore对象,你也可以使用以下代码静态地创建semaphore对象:
Semaphore_Handle Semaphore_create(
Int count, Semaphore_Params *attrs Error_Block *eb ); Void Semaphore_delete(Semaphore_Handle *sem); |
semaphore在创建时初始化其count。通常count被设置为semaphore所同步的资源数量。
Semaphore_pend() 用于等待一个semaphore。如果semaphore count大于0,Semaphore_pend() 简单地减少count并返回。否则Semaphore_pend() 等待semaphore被Semaphore_post()提交。
下列代码所示的 Semaphore_pend()的timeout参数允许task等待直到超过timeout,无限期等待使用(BIOS_WAIT_FOREVER),或根本不等待用(BIOS_NO_WAIT)。Semaphore_pend()的返回值用于指示是否semaphore被成功捕获。
Bool Semaphore_pend(
Semaphore_Handle sem, UInt timeout); |
下列代码演示了Semaphore_post(),用于发送一个semaphore。如果一个task在等待semaphore,Semaphore_post()将移除semaphore队列的第一个task,并将其放入准备队列。如果没有tasks在等待,Semaphore_post()只是简单地增加semaphore count并返回。
Void Semaphore_post(Semaphore_Handle sem);
|
4.1.1 Semaphore示例
下例提供了简单代码用于三个写tasks创建唯一信息并将它们放至一个读task的列表中。写tasks调用Semaphore_post()来指示另一信息已经放入列表。读task调用Semaphore_pend()来等待信息。Semaphore_pend()仅在一个信息于列表中可用时返回。读task使用System_printf()函数打印信息。
此例中的三个写tasks,一个读task,一个semaphore和一个队列静态创建如下:
var Defaults = xdc.useModule(
'xdc.runtime.Defaults');
var Diags = xdc.useModule( 'xdc.runtime.Diags'); var Error = xdc.useModule( 'xdc.runtime.Error'); var Log = xdc.useModule( 'xdc.runtime.Log'); var LoggerBuf = xdc.useModule( 'xdc.runtime.LoggerBuf'); var Main = xdc.useModule( 'xdc.runtime.Main'); var Memory = xdc.useModule( 'xdc.runtime.Memory') var SysMin = xdc.useModule( 'xdc.runtime.SysMin'); var System = xdc.useModule( 'xdc.runtime.System'); var Text = xdc.useModule( 'xdc.runtime.Text'); var BIOS = xdc.useModule( 'ti.sysbios.BIOS'); var Clock = xdc.useModule( 'ti.sysbios.knl.Clock'); var Task = xdc.useModule( 'ti.sysbios.knl.Task'); var Semaphore = xdc.useModule( 'ti.sysbios.knl.Semaphore'); var Hwi = xdc.useModule( 'ti.sysbios.hal.Hwi'); var HeapMem = xdc.useModule( 'ti.sysbios.heaps.HeapMem'); /* set heap and stack sizes */ BIOS.heapSize = 0x2000; Program.stack = 0x1000; SysMin.bufSize = 0x400; /* set library type */ BIOS.libType = BIOS.LibType_Custom; /* Set logger for the whole system */ var loggerBufParams = new LoggerBuf.Params(); loggerBufParams.numEntries = 32; var logger0 = LoggerBuf.create(loggerBufParams); Defaults.common$.logger = logger0; Main.common$.diags_INFO = Diags.ALWAYS_ON; /* Use Semaphore, and Task modules and set global properties */ var Semaphore = xdc.useModule( 'ti.sysbios.knl.Semaphore'); Program.global.sem = Semaphore.create( 0); var Task = xdc.useModule( 'ti.sysbios.knl.Task'); Task.defaultStackSize = 512; Task.idleTaskVitalTaskFlag = false; /* Statically create reader and writer Tasks */ var reader = Task.create( '&reader'); reader.priority = 5; var writer0 = Task.create( '&writer'); writer0.priority = 3; writer0.arg0 = 0; var writer1 = Task.create( '&writer'); writer1.priority = 3; writer1.arg0 = 1; var writer2 = Task.create( '&writer'); writer2.priority = 3; writer2.arg0 = 2; /* uses Queue module and create two instances statically */ var Queue = xdc.useModule( 'ti.sysbios.knl.Queue'); Program.global.msgQueue = Queue.create(); Program.global.freeQueue = Queue.create(); |
C代码如下:
/* ======== semtest.c ======== */
#include <xdc/std.h> #include <xdc/runtime/Memory.h> #include <xdc/runtime/System.h> #include <xdc/runtime/Error.h> #include <ti/sysbios/BIOS.h> #include <ti/sysbios/knl/Semaphore.h> #include <ti/sysbios/knl/Task.h> #include <ti/sysbios/knl/Queue.h> #define NUMMSGS 3 /* number of messages */ #define NUMWRITERS 3 /* number of writer tasks created with */ /* Config Tool */ typedef struct MsgObj { Queue_Elem elem; /* first field for Queue */ Int id; /* writer task id */ Char val; /* message value */ } MsgObj, *Msg; Void reader(); Void writer(); /* The following objects are created statically. */ extern Semaphore_Handle sem; extern Queue_Handle msgQueue; extern Queue_Handle freeQueue; /* ======== main ======== */ Int main(Int argc, Char *argv[]) { Int i; MsgObj *msg; Error_Block eb; Error_init(&eb); //这里注意,一个msg可容纳三个MsgObj结构体 msg = (MsgObj *) Memory_alloc( NULL, NUMMSGS * sizeof(MsgObj), 0, &eb); if (msg == NULL) { System_abort( "Memory allocation failed"); } /* 把三个空消息msg压入freeQueue队列,则队列里总共可容纳9个MsgObj结构体 */ for (i = 0; i < NUMMSGS; msg++, i++) { Queue_put(freeQueue, (Queue_Elem *) msg); } BIOS_start(); return( 0); } /* ======== reader ======== */ Void reader() { Msg msg; Int i; //循环9次读取msgQueue里的信息,每等到一个信号量读取一次 for (i = 0; i < NUMMSGS * NUMWRITERS; i++) { /* Wait for semaphore to be posted by writer(). */ Semaphore_pend(sem, BIOS_WAIT_FOREVER); /* get message */ msg = Queue_get(msgQueue); /* print value */ System_printf( "read '%c' from (%d).\n", msg->val, msg->id); /* free msg 压回去的意义何在?*/ Queue_put(freeQueue, (Queue_Elem *) msg); } System_printf( "reader done.\n"); } /* ======== writer ======== */ Void writer(Int id) { Msg msg; Int i; for (i = 0; i < NUMMSGS; i++) { /* 从freeQueue里出队一条信息,改造后压入msgQueue,然后 * 发信号量让reader接手处理信息,总共三条信息 */ msg = Queue_get(freeQueue); /* fill in value */ msg->id = id; msg->val = (i & 0xf) + 'a'; System_printf( "(%d) writing '%c' ...\n", id, msg->val); /* put message */ Queue_put(msgQueue, (Queue_Elem *) msg); /* post semaphore */ Semaphore_post(sem); } System_printf( "writer (%d) done.\n", id); } |
运行结果如下
[Cortex_M3_0] (
0) writing
'a' ...
read 'a' from ( 0). ( 0) writing 'b' ... read 'b' from ( 0). ( 0) writing 'c' ... read 'c' from ( 0). writer ( 0) done. ( 1) writing 'a' ... read 'a' from ( 1). ( 1) writing 'b' ... read 'b' from ( 1). ( 1) writing 'c' ... read 'c' from ( 1). writer ( 1) done. ( 2) writing 'a' ... read 'a' from ( 2). ( 2) writing 'b' ... read 'b' from ( 2). ( 2) writing 'c' ... read 'c' from ( 2). reader done. writer ( 2) done. |
此例一共三个writer,一个reader。writer的优先级列低,每次它向队列压一条信息然后发信号量,让reader接收处理信息,一共压三条信息后结束。由于有三个writer,所以最后处理了9条信息。Queue是队列,尾进头出,这个简单的数据结构的知识我就不哆嗦了。
个人感觉这个例子写得不是太好,为何要用两个队列来完成这项功能,我看了半天还是无法完全理解作者意图。