FreeRtos信号量(互斥信号量与二值信号量)

信号量类型:

二值信号量:

状态只有0和1,表示是否有信号,类似钥匙,用于事件通知和简单同步。

计数信号量:

带有计数器,可递增或递减,用于控制特定资源访问数量。

互斥信号量:

用于保护共享资源,同一时刻仅一个任务能访问,支持优先级继承。

二值与互斥信号量异同:

相同点:都用于任务同步和资源访问控制,有可用、不可用两种状态,获取后需释放下一个任务才能获取。
不同点
设计目的:互斥信号量保护共享资源,解决优先级翻转;二值信号量用于事件通知。
语义用法:互斥信号量谁获取谁释放;二值信号量任何任务或中断可释放。
使用场景:互斥信号量不能用于中断;二值信号量有带ISR结尾API可用于中断。
内部实现:互斥信号量是特殊二值信号量,创建时有优先级继承机制,支持递归获取;二值信号量不支持。

互斥锁(Mutex)的作用:

操作流程:任务访问共享资源前先获取互斥锁,操作完成后释放,确保同一时刻只有一个任务能访问共享资源,避免竞争条件。
输出完整性:保证共享变量操作及输出的完整性,一个任务执行时,其他任务等待。
二值信号量:
状态与初始化:用于任务同步,有take和give两个状态,默认初始化处于释放状态(队列中有信号量),创建OS信号量时初始为空队列,需先放入信号量才能获取。
应用场景:常用于任务与中断之间的同步。

互斥信号量与二值信号量区别:

初始状态:互斥信号量初始状态就存在一个信号量,任务可立刻获取,无需额外添加。(xSemaphoreTake(xMutex,portMAX_DELAY))
任务行为:任务调用获取信号量,若已持有则挂起,简化任务同步步骤。
应用场景:专门用于保护共享资源,通过优先级继承机制避免优先级反转问题。

互斥锁代码示例:

创建3个任务,Task1,Task2,Task3,共同使用同一资源sharedCounter,在任务中对其自增,在main 函数里面创建并调用Task1,Task2,Task3,如果不使用互斥就会导致资源不会按照预期进行自增长。使用互斥信号量之后,sharedCounter就会按照时间片任务逐次增加

#include "FreeRTOS.h"
#include "semphr.h"

int sharedCounter = 0;
SemaphoreHandle_t xMutex; //创建互斥信号量的句柄


void Task1(void *pvParameter)
{
    while(1)
    {
        if(pdTRUE == xSemaphoreTake(xMutex,portMAX_DELAY))
        {
        sharedCounter++;
        printf("Task1:sharedCounter = %d \r\n",sharedCounter);
        xSemaphoreGive(xMutex);
        }
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void Task2(void *pvParameter)
{
while(1)
  {
    if(pdTRUE == xSemaphoreTake(xMutex,portMAX_DELAY))
    {
    sharedCounter++;
    printf("Task2:sharedCounter = %d \r\n",sharedCounter);
    xSemaphoreGive(xMutex);
    }
    vTaskDelay(pdMS_TO_TICKS(100));
  }
  
}

void Task3(void *pvParameter)
{
    while(1)
    {
        if(pdTRUE == xSemaphoreTake(xMutex,portMAX_DELAY))
        {
        sharedCounter++;
        printf("Task3:sharedCounter = %d \r\n",sharedCounter);
        xSemaphoreGive(xMutex);
        }
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  
  printf("hello world");
  /* USER CODE BEGIN 2 */
  xMutex = xSemaphoreCreateMutex();

  xTaskCreate(Task1,"task1",100,NULL,1,NULL);
  xTaskCreate(Task2,"task2",100,NULL,1,NULL);
  xTaskCreate(Task3,"task3",100,NULL,1,NULL);
  
  vTaskStartScheduler();
  /* USER CODE END 2 */

  /* Init scheduler */
  osKernelInitialize();  /* Call init function for freertos objects (in freertos.c) */
  MX_FREERTOS_Init();

  /* Start scheduler */
  osKernelStart();
  /* We should never get here as control is now taken by the scheduler */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

在这里插入图片描述
如果不使用互斥锁,sharecounter不会自增1的打印,而是有可能是

Task1:sharedCounter = 1 
Task2:sharedCounter = 4
Task3:sharedCounter = 7 
Task1:sharedCounter = 10 
Task2:sharedCounter = 13
Task3:sharedCounter = 16
Task1:sharedCounter = 19
Task2:sharedCounter = 22
Task3:sharedCounter = 25

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
这里是分界线
首先先看看这个,对理解下面的问题有帮助。
互斥量
特点:
所有权机制:获取互斥量的任务必须是释放它的任务。
优先级继承:大多数RTOS支持优先级继承,避免优先级反转问题。
嵌套锁定:同一任务可以多次获取统一互斥量(需相应释放),避免自死锁。

二值信号量:
特点:二进制计数器:值只能是0(不可用)或1(可用)
无所有权:任何任务都可以释放信号量,即使不是获取者
用途灵活:可用于互斥访问,也可用于任务同步(如时间标志)

今天遇到一个问题:
感觉有点意思,我先把代码给贴出来。
事情是这样子的,我现在用Ymodem协议在做OTA升级,过程中呢有点问题
在Ymodem协议中有这么一段代码。

else
{
  packet_data+=PACKET_HEADER;
//xQueueSend(Queue_AppDataBuffer, &(packet_data+PACKET_HEADER), 0); //这样写是错的
  xQueueSend(Queue_AppDataBuffer, &packet_data, 0);  //发送净荷数据 
  xSemaphoreTake(Semaphore_ExtFlashState,portMAX_DELAY);
  xSemaphoreGive(Semaphore_ExtFlashState);
  if(packet_data == buf1)
  {
    packet_data = buf2;
  }
  else
  {
    packet_data = buf1;
  }
  Send_Byte(ACK);
}
packets_received ++;
session_begin = 1;
}

问题就是这个互斥量,先拿到互斥量,然后立马就给释放了?这不是做无用功?这是为啥?
首先,要知道互斥量也是队列啊!他拿到互斥量然后再释放,吃多了?
并不是,他在等!等这个其他线程中的互斥量给我释放,我才能干下面的事儿。为啥?因为下面有一个buffer转换的过程,在我这个Ymodem协议中,我设置了两个缓冲buffer,buffer1以及buffer2。那么另外一个互斥量被谁拿了?就是我这个下载数据的线程里面,再仔细点就是在这个while(1)循环里面。

void DownloadAppData_task_runnable(void *argument)
{
  uint8_t * pu8_data = NULL;
  int32_t * pu32_size = NULL;
  //第一帧数据一定是数据的长度
  xQueueReceive(Queue_AppDataBuffer,&pu32_size,portMAX_DELAY);
  //需要擦除掉flash size 所对应的扇区
  //TODO:
  //xSemaphoreGive(Semaphore_ExtFlashState);//这里没必要,因为互斥量在刚开始创建的时候,队列里有值
  while(1)
  {
    xQueueReceive(Queue_AppDataBuffer,&pu8_data,portMAX_DELAY);
    xSemaphoreTake(Semaphore_ExtFlashState,0);
    if(pu8_data == NULL)
    {
      continue;
    }
    //执行写入W25q的逻辑
    //TODO:
    xSemaphoreGive(Semaphore_ExtFlashState);
  }
  /* USER CODE END DowanloadAppData_task_runnable */
}

当在Ymodem协议发送完数据后,DownloadAppData_task_runnable这个函数中,Queue_AppDataBuffer就接受到数据了,这里有两个接受数据的队列,第一个是接收数据长度,第二个是接收净荷数据(就是纯数据,没有数据长度、命令啥的),在这个while(1)循环中,当我接收到数据之后,就要把数据放入buffer中,拿到互斥量之后,就开始等这个buffer 是不是为空指针,不是空指针没说明数据已经拿到了,释放互斥量。当释放完毕之后,Ymodemx协议中这个才能拿到这个互斥量,拿到了互斥量就说明下载函数中中的活了已经干完了,Ymodem继续。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值