UCOS操作系统——信号量与互斥信号量(九)

UCOS操作系统

一、信号量简介

与FreeRTOS相同信号量像是一种上锁机制,代码必须获得对应的钥匙才能继续执行,一旦获得了钥匙,也就意味着该任务具有进入被锁部分代码的权限。一旦执行至被锁代码段,则任务一直等待,直到对应被锁部分代码的钥匙被再次释放才能继续执行。
信号量用于控制对共享资源的保护,但是现在基本用来做任务同步用。
要想获取资源的任务必须执行“等待”操作,如果该资源对应的信号量有效值大于1,则任务可以获得该资源,任务继续运行。如果该信号量的有效值为0,则任务加入等待信号量的任务表中。如果等待时间超过某一个设定值,该信号量仍然没有被释放掉,则等待信号量的任务就进入就绪态,如果将等待时间设置为0的话任务就将一直等待该信号量。
信号量通常分为两种:二进制信号量和计数型信号量。

1、二进制信号量

某一资源对应的信号量为 1 的时候,那么就可以使用这一资源,如果对应资源的信号量为0,那么等待该信号量的任务就会被放进等待信号量的任务表中。在等待信号量的时候也可以设置超时,如果超过设定的时间任务没有等到信号量的话那么该任务就会进入就绪态。任务以“发信号”的方式操作信号量。可以看出如果一个信号量为二进制信号量的话,一次只能一个任务使用共享资源。

2、计数型信号量

有时候我们需要可以同时有多个任务访问共享资源,这个时候二进制信号量就不能使用了,计数型信号量就是用来解决这个问题的。比如某一个信号量初始化值为 10,那么只有前 10 个请求该信号量的任务可以使用共享资源,以后的任务需要等待前 10 个任务释放掉信号量。每当有任务请求信号量的时候,信号量的值就会减 1,直到减为 0。当有任务释放掉信号量的时候信号量的值就会加 1。

二、使用信号量

1.相关API函数

OSSemCreate() 创建一个信号量
OSSemDel() 删除一个信号量
OSSemPend() 等待一个信号量
OSSemPendAbort() 取消等待
OSSemPost() 释放一个信号量
OSSemSet() 强制设置一个信号量的值

2.OSSemCreate()创建信号量

void OSSemCreate ( OS_SEM *p_sem,
 CPU_CHAR *p_name,
 OS_SEM_CTR cnt,
 OS_ERR *p_err)

p_sem: 指向信号量控制块,我们需要按照如下所示方式定义一个全局信号量,并将
这个信号量的指针传递给函数 OSSemCreate()。
OS_SEM TestSem;
p_name: 指向信号量的名字。
cnt: 设置信号量的初始值,如果此值为 1,代表此信号量为二进制信号量,如果大于 1
的话就代表此信号量为计数型信号量。
p_err: 保存调用此函数后的返回的错误码。
二值信号量:
在这里插入图片描述
计数信号量:
在这里插入图片描述

3.OSSemPend()请求信号量

当一个任务需要独占式的访问某个特定的系统资源时,需要与其他任务或中断服务程序同
步,或者需要等待某个事件的发生,应该调用函数 OSSemPend(),函数原型如下:
前面不是提到信号量就像一把锁,只有获取到了才会执行下面得内容,否则就一直等待。

OS_SEM_CTR OSSemPend ( OS_SEM *p_sem,
 OS_TICK timeout,
 OS_OPT opt,
 CPU_TS *p_ts,
 OS_ERR *p_err)

p_sem: 指向一个信号量的指针。
timeout: 指定等待信号量的超时时间(时钟节拍数),如果在指定时间内没有等到信号量则允许任务恢复执行。如果指定时间为 0 的话任务就会一直等待下去,直到等到信号量。
opt: 用于设置是否使用阻塞模式,有下面两个选项。
OS_OPT_PEND_BLOCKING 指定信号量无效时,任务挂起以等待信号量。
OS_OPT_PEND_NON_BLOCKING 信号量无效时,任务直接返回。
p_ts: 指向一个时间戳,用来记录接收到信号量的时刻,如果给这个参数赋值 NULL,
则说明用户没有要求时间戳。
p_err: 保存调用本函数后返回的错误码。
在这里插入图片描述

OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); 	//请求信号量

4.OSSemPost()释放信号量

任务获得信号量以后就可以访问共享资源了,在任务访问完共享资源以后必须释放信号量,释放信号量也叫发送信号量,使用函数 OSSemPost()发送信号量。如果没有任务在等待该信号量的话则 OSSemPost()函数只是简单的将信号量加 1,然后返回到调用该函数的任务中继续运行。如果有一个或者多个任务在等待这个信号量,则优先级最高的任务将获得这个信号量,然后由调度器来判定刚获得信号量的任务是否为系统中优先级最高的就绪任务,如果是,则系统将进行任务切换,运行这个就绪任务,OSSemPost()函数原型如下:

OS_SEM_CTR OSSemPost ( OS_SEM *p_sem,
 OS_OPT opt,
 OS_ERR *p_err)

p_sem: 指向一个信号量的指针
opt: 用来选择信号量发送的方式。
OS_OPT_POST_1 仅向等待该信号量的优先级最高的任务发送信号量。
OS_OPT_POST_ALL 向等待该信号量的所有任务发送信号量。
OS_OPT_POST_NO_SCHED 该选项禁止在本函数内执行任务调度操作。即使
该函数使得更高优先级的任务结束挂起进入就绪状态,也不会执行任务调度,而是会
在其他后续函数中完成任务调度。
p_err: 用来保存调用此函数后返回的错误码

三、优先级反转

例如:三个不同优先级的任务——低任务、中任务、高任务
创建二值信号量,然后释放一次信号量。低任务获取信号量,长时间不释放。
高任务获取信号量,但是此时信号量被低任务占用着,高任务只能等待。但是等待过程中,中任务是一直运行的。出现了优先级反转。
可以看我之前的博客,写的很详细

四、互斥信号量

与FreeRTOS一样,使用互斥信号量可以完美的避开这个问题。
在这里插入图片描述
不同于二值信号量的是互斥信号量具有优先级继承的特性。当一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的“优先级翻转”的影响降到最低。

1. OSMutexCreate()创建互斥信号量

创建互斥信号量使用函数 OSMutexCreate(),函数原型如下:

void OSMutexCreate (OS_MUTEX *p_mutex,
 CPU_CHAR *p_name,
 OS_ERR *p_err)

p_mutex: 指向互斥型信号量控制块。互斥型信号量必须有用户应用程序进行实际分配
OS_MUTEX MyMutex;
p_name: 互斥信号量的名字
p_err: 调用此函数后返回的错误码。

2.OSMutexPend()请求互斥信号量

当一个任务需要对资源进行独占式访问的时候就可以使用函数 OSMutexPend(),如果该互斥信号量正在被其他的任务使用,那么 UCOSIII 就会将请求这个互斥信号量的任务放置在这个互斥信号量的等待表中。任务会一直等待,直到这个互斥信号量被释放掉,或者设定的超时时间到达为止。如果在设定的超时时间到达之前信号量被释放,UCOSIII 将会恢复所有等待这个信号量的任务中优先级最高的任务
注意!如果占用该互斥信号量的任务比当前申请该互斥信号量的任务优先级低的话,
OSMutexPend()函数会将占用该互斥信号量的任务的优先级提升到和当前申请任务的优先级一样。当占用该互斥信号量的任务释放掉该互斥信号量以后,恢复到之前的优先级。OSMutexPend()函数原型如下:

void OSMutexPend (OS_MUTEX *p_mutex,
 OS_TICK timeout,
 OS_OPT opt,
 CPU_TS *p_ts,
 OS_ERR *p_err)

p_mutex: 指向互斥信号量。
timeout: 指定等待互斥信号量的超时时间(时钟节拍数),如果在指定的时间内互斥信
号量没有释放,则允许任务恢复执行。该值设置为 0 的话,表示任务将会一直等待下去,直到信号量被释放掉。
opt: 用于选择是否使用阻塞模式。
OS_OPT_PEND_BLOCKING 指定互斥信号量被占用时,任务挂起等待该互斥信号量。
OS_OPT_PEND_NON_BLOCKING 指定当互斥信号量被占用时,直接返回任务。
注意!当设置为 OS_OPT_PEND_NON_BLOCKING,是 timeout 参数就没有
意义了,应该设置为 0。
p_ts: 指向一个时间戳,记录发送、终止或删除互斥信号量的时刻。
p_err: 用于保存调用此函数后返回的错误码。

3.OSMutexPost()函数释放互斥信号量

我们可以通过调用函数 OSMutexPost()来释放互斥型信号量,只有之前调用过函数
OSMutexPend()获取互斥信号量,才需要调用 OSMutexPost()函数来释放这个互斥信号量,函数原型如下:

void OSMutexPost (OS_MUTEX *p_mutex,
 OS_OPT opt,
 OS_ERR *p_err)

p_mutex: 指向互斥信号量。
opt: 用来指定是否进行任务调度操作
OS_OPT_POST_NONE 不指定特定的选项
OS_OPT_POST_NO_SCHED 禁止在本函数内执行任务调度操作。
p_err: 用来保存调用此函数返回的错误码

总结

信号量与互斥信号量需要结合实验例程来理解,下一篇博客会提到每个知识点对应的实验。

猜你喜欢

转载自blog.csdn.net/qq_51963216/article/details/123911405