【SYS/BIOS】4.1节同步模块之信号量

第4章 同步模块

本章介绍克用于同步访问共享资源的模块
目录列表如下:

  • 4.1 信号量(Semaphores)

  • 4.2 事件模块(Event Module)

  • 4.3门(Gates)

  • 4.4 邮箱(Mailboxes)

  • 4.5 队列(Queues)

这一篇介绍4.1节,信号量的内容。

4.1 信号量(Semaphores)

SYS/BIOS为基于信号量的任务间同步和通信提供了基本的函数集。信号量通常用于在一组相互竞争的任务之间协调对共享资源的访问。信号量模块提供了一些函数,可以通过操作类型为Semaphore_Handle的句柄来访问信号量对象,如果想了解概述请参阅介绍信号量的视频。

信号量对象可以声明为计数信号量或二进制信号量,也可以声明为简单FIFO或优先级感知信号量。信号量可用于任务同步和互斥。计数和二进制信号量都使用相同的API。

默认情况下,信号量是简单的计数信号量。

计数信号量保持对相应可用资源数量的内部计数,当计数大于0时,任务在获取信号量时不会阻塞。信号量的计数值仅受16位计数器的大小限制。如果已禁用断言,则如果计数器从最大16位值递增,则计数将在不通知的情况下滚动。

要配置计数信号量,请按如下所示设置mode参数:

semParams.mode = Semaphore_Mode_COUNTING;

二进制信号量要么可用,要么不可用。其值的增量不能超过1。因此,它们应该用于协调最多两个任务对共享资源的访问。二进制信号量比计数信号量提供更好的性能。

要配置二进制信号量,请按如下所示设置mode参数:

semParams.mode = Semaphore_Mode_BINARY;

任务以FIFO顺序等待简单的计数和二进制信号量,而不考虑任务的优先级。或者,您可以创建“优先级”信号量,在优先级较低的第一个任务之前将挂起的任务插入等待列表。因此,优先级相同的任务按FIFO顺序等待,但优先级较高的任务在优先级较低的任务之前就准备好了。

要配置计数或二进制优先级信号量,请使用以下常量之一设置模式参数:

semParams.mode = Semaphore_Mode_COUNTING_PRIORITY;
semParams.mode = Semaphore_Mode_BINARY_PRIORITY;

请注意,使用优先级信号量可能会增加系统中的中断延迟,因为在扫描等待该信号量的任务列表以寻找正确的插入点时,中断会被禁用。这通常时每个等待任务的十几条指令。例如,如果有10个优先级更高的任务等待,那么在将新任务输入列表之前,所有10个任务都将在禁用中断的情况下进行检查。

有关模块实例的参数结构和单个参数的信息,请参阅第1.6.1节中描述的API参考帮助系统。

函数Semaphore_create() 和 Semaphore_delete() 用于创建和删除信号量对象,分别如例4-1所示。

例4-1 创建和删除1个信号量

Semaphore_Handle Semaphore_create(Int count, Semaphore_Params   *attrs, Error_Block *eb );
Void Semaphore_delete(Semaphore_Handle *sem);

信号量计数在创建时初始化为计数。通常情况下,count设置为信号量正在同步的资源数。

Semaphore_pend()等待信号量。如果信号量计数大于0,则信号量Semaphore_pend()会简单地减少计数并返回。否则Semaphore_pend()将等待Semaphore_post()发布信号量。

如例4-2所示,Semaphore_pend() 的timeout参数运行任务等待到超时、无限期等待(BIOS_WAIT_FOREVER)或根本不等待(BIOS_NO_WAIT)。

Semaphore_pend() 的返回值用于指示是否成功获取了信号量。

例4-2 使用Semaphore_pend()设置超时

Bool Semaphore_pend(Semaphore_Handle   sem, UInt   timeout);

例4-3显示了Semaphore_post(),这是用了发信号的。如果一个任务正在等待信号量,Semaphore_post()会从信号量队列中删除第一个任务,并将其放入就绪队列。如果没有任务等待,Semaphore_post()只需增加信号量计数并返回。

例4-3 用Semaphore_post()发信号

Void Semaphore_post(Semaphore_Handle sem);

调用Semaphore_post()可能会导致在特定情况下重新启用硬件中断。有关启用和禁用中断的详细信息,请参见第8.2.4节。

4.1.1 信号量示例

示例4-4提供了3个writer任务的示例代码,这些任务创建独特的信息,并将它们放在一个reader任务的列表中。Writer任务调用Semaphore_post()来指示列表中已经有另一条消息。Reader任务调用Semaphore_pend()来等待消息。

Semaphore_pend()仅在列表中有消息时返回。Reader任务使用System_printf()函数打印消息。

本示例程序中的三个writer任务、一个reader任务、一个信号量和一个队列是静态创建的,如下所示:

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.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();

由于程序使用多个任务,因此使用计数信号来同步对列表的访问。图4-1提供了示例4-3的结果视图。虽然这三个writer任务是先安排的,但消息一被放入队列就被读取,因为reader任务的优先级高于writer的任务优先级。

示例4-4使用三个writer任务的信号量示例

/* ======== 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 *) Memory_alloc(NULL, NUMMSGS * sizeof(MsgObj), 0, &eb);
    if (msg == NULL) {
        System_abort("Memory allocation failed");
    }
    
    /* Put all messages on freeQueue */
    for (i = 0; i < NUMMSGS; msg++, i++) {
        Queue_put(freeQueue, (Queue_Elem *) msg);
    }
    BIOS_start();
    return(0);
}
/* ======== reader ======== */
Void reader()
{
    Msg msg;
    Int i;
    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++) {
        /* Get msg from the free list. Since reader is higher
         * priority and only blocks on sem, list is never
         * empty. */
        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);
}

图4-1 例4-4的跟踪窗结果

(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.

以上内容翻译自TI官方文档 TI-RTOS Kernel (SYS/BIOS) User’s Guide Literature Number: SPRUEX3U February 2018

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值