FreeRTOS信号量

1.什么是信号量

1.1理解信号量

有这么一个场景,我需要爬翠华山,我要进翠华山我得要有门票,景区门票数量为信号量,游客为任务,如果只剩下一张门票,而有多个人,那只能有一个人可以上山。人关心门票长什么样吗?只关心门票的数量。

通过上面的例子我们就可以实现互斥的操作。好多个任务都想用这个临界资源怎么办,我根据信号量只让一个任务使用。

在这种情况下我们只需要维护一个数值,使用信号量效率更高、更节省内存 本章涉及如下内容:

  • 怎么创建、删除信号量
  • 怎么发送、获得信号量
  • 什么是计数型信号量?什么是二进制信号量?

1.2信号量和队列的区别

 1、信号量不需要存储数据只记录个数,不需要环形Buffer。

2、信号量没有写等待链表只有读等待链表

发送信号量(信号量增加)为什么不需要等待?

我有门票我就放票,我没票了游客抢光了,我想放也放不了,放票过程根本就不需要等待。

而游客他会等呀,他都来了他就愿意等一会,虽然现在没票了,过一会可能就会有。实在没有票就回家。

其余基本差不多,read就是Take(取,拿),write就是give(给)

2.信号量机制

当任务拿信号量时有种情况

1、有信号量直接拿

2、没有信号量,可选择不等待直接返回

3、没有信号量,选择等待,如果有信号量就返回pdTRUE,如果等到阻塞结束就返回失败。

在选择等待后会做一件事,把自己从ready链表删除放在Tack_list和Delay_list

如果在等的过程中获取到信号量就返回成功,这个过程和队列一样,在Give信号量的时候会去遍历等待信号量的链表,将优先级最高的任务或同级优先级任务等待时间最长的任务从等待信号量链表和等待链表中删除放回就绪链表。

等待超时放回由Tick中断来执行放回操作。

3.信号量相关API函数

使用信号量时,先创建、然后去添加资源、获得资源。使用句柄来表示一个信号量。

3.1 创建

使用信号量之前,要先创建,得到一个句柄;使用信号量时,要使用句柄来表明使用哪个信号量。 对于二进制信号量、计数型信号量,它们的创建函数不一样:

二进制信号量计数型信号量
动态创建xSemaphoreCreateBinary 计数值初始值为0xSemaphoreCreateCounting
vSemaphoreCreateBinary(过时了) 计数值初始值为1
静态创建xSemaphoreCreateBinaryStaticxSemaphoreCreateCountingStatic

创建二进制信号量的函数原型如下:

/* 创建一个二进制信号量,返回它的句柄。
 * 此函数内部会分配信号量结构体 
 * 返回值: 返回句柄,非NULL表示成功
 */
SemaphoreHandle_t xSemaphoreCreateBinary( void );

/* 创建一个二进制信号量,返回它的句柄。
 * 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针
 * 返回值: 返回句柄,非NULL表示成功
 */
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer );

创建计数型信号量的函数原型如下:

/* 创建一个计数型信号量,返回它的句柄。
 * 此函数内部会分配信号量结构体 
 * uxMaxCount: 最大计数值
 * uxInitialCount: 初始计数值
 * 返回值: 返回句柄,非NULL表示成功
 */
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);

/* 创建一个计数型信号量,返回它的句柄。
 * 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针
 * uxMaxCount: 最大计数值
 * uxInitialCount: 初始计数值
 * pxSemaphoreBuffer: StaticSemaphore_t结构体指针
 * 返回值: 返回句柄,非NULL表示成功
 */
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, 
                                                 UBaseType_t uxInitialCount, 
                                                 StaticSemaphore_t *pxSemaphoreBuffer );

3.2 删除

对于动态创建的信号量,不再需要它们时,可以删除它们以回收内存。

vSemaphoreDelete可以用来删除二进制信号量、计数型信号量,函数原型如下:

/*
 * xSemaphore: 信号量句柄,你要删除哪个信号量
 */
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

3.3 give/take

二进制信号量、计数型信号量的give、take操作函数是一样的。这些函数也分为2个版本:给任务使用,给ISR使用。列表如下:

在任务中使用在ISR中使用
givexSemaphoreGivexSemaphoreGiveFromISR
takexSemaphoreTakexSemaphoreTakeFromISR

xSemaphoreGive的函数原型如下:

BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );

xSemaphoreGive函数的参数与返回值列表如下:

参数说明
xSemaphore信号量句柄,释放哪个信号量
返回值pdTRUE表示成功, 如果二进制信号量的计数值已经是1,再次调用此函数则返回失败; 如果计数型信号量的计数值已经是最大值,再次调用此函数则返回失败

pxHigherPriorityTaskWoken的函数原型如下:

BaseType_t xSemaphoreGiveFromISR(
                        SemaphoreHandle_t xSemaphore,
                        BaseType_t *pxHigherPriorityTaskWoken
                    );

xSemaphoreGiveFromISR函数的参数与返回值列表如下:

参数说明
xSemaphore信号量句柄,释放哪个信号量
pxHigherPriorityTaskWoken如果释放信号量导致更高优先级的任务变为了就绪态, 则*pxHigherPriorityTaskWoken = pdTRUE
返回值pdTRUE表示成功, 如果二进制信号量的计数值已经是1,再次调用此函数则返回失败; 如果计数型信号量的计数值已经是最大值,再次调用此函数则返回失败

xSemaphoreTake的函数原型如下:

BaseType_t xSemaphoreTake(
                   SemaphoreHandle_t xSemaphore,
                   TickType_t xTicksToWait
               );

xSemaphoreTake函数的参数与返回值列表如下:

参数说明
xSemaphore信号量句柄,获取哪个信号量
xTicksToWait如果无法马上获得信号量,阻塞一会: 0:不阻塞,马上返回 portMAX_DELAY: 一直阻塞直到成功 其他值: 阻塞的Tick个数,可以使用*pdMS_TO_TICKS()*来指定阻塞时间为若干ms
返回值pdTRUE表示成功

xSemaphoreTakeFromISR的函数原型如下:

BaseType_t xSemaphoreTakeFromISR(
                        SemaphoreHandle_t xSemaphore,
                        BaseType_t *pxHigherPriorityTaskWoken
                    );

xSemaphoreTakeFromISR函数的参数与返回值列表如下:

参数说明
xSemaphore信号量句柄,获取哪个信号量
pxHigherPriorityTaskWoken如果获取信号量导致更高优先级的任务变为了就绪态, 则*pxHigherPriorityTaskWoken = pdTRUE
返回值pdTRUE表示成功

3.4问题与解答

问题1:为什么Give要在while循环内,Tack在循环外 ,把Tack放在循环内为什么系统会卡死?

循环运行太快直接将信号量Tack完了,没信号量也不放信号量,设置没有信号量一直等待当然就卡死了。

问题2:为什么没有信号量就一直等待,等有信号量了才能跑,而只等待700个Tick小车还没获取信号量就开始跑?

小车只等待700个Tick等到阻塞结束后Tick中断会把他再次运行。

问题3:为什么显示小车1获取信号量,不是小车三,不是最后创建的任务先执行吗?

AI:正常情况下先创建的任务先执行。

4.优先级“翻转”

高优先级任务不能执行低优先级任务反而可以执行,我们引入两个情况来说明:

上面的黑色时间特别短,绿色时间也特别短,任务进入基本就直接阻塞了。

这两种情况都是任务先后执行顺序和优先级信号量密切相关。

如何解决优先级翻转问题?使用互斥亮!

我们下次开始讲互斥量,一起成为入门嵌入式! 

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值