信号量简介
def 1: 信号量是一种解决问题的机制,可以实现共享资源的访问
信号量浅显理解例子:
空车位: 信号量资源(计数值)
让出占用车位: 释放信号量(计数值++)
占用车位:获取信号量(计数值--)
当计数值大于0时,代表信号量有资源,当释放信号量,信号量计数值(资源数) + 1
当获取信号量,信号量计数值(资源数) - 1,信号量处于传递状态
信号量的计数值都是有限制的:限定最大值,如果最大值被限定为1,那么它就是二值信号量,如果最大值不是1,它就是计数型信号量。
def 2 :队列和信号量之间的对比:
注:
队列
1: 队列可以容纳多个数据,创建队列有两部分内存,队列结构体+队列项存储空间
2: 写入队列:当队列满时,可以阻塞
3: 读取队列: 当队列空时,可以阻塞
信号量
1:仅存放计数值,无法存放其他数据,创建信号量,只需分配信号量结构体
2:释放信号量:不可阻塞,计数值++,当计数值为最大值时,返回失败
3:获取信号量:计数值-- ,当没有资源时,可以阻塞
注:
计数值代表的是当前信号量的资源数
信号量这个名字很恰当:
-
信号:起通知作用
-
量:还可以用来表示资源的数量
-
当"量"没有限制时,它就是"计数型信号量"(Counting Semaphores)
-
当"量"只有0、1两个取值时,它就是"二进制信号量"(Binary Semaphores)
-
支持的动作:"give"给出资源,计数值加1;"take"获得资源,计数值减1
计数型信号量的典型场景是:
-
计数:事件产生时"give"信号量,让计数值加1;处理事件时要先"take"信号量,就是获得信号量,让计数值减1。
-
资源管理:要想访问资源需要先"take"信号量,让计数值减1;用完资源后"give"信号量,让计数值加1。
信号量的"give"、"take"双方并不需要相同,可以用于生产者-消费者场合:
-
生产者为任务A、B,消费者为任务C、D
-
一开始信号量的计数值为0,如果任务C、D想获得信号量,会有两种结果: 阻塞:买不到东西咱就等等吧,可以定个闹钟(超时时间) 即刻返回失败:不等
-
任务A、B可以生产资源,就是让信号量的计数值增加1,并且把等待这个资源的顾客唤醒
唤醒谁?谁优先级高就唤醒谁,如果大家优先级一样就唤醒等待时间最长的人
二值信号量简介 :
def:二值信号量本质是一个长度为1的队列,该队列只有空和满两种状态,这就是二值,当队列处于满状态时值为1,当队列处于空状态时值为0。
二值信号量通常用于互斥访问和任务同步,与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转问题,所以二值信号量更适合同步,
同步和互斥概念:
【互斥】
互斥是指在计算机科学领域中的一种同步机制,用于确保在同一时间只有一个进程或线程能够访问某个共享资源,防止因多个进程同时访问而导致数据混乱或错误的情况发生。当一个进程正在访问共享资源时,其他进程必须等待该进程释放资源后才能访问,从而确保资源的独占性。互斥的目的是保护共享资源的完整性和一致性,避免竞争条件的发生,保证程序的正确性和稳定性。Mutex(互斥锁)是实现互斥的一种常见机制。
【同步】
同步是指在计算机编程中一种协调多个线程或进程之间操作的机制。通过同步机制,可以确保多个线程或进程在执行过程中按照一定顺序或规则进行交互,以避免数据竞争、死锁等问题的发生。同步可以实现数据的共享和通信,确保数据的一致性和正确性。常见的同步机制包括互斥锁、信号量、事件等,通过这些机制,可以控制线程或进程的执行顺序、访问共享资源的次序,以及协调它们之间的操作。同步是实现多任务协同工作的重要手段,保证了程序的正确性和稳定性。
二值信号量API函数(熟悉 )
使用二值信号量的过程: 创建二值信号量 ---> 释放二值信号量 ---> 获取二值信号量
创建:
-
使用信号量之前,要先创建,得到一个句柄;使用信号量时,要使用句柄来表明使用哪个信号量。
-
对于二进制信号量、计数型信号量,它们的创建函数不一样:
-
释放信号量 (任务级) -----------> xSemaphorGive()
-
在中断中释放信号量(中断级) -------------> xSemaphorGiveFromISR()
-
获取信号量(任务级) ----------------------> xSemaphoreTask()
-
在中断中获取信号量(中断级) ---------------> xSemaphoreFromISR()
创建二进制信号量的函数原型如下:
/* 创建一个二进制信号量,返回它的句柄。
* 此函数内部会分配信号量结构体
* 返回值: 返回句柄,非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 );
释放二值信号量的函数:
------------------------------------------------------正点原子-----------------------------------------------
描述:(返回值描述)
-------------------------------------韦东山老师-------------------------------------------------------------
删除:
-
对于动态创建的信号量,不再需要它们时,可以删除它们以回收内存。
-
vSemaphoreDelete可以用来删除二进制信号量、计数型信号量,函数原型如下:
/*
* xSemaphore: 信号量句柄,你要删除哪个信号量
*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
give/take
二进制信号量、计数型信号量的give、take操作函数是一样的。这些函数也分为2个版本:给任务使 用,给ISR使用。列表如下:
xSemaphoreGive的函数原型如下:【释放二值信号量】
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
pxHigherPriorityTaskWoken的函数原型如下:
BaseType_t xSemaphoreGiveFromISR(
SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken
);
xSemaphoreGiveFromISR函数的参数与返回值列表如下:
xSemaphoreTake的函数原型如下:【获取二值信号量】
--------------------------正点原子---------------------------------------------------------------------------
------------------------------韦东山老师--------------------------------------------------------------------
BaseType_t xSemaphoreTake(
SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait
);
xSemaphoreTakeFromISR的函数原型如下:
BaseType_t xSemaphoreTakeFromISR(
SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken
);
xSemaphoreTakeFromISR函数的参数与返回值列表如下:
两种信号量之间的对比
信号量的计数值都有限制:限定了最大值。如果最大值被限定为1,那么它就是二进制信号量;如果最 大值不是1,它就是计数型信号量。
差别列表如下: