UCOSIII其他内容导航不迷路
UCOSIII操作系统-简介
【UCOSIII操作系统】任务篇(1)创建任务
【UCOSIII操作系统】任务篇(2)相关API函数
【UCOSIII操作系统】系统初始化篇(1)系统初始化
【UCOSIII操作系统】系统初始化篇(2)CPU,SysTick,内存初始化
【UCOSIII操作系统】硬件初始化篇(1)硬件初始化以及开始运行系统
【UCOSIII操作系统】消息队列篇(1)消息队列
【UCOSIII操作系统】消息队列篇(2)任务消息队列
【UCOSIII操作系统】信号量与互斥量篇(2)互斥量
【UCOSIII操作系统】信号量与互斥量篇(3)任务信号量
【UCOSIII操作系统】事件篇
【UCOSIII操作系统】中断管理篇
【UCOSIII操作系统】临界段篇
【UCOSIII操作系统】软件定时器篇
【UCOSIII操作系统】内存管理篇
已完结
说在前面:
这个内容不适合0基础的人,因为这里只讲了应用层面的东西,并没有深入内核讲解,所以要从零开始学UCOSIII的朋友,可以先去学完入门内容,再来观看这个笔记加深印象。
这篇文章是个人学习整理,如有错误请指正
信号量(Semaphore)是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问,常用于协助一组相互竞争的任务来访问临界资源。在多任务系统中,各任务之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。
信号量像是一种上锁机制,代码必须获得对应的钥匙才能继续执行,一旦获得了 钥匙,也就意味着该任务具有进入被锁部分代码的权限。一旦执行至被锁代码段,则任务一直等待,直到对应被锁部分代码的钥匙被再次释放才能继续执行。
某一资源对应的信号量为1的时候,那么就可以使用这一资源,如果对应资源的信号量为0,那么等待该信号量的任务就会被放进等待信号量的任务表中。在等待信号量的时候也可以设置超时,如果超过设定的时间任务没有等到信号量的话那么该任务就会进入就绪态。任务以“发信号”的方式操作信号量。可以看出如果一个信号量为二进制信号量的话,一次只能-一个任务:使用共享资源。
有时候我们需要可以同时有多个任务访问共享资源,这个时候二进制信号量就不能使用了,计数型信号量就是用来解决这个问题的。比如某—个信号量初始化值为10,那么只有前10个请求该信号量的任务可以使用共享资源,以后的任务需要等待前10个任务释放掉信号量。每当有任务请求信号量的时候,信号量的值就会减1,直到减为0。当有任务释放掉信号量的时候信号量的值就会加1。
函数 | 描述 |
---|---|
OSSemCreate() | 创建一个信号量(常用) |
OSSemDel() | 删除一个信号量 |
OSSemPend() | 等待一个信号量(常用) |
OSSemPendAbort() | 取消等待 |
OSSemPost() | 释放一个信号量(常用) |
OSSemSet() | 强制设置一个信号量的值 |
void OSSemCreate (OS_SEM *p_sem, //信号量控制块指针
CPU_CHAR *p_name, //信号量名称
OS_SEM_CTR cnt, //资源数目或事件是否发生标志
OS_ERR *p_err) //返回错误类型
信号量控制块指针: 指向我们定义的信号量控制块结构体变量, 所以在创建之前我们需要先定义一个信号量控制块变量。
OS_SEM MY_SEM; //定义一个信号量
cnt: 这个值表示初始化时候资源的个数或事件是否发生标志,一般信号量是二值信号量的时候,这个值一般为0或者为1,而如果信号量作为计数信号量的时候,这个值一般定义为初始资源的个数。
OSSemDel0用于删除一个信 号量,信号量删除函数是根据信号量结构(信号量句柄)直接删除的,删除之后这个信号量的所有信息都会被系统清空,而且不能再次使用这个信号量了,但是需要注意的是,如果某个信号量没有被定义,那也是无法被删除的,如果有任务阻塞在该信号量上,那么尽量不要删除该信号量。想要使用互斥量删除函数就必须将OS_ CFG_ SEM_ DEL_ EN宏定义配置为1。
#if OS_CFG_SEM_DEL_EN > 0u //如果使能了 OSSemDel() 函数
OS_OBJ_QTY OSSemDel (OS_SEM *p_sem, //信号量指针
OS_OPT opt, //选项
OS_ERR *p_err) //返回错误类型
OS_OPT_DEL_NO_PEND: //如果只在没有任务等待的情况下删除信号量
OS_OPT_DEL_ALWAYS: //无论如何必须删除信号量
任务获得信号量以后就可以访问共享资源了,在任务访问完共享资源以后必须释放信号量,释放信号量也叫发送信号量,使用函数OSSemPost()发送信号量。如果没有任务在等待该信号量的话则OSSemPost()函数只是简单的将信号量加1,然后返回到调用该函数的任务中继续运行。如果有一个或者多个任务在等待这个信号量,则优先级最高的任务将获得这个信号量,然后由调度器来判定刚获得信号量的任务是否为系统中优先级最高的就绪任务,如果是,则系统将进.行任务切换,运行这个就绪任务。
OS_SEM_CTR OSSemPost (OS_SEM *p_sem, //信号量控制块指针
OS_OPT opt, //选项
OS_ERR *p_err) //返回错误类型
OS_ OPT_ POST_ 1 仅向等待该信号量的优先级最高的任务发送信号量。
OS_ OPT _POST_ ALL 向等待该信号量的所有任务发送信号量。
OS_ OPT_ POST_ NO SCHED 该选项禁止在本函数内执行任务调度操作。即使该函数使得更高优先级的任务结束挂起进入就绪状态,也不会执行任务调度,而是会在其他后续函数中完成任务调度。
OS_SEM SemOfKey; //标志KEY1 是否被按下的信号量
OSSemPost((OS_SEM *)&SemOfKey, //发布SemOfKey
(OS_OPT )OS_OPT_POST_ALL, //发布给所有等待任务
(OS_ERR *)&err); //返回错误类型
与消息队列的操作一样,信号量的获取可以在任务中使用。
与释放信号量对应的是获取信号量,我们知道,当信号量有效的时候,任务才能获取信号量,当任务获取了某个信号量的时候,该信号量的可用个数就减一-,当它减到0的时候,任务就无法再获取了,并且获取的任务会进入阻塞态(假如用户指定了阻塞超时时间的话)。如果某个信号量中当前拥有1个可用的信号量的话,被获取一次就变得无效了,那么此时另外一个任务获取该信号量的时候,就会无法获取成功,该任务便会进入阻塞态,阻塞时间由用户指定。
uCOS支持系统中多个任务获取同一个信号量,假如信号量中已有多个任务在等待,那么这些任务会按照优先级顺序进行排列,如果信号量在释放的时候选择只释放给一个任务,那么在所有等待任务中最高优先级的任务优先获得信号量,而如果信号量在释放的时候选择释放给所有任务,则所有等待的任务都会获取到信号量。
OS_SEM_CTR OSSemPend (OS_SEM *p_sem, //信号量指针
OS_TICK timeout, //等待超时时间
OS_OPT opt, //选项
CPU_TS *p_ts, //等到信号量时的时间戳
OS_ERR *p_err) //返回错误类型
OSSemPend ((OS_SEM *)&SemOfKey, //等待该信号量被发布
(OS_TICK )0, //无期限等待
(OS_OPT )OS_OPT_PEND_BLOCKING, //如果没有信号量可用就等待
(CPU_TS *)&ts_sem_post, //获取信号量最后一次被发布的时间戳
(OS_ERR *)&err); //返回错误类型
自己的一些理解:
对于信号量,如果我一直释放的话计数信号量就会一直增加一直增加,然后请求信号量的任务就能一直请求到信号量,就可以多次运行。
详细代码可以参考正点原子
例10-2 UCOSIII使用信号量访问共享资源区
例10-3 UCOSIII使用信号量进行任务同步