liteos信号量(八)
1. 概述
1.1 基本概念
信号量(Semaphore)是一种实现任务间通信的机制,实现任务之间同步或临界资源的互斥访问。常用于协助一组相互竞争的任务来访问临界资源。
在多任务系统中,各任务之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。
通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数。其值的含义分两种情况:
- 0,表示没有积累下来的Post操作,且有可能有在此信号量上阻塞的任务。
- 正值,表示有一个或多个Post下来的释放操作。
以同步为目的的信号量和以互斥为目的的信号量在使用有如下不同:
- 用作互斥时,信号量创建后记数是满的,在需要使用临界资源时,先取信号量,使其变空,这样其他任务需要使用临界资源时就会因为无法取到信号量而阻塞,从而保证了临界资源的安全。
- 用作同步时,信号量在创建后被置为空,任务1取信号量而阻塞,任务2在某种条件发生后,释放信号量,于是任务1得以进入READY或RUNNING态,从而达到了两个任务间的同步。
1.2 运作机制
1.2.1 信号量控制块
/**
* @ingroup los_sem
* Semaphore control structure.
*/
typedef struct
{
UINT8 usSemStat; /**是否使用标志位*/
UINT16 uwSemCount; /**信号量索引号*/
UINT32 usSemID; /**信号量计数*/
LOS_DL_LIST stSemList; /**挂接阻塞于该信号量的任务*/
}SEM_CB_S;
1.2.2 信号量运作原理
信号量初始化,为配置的N个信号量申请内存(N值可以由用户自行配置,受内存限制,详见第十章 配置参考),并把所有的信号量初始化成未使用,并加入到未使用链表中供系统使用。
信号量创建,从未使用的信号量链表中获取一个信号量资源,并设定初值。
信号量申请,若其计数器值大于0,则直接减1返回成功。否则任务阻塞,等待其它任务释放该信号量,等待的超时时间可设定。当任务被一个信号量阻塞时,将该任务挂到信号量等待任务队列的队尾。
信号量释放,若没有任务等待该信号量,则直接将计数器加1返回。否则唤醒该信号量等待任务队列上的第一个任务。
信号量删除,将正在使用的信号量置为未使用信号量,并挂回到未使用链表。
信号量允许多个任务在同一时刻访问同一资源,但会限制同一时刻访问此资源的最大任务数目。访问同一资源的任务数达到该资源的最大数量时,会阻塞其他试图获取该资源的任务,直到有任务释放该信号量。
2. 开发指导
2.1 使用场景
信号量是一种非常灵活的同步方式,可以运用在多种场合中,实现锁、同步、资源计数等功能,也能方便的用于任务与任务,中断与任务的同步中。
2.2 功能
Huawei LiteOS 系统中的信号量模块为用户提供下面几种功能。
功能分类 | 接口名 | 描述 |
---|---|---|
信号量的创建和删除 | LOS_SemCreate | 创建信号量 |
- | LOS_SemDelete | 删除指定的信号量 |
信号量的申请和释放 | LOS_SemPend | 申请指定的信号量 |
- | LOS_SemPost | 释放指定的信号量 |
2.3 开发流程
信号量的开发典型流程:
- 创建信号量LOS_SemCreate。
- 申请信号量LOS_SemPend。
信号量有三种申请模式:无阻塞模式、永久阻塞模式、定时阻塞模式
- 无阻塞模式:任务需要申请信号量,若当前信号量的任务数没有到信号量设定的上限,则申请成功。否则,立即返回申请失败
- 永久阻塞模式:任务需要申请信号量,若当前信号量的任务数没有到信号量设定的上限,则申请成功。否则,该任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,直到有其他任务释放该信号量,阻塞任务才会重新得以执行
- 定时阻塞模式:任务需要申请信号量,若当前信号量的任务数没有到信号量设定的上限,则申请成功。否则,该任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,指定时间超时前有其他任务释放该信号量,或者用户指定时间超时后,阻塞任务才会重新得以执行
- 释放信号量LOS_SemPost。
- 如果有任务阻塞于指定信号量,则唤醒该信号量阻塞队列上的第一个任务。该任务进入就绪态,并进行调度
- 如果没有任务阻塞于指定信号量,释放信号量成功
- 删除信号量LOS_SemDelet
2.4 信号量错误码
对可能导致信号量操作失败的情况,包括创建信号量、申请信号量、释放信号量、删除信号量等,均需要返回对应的错误码,以便快速定位错误原因。
序 号 | 定义 | 实际数值 | 描述 | 参考解决方案 |
---|---|---|---|---|
1 | LOS_ERRNO_SEM_NO_MEMORY | 0x02000700 | 内存空间不足 | 分配更大的内存分区 |
2 | LOS_ERRNO_SEM_INVALID | 0x02000701 | 非法传参 | 改变传数为合法值 |
3 | LOS_ERRNO_SEM_PTR_NULL | 0x02000702 | 传入空指针 | 传入合法指针 |
4 | LOS_ERRNO_SEM_ALL_BUSY | 0x02000703 | 信号量控制块不可用 | 释放资源信号量资源 |
5 | LOS_ERRNO_SEM_UNAVAILABLE | 0x02000704 | 定时时间非法 | 传入正确的定时时间 |
6 | LOS_ERRNO_SEM_PEND_INTERR | 0x02000705 | 中断期间非法调用LOS_SemPend | 中断期间禁止调用LOS_SemPend |
7 | LOS_ERRNO_SEM_PEND_IN_LOCK | 0x02000706 | 任务被锁,无法获得信号量 | 在任务被锁时,不能调用LOS_SemPend |
8 | LOS_ERRNO_SEM_TIMEOUT | 0x02000707 | 获取信号量时间超时 | 将时间设置在合理范围内 |
错误码定义:错误码是一个32位的存储单元, 31~24位表示错误等级, 23~16位表示错误码标志, 15~8位代表错误码所属模块, 7~0位表示错误码序号,如下
#define LOS_ERRNO_OS_NORMAL(MID,ERRNO) \
(LOS_ERRTYPE_NORMAL | LOS_ERRNO_OS_ID | ((UINT32)(MID) << 8) | (ERRNO))
LOS_ERRTYPE_NORMAL :Define the error level as critical
LOS_ERRNO_OS_ID :OS error code flag.
MID:OS_MOUDLE_ID
ERRNO:error ID number
例如:
LOS_ERRNO_SEM_NO_MEMORY LOS_ERRNO_OS_ERROR(LOS_MOD_SEM, 0x00))
2.5 平台差异性
2.6 注意事项
- 由于中断不能被阻塞,因此在申请信号量时,阻塞模式不能在中断中使用。
3. 编程实例
3.1 实例描述
本实例实现如下功能;
- 测试任务Example_TaskEntry创建一个信号量,锁任务调度,创建两个任务Example_SemTask1、 Example_SemTask2,Example_SemTask2优先级高于Example_SemTask1,两个任务中申请同一信号量,解锁任务调度后两任务阻塞,测试任务Example_TaskEntry释放信号量。
- Example_SemTask2得到信号量,被调度,然后任务休眠20Tick,Example_SemTask2延迟, Example_SemTask1被唤醒。
- Example_SemTask1定时阻塞模式申请信号量,等待时间为10Tick,因信号量仍被Example_SemTask2持有, Example_SemTask1挂起, 10Tick后仍未得到信号量,Example_SemTask1被唤醒,试图以永久阻塞模式申请信号量, Example_SemTask1挂起。
- 20Tick后Example_SemTask2唤醒, 释放信号量后, Example_SemTask1得到信号量被调度运行,最后释放信号量。
- Example_SemTask1执行完, 40Tick后任务Example_TaskEntry被唤醒,执行删除信号量,删除两个任务。
3.2 编程示例
前提条件:
- 在los_config.h中,将LOSCFG_BASE_IPC_SEM配置为YES。
- 配置用户定义的LOSCFG_BASE_IPC_SEM_LIMIT最大的信号量数,如1024。
代码实现如下
#include "los_sem.h"
/*任务PID*/
static UINT32 g_TestTaskID01,g_TestTaskID02;
/*测试任务优先级*/
#define TASK_PRIO_TEST 5
/*信号量结构体ID*/
static SEM_HANDLE_T g_usSemID;
VOID Example_SemTask1(void)
{
UINT32 uwRet;
printf("Example_SemTask1 try get sem g_usSemID ,timeout 10 ticks.\n");
/*定时阻塞模式申请信号量,定时时间为10Tick*/
uwRet = LOS_SemPend(g_usSemID, 10);
/*申请到信号量*/
if(LOS_OK == uwRet)
{
LOS_SemPost(g_usSemID);
return;
}
/*定时时间到,未申请到信号量*/
if(LOS_ERRNO_SEM_TIMEOUT == uwRet)
{
printf("Example_SemTask1 timeout and try get sem g_usSemID wait forever.\n");
/*永久阻塞模式申请信号量*/
uwRet = LOS_SemPend(g_usSemID, LOS_WAIT_FOREVER);
printf("Example_SemTask1 wait_forever and get sem g_usSemID .\n");
if(LOS_OK == uwRet)
{
LOS_SemPost(g_usSemID);
return;
}
}
return;
}
VOID Example_SemTask2(void)
{
UINT32 uwRet;
printf("Example_SemTask2 try get sem g_usSemID wait forever.\n");
/*永久阻塞模式申请信号量*/
uwRet = LOS_SemPend(g_usSemID, LOS_WAIT_FOREVER);
if(LOS_OK == uwRet)
printf("Example_SemTask2 get sem g_usSemID and then delay 20ticks .\n");
/*任务休眠20 Tick*/
LOS_TaskDelay(20);
printf("Example_SemTask2 post sem g_usSemID .\n");
/*释放信号量*/
LOS_SemPost(g_usSemID);
return;
}
UINT32 Example_TaskEntry()
{
UINT32 uwRet;
TSK_INIT_PARAM_S stTask1;
TSK_INIT_PARAM_S stTask2;
/*创建信号量*/
LOS_SemCreate(0,&g_usSemID);
/*锁任务调度*/
LOS_TaskLock();
/*创建任务1*/
memset(&stTask1, 0, sizeof(TSK_INIT_PARAM_S));
stTask1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_SemTask1;
stTask1.pcName = "MutexTsk1";
stTask1.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
stTask1.usTaskPrio = TASK_PRIO_TEST;
uwRet = LOS_TaskCreate(&g_TestTaskID01, &stTask1);
if(uwRet != LOS_OK)
{
printf("task1 create failed .\n");
return LOS_NOK;
}
/*创建任务2*/
memset(&stTask2, 0, sizeof(TSK_INIT_PARAM_S));
stTask2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_SemTask2;
stTask2.pcName = "MutexTsk2";
stTask2.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
stTask2.usTaskPrio = (TASK_PRIO_TEST - 1);
uwRet = LOS_TaskCreate(&g_TestTaskID02, &stTask2);
if(uwRet != LOS_OK)
{
printf("task2 create failed .\n");
return LOS_NOK;
}
/*解锁任务调度*/
LOS_TaskUnlock();
uwRet = LOS_SemPost(g_usSemID);
/*任务休眠40 Tick*/
LOS_TaskDelay(40);
/*删除信号量*/
LOS_SemDelete(g_usSemID);
/*删除任务1*/
uwRet = LOS_TaskDelete(g_TestTaskID01);
if(uwRet != LOS_OK)
{
printf("task1 delete failed .\n");
return LOS_NOK;
}
/*删除任务2*/
uwRet = LOS_TaskDelete(g_TestTaskID02);
if(uwRet != LOS_OK)
{
printf("task2 delete failed .\n");
return LOS_NOK;
}
return LOS_OK;
}
3.3 结果验证
编译运行得到的结果为:
Example_SemTask2 try get sem g_usSemID wait forever.
Example_SemTask1 try get sem g_usSemID ,timeout 10 ticks.
Example_SemTask2 get sem g_usSemID and then delay 20ticks .
Example_SemTask1 timeout and try get sem g_usSemID wait forever.
Example_SemTask2 post sem g_usSemID .
Example_SemTask1 wait_forever and get sem g_usSemID
liteos信号量(八)的更多相关文章
- Python asyncio文档阅读摘要
文档地址:https://docs.python.org/3/library/asyncio.html 文档第一句话说得很明白,asyncio是单线程并发,这种event loop架构是很多新型异步并 ...
- {Python之线程} 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Threading模块 九 锁 十 信号量 十一 事件Event 十二 条件Condition(了解) 十三 定时器
Python之线程 线程 本节目录 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Thr ...
- 秒杀多线程第八篇 经典线程同步 信号量Semaphore
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <且不超过最大资源数量. 第三个參数能够用来传出先前的资源计数,设为NULL表示不须要传出. 注意:当 ...
- Python 第八篇:异常处理、Socket语法、SocketServer实现多并发、进程和线程、线程锁、GIL、Event、信号量、进程间通讯
本节内容: 异常处理.Socket语法.SocketServer实现多并发.进程和线程.线程锁.GIL.Event.信号量.进程间通讯.生产者消费者模型.队列Queue.multiprocess实例 ...
- Python之路(第三十八篇) 并发编程:进程同步锁/互斥锁、信号量、事件、队列、生产者消费者模型
一.进程锁(同步锁/互斥锁) 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理. 例 ...
- Java并发编程的艺术(八)——闭锁、同步屏障、信号量详解
1. 闭锁:CountDownLatch 1.1 使用场景 若有多条线程,其中一条线程需要等到其他所有线程准备完所需的资源后才能运行,这样的情况可以使用闭锁. 1.2 代码实现 // 初始化闭锁,并设 ...
- windows多线程(八) 信号量Semaphore
如果你看到了这里,我就认为你已经对掌握了有关关键段 CriticalSection.互斥量Mutex和事件Event有关的内容,所以最基本的东西就不再介绍了.如果没有掌握上面说的内容,可以看这里: 关 ...
- Java并发编程原理与实战二十八:信号量Semaphore
1.Semaphore简介 Semaphore,是JDK1.5的java.util.concurrent并发包中提供的一个并发工具类. 所谓Semaphore即 信号量 的意思. 这个叫法并不能很好地 ...
- 秒杀多线程第八篇 经典线程同步 信号量Semaphore (续)
java semaphore实现: Semaphore当前在多线程环境下被扩放使用,操作系统的信号量是个很重要的概念,在进程控制方面都有应用.Java 并发库 的Semaphore 可以很轻松完成信号 ...
随机推荐
- CodeForces - 1248D1 (思维+暴力)
题意 有一个括号序列,你可以选择两个位置i,j(i可以等于j),进行交换.使得最后的循环位置(i的数目)最大. 循环位置:i(0<=i<len),将前i个字符移到最后,得到的新序列是合法的 ...
- 一套从alpine基本镜像到node8.16.2的全套dockerfile
这个花了点时间,可以正式跑起来了. 加了常用的工具及中文时区,非root帐号. 除了pm2,其它的module放到应用程序本身的node_modules目录下来实现的. 一,3rd_part/node ...
- Jmeter-Question之“HTTPS请求”
前面在Jmeter-Question中有提到若干问题,有时间呢,我也会进行继续编写随笔,梳理自己的知识,本篇呢,便来记Jmeter发送https请求的过程 内容大致与http://blog.csdn. ...
- Redis学习笔记(八、缓存设计)
目录: 缓存更新策略 缓存粒度 缓存穿透 缓存雪崩 缓存击穿 缓存更新策略: 1.内存溢出淘汰策略 当redis的使用内存超过maxmemory时会触发相应的策略,具体策略由maxmemory-pol ...
- 一,java框架学习
一,java框架学习 Hibernate概述Hibernate是一个开放源代码的ORM(对象关系映射)框架,对jdbc进行了轻量级的封装,是的java开发人员可以使用面向对象编程思想操作数据库,简化操 ...
- SecureCRT自动断开连接的解决方法
方法一: 在普通用户下,输入重启sshd服务的命令:service sshd restart 这时会提示:管理系统服务或单元需要身份验证.解决的方法:先要切换为root用户,接着重启sshd服务:se ...
- 2019.6.11_MySQL进阶一:索引
所谓索引就是为特定的mysql字段进行一些特定的算法排序,比如二叉树的算法和哈希算法,哈希算法是通过建立特征值,然后根据特征值来快速查找.MySQL索引的建立对于MySQL的高效运行是很重要的,索引可 ...
- (day48)Bootstrap、Adminlte框架、sweetalert
目录 Bootstrap框架官网 Adminlte框架官网 sweetalert 一.基础 二.布局 三.组件 四.插件 Bootstrap框架官网 Adminlte框架官网 sweetalert g ...
- luoguP2178 [NOI2015]品酒大会(后缀数组做法)
题意 因为一个\(k\)相似必定为\(k-1,k-2....0\)相似,对于一个\(lcp\)为\(k\)后缀对\((i,j)\),我们只用把它的贡献加在\(k\)的答案上,最后求一个后缀和和后缀ma ...
- mysql数据库的创建问题
数据库客户端工具navicate 1.使用create database语句创建数据库 (1)指定字符集 create [database|schema ]if not exists 数据库名 def ...