参考:

  1. 【野火】物联网操作系统 LiteOS 开发实战指南
  2. Huawei LiteOS | 中文网

4. 信号量

4.1 基本概念

  • 信号量(semaphore):是一种实现任务间通信的机制,实现任务之间同步或临界资源的互斥访问,常用用于协助一组相互竞争的任务来访问临界资源。

    • 有点起到类似于裸机编程的标志量
  • 功能:在多任务系统中,各个任务之间需要同步互斥实现临界资源的保护

  • 实现方式:信号量是一个非负整数,所有获取它的任务都会将该整数减一(获取它为了使用资源),当该整数值为0时,所有试图获取它的任务都将处于阻塞状态。

    • 通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数
  • 常见信号量

    • 二值信号量
    • 计数信号量

4.2 信号量的运作机制

  • 信号量的初始化:为配置的 N 个信号量申请内存(N值可以由用户自行配置,受内存限制),并把所有的信号量初始化成未使用,并加入到未使用链表(g_stUnusedSemList)中供系统使用

  • 信号量的创建:从未使用的信号量链表中获取一个信号量资源,并设定初值

  • 信号量申请:若其计数器值大于 0,则直接减 1 返回成功。否则任务阻塞,等待其它任务释放该信号量,等待的超时时间可设定。当任务被一个信号量阻塞时,将该任务挂到信号量等待任务队列的队尾

  • 信号量释放:若没有任务等待该信号量,则直接将计数器加1返回。否则唤醒该信号量等待任务队列上的第一个任务

  • 信号量删除:将正在使用的信号量置为未使用信号量,并挂回到未使用链表

  • 运作图如下所示:

    在这里插入图片描述

4.3 二值信号量

4.3.1 基本定义
  • 只有 0 (资源被获取)和 1(资源被释放) 两种情况的信号量称之为二值信号量
4.3.2 应用场景
  • 临界资源访问(使用互斥信号量包含优先级继承机制)更多)
  • 同步
    • 任务与任务间同步,如获取传感器数据任务与液晶屏幕刷新任务
    • 任务与中断同步,如网络信息的接收处理
  • 互斥与同步为目的的信号量不同
    • 用作互斥时,着眼于可用资源的有和无,占线的作用,即信号量创建后,信号量中可用信号量个数是1,在需要使用临界资源时,获取信号量,使其变空,这样其他任务获取不到该信号量而阻塞,保证临界资源的的安全
    • 用作同步时,着眼于任务执行条件的因果性,起等待条件的作用,即信号量创建后被置位空,任务1取信号量而阻塞,任务2在某种条件发生后,释放信号量,于是任务1得以进入就绪态或运行态(如果就绪任务的优先级是最高的),从而达到两个任务间的同步
4.3.3 机制简介
  • 用作同步时,信号量在创建后置为空,任务1获取信号量而进入阻塞,任务2在某种条件发生后,释放信号量,于是任务1获取信号量(如果优先级最高)得以进入就绪态,从而达到两个任务间的同步。

  • 值得注意的是:中断服务函数中释放信号量,任务1也会得到信号量,从而达到任务与中断间的同步,实现了裸机编程中轮询标志位的方式,但是实时性更高,效率也更高。

  • 任务执行完毕不需要归还信号量

  • 具体详情:

    • 信号量初始化,为配置的N个信号量申请内存,并把所有的信号量初始化成未使用,并加入到信号量未使用链表g_stUnusedSemList)中供系统使用

    • 信号量创建,从未使用的信号量链表中获取一个信号量资源,该信号量的最大数值为1OS_SEM_BINARY_MAX_COUNT),也就是二值信号量

      • 在创建二值信号量的时候,该二值信号量是否有效个数由用户自己定义
    • 二值信号量的获取

      • 任何任务都可以从已创建的二值信号量资源中获取一个二值信号量

      • 若当前信号量有效,获取成功,并返回正确

      • 若当前信号量无效,任务根据用户指定的阻塞超时时间来等待其他任务/中断释放信号量,等待过程中任务变为阻塞状态,任务将被挂到该信号量的阻塞等待列表中
        在这里插入图片描述

      • 当某个时间点中断或者另一个任务因某种条件,释放了一个二值信号量,那么之前获取无效信号量而处于阻塞状态的任务将从阻塞态解除,变为就绪态。
        在这里插入图片描述

    • 整个二值信号量的运作机制如下图所示:

    在这里插入图片描述

4.4 计数信号量

4.4.1 定义
  • 用于计数的信号量称之为计数信号量
4.4.1 应用场景
  • 事件计数,计数值表示还有多少个事件没有被处理
  • 资源管理,计数值表示系统中可用资源的数目(使用完资源后,必须归还信号量)
4.4.2 运作机制
  • 用于资源管理时:
    • 允许多个任务获取信号量访问共享资源,但会限制任务的最大数目
    • 当访问的任务数达到可支持的最大数目时,会阻塞其他试图获取该信号量的任务,直到有任务释放了信号量
  • 下图展示出计数信号量用于公共资源管理时的运作机制
    在这里插入图片描述

4.5 信号量的使用

4.5.1 信号量控制块
typedef struct
{
    UINT16           usSemStat;         /**是否使用标志位*/
    UINT16          uwSemCount;         /**信号量索引号*/
    UINT16          usMaxSemCount;      /**信号量最大数,二值信号量最大为1,计数信号量最大0xFFFF*/
    UINT16          usSemID;            /**信号量控制ID*/
    LOS_DL_LIST     stSemList;          /**信号量阻塞列表,用于记录正在等待信号量的任务*/
} SEM_CB_S;  
4.5.2 信号量功能函数
功能分类 接口名 描述
信号量的创建和删除 LOS_SemCreate 创建计数型信号量
LOS_BinarySemCreate 创建二进制信号量
LOS_SemDelete 删除指定的信号量
信号量的申请和释放 LOS_SemPend 申请指定的信号量
LOS_SemPost 释放指定的信号量
  • 二值信号量创建函数LOS_BinarySemCreate()

    UINT32 LOS_BinarySemCreate (UINT16 usCount,			/* 信号量可用个数[0,1] */
                                UINT32 *puwSemHandle)   /* 信号量ID */
    
    • 在创建二值信号量的时候,我们只需要传入二值信号量ID与初始化可用信号量个数即可,二值信号量的最大容量为OS_SEM_BINARY_MAX_COUNT ,也就是1
    • 由于是二值信号量,我们刚创建的时候可以让信号量有效,可以在第一次就可以获取到信号量,传入信号量的可用个数为1,在一些不需要立即获取到信号量的时候可以将信号量的可用个数设置为0
  • 计数型信号量创建函数LOS_SemCreate()

    UINT32 LOS_SemCreate (UINT16 usCount,			/* 信号量可用个数[0,1] */
                          UINT32 *puwSemHandle)     /* 信号量ID */
    
    • 计数型信号量的最大容量则为 OS_SEM_COUNTING_MAX_COUNT,也就是 0xFFFF ,计数型信号量的创建可以默认初始化有效的信号量个数为0~0xFFFF
  • 信号量删除函数LOS_SemDelete()

    UINT32 LOS_SemDelete(UINT32 uwSemHandle)  /* 信号ID */
    
    • 信号量删除函数式根据信号量ID直接删除的
    • 信号量在使用或者有任务在阻塞中等待该信号量的时候是不能删除的
  • 信号量释放函数LOS_SemPost()

    UINT32 LOS_SemPost(UINT32 uwSemHandle)  /* 信号ID */
    
    • 使信号量变得有效的方式:
      • 创建信号量时,初始化,设置一个可用的信号量个数
      • 调用信号量释放函数LOS_SemPost()
    • 可以在任务中、中断中使用
    • 不能一直释放信号量,需要受可用信号量范围的限制
  • 信号量获取函数LOS_SemPend()

UINT32 LOS_SemPend(UINT32 uwSemHandle,	/* 信号量ID */
                   UINT32 uwTimeout)	/* 等待时间 */
  • 当信号量有效的时候,任务才能获取信号量
  • 每次调用一次LOS_SemPend()函数申请信号量的时候,信号量的可用个数便减少一个,直至为0
  • 如果信号量无效时,某任务调用该函数,则该任务便会进入阻塞状态
  • 阻塞时间由用户指定
  • 不允许在中断的上下文环境中获取信号量
  • 例子如下
static void Read_Task(void)
{
    while (1) {
        //获取二值信号量 xSemaphore,没获取到则一直等待
        LOS_SemPend( BinarySem_Handle , LOS_WAIT_FOREVER );
        if ( ucValue [ 0 ] == ucValue [ 1 ] ) {
        	printf ( "\r\nSuccessful\r\n" );
        } else {
        	printf ( "\r\nFail\r\n" );
        }
        LOS_SemPost( BinarySem_Handle ); //给出二值信号量 xSemaphore
        LOS_TaskDelay ( 1000 ); //每 1s 读一次,延时 1000 个 tick
    }
}
  • 信号量申请有三种模式:
    • 无阻塞模式:任务需要申请信号量,若当前信号量的任务数米有到达信号量设定的上限,则申请成功,否则,立即返回,申请失败
    • 永久阻塞模式:任务申请信号量,若当前信号量的任务数没有到达信号量设定的上限,则申请成功,否则,该任务进入阻塞状态,系统切换到其他任务执行,直到其他任务释放该信号量,阻塞任务才会重新得以执行
    • 定时阻塞模式:任务需要申请信号量,若当前信号量的任务数没有到信号量设定的上限,则申请成功。否则,该任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,指定时间超时前有其他任务释放该信号量,或者用户指定时间超时后,阻塞任务才会重新得以执行
Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐