mutex 如何保证 freeRTOS 中的所有权?

How mutex guarantee ownership in freeRTOS?

我正在使用 esp32 在 freeRTOS 中玩 Mutex。在一些文档中我读到互斥锁保证所有权,这意味着如果一个线程(我们将其命名为 task_A)锁定了一个关键资源(获取令牌)其他线程(task_B 和 task_C)将保持保持模式,等待资源被锁定它的同一个线程(即 task_A)解锁。我试图通过设置其他任务(task_B 和 task_C)来证明在开始做任何事情之前给出一个令牌,然后它会尝试从互斥锁持有者那里获取一个令牌,这是令人惊讶的是,没有显示出任何错误。 好吧,我用来验证或显示事情如何工作的方法我创建了一个显示功能来读取每个任务发布(设置和清除)的事件(当它处于等待模式时它会设置等待位如果它正在工作它将设置工作咬等......,你明白了)。和一个简单的 printf() 以防 take 或 give 函数出错(xSemaphoreTake != true 和 xSemaphoreGive != true)。

我无法使用调试模式,因为我没有任何类型的微控制器调试器。

这是我正在尝试做的一个例子: 我创建了许多任务,每个任务都会调用此函数,但在不同的时间使用不同的设置。

void vVirtualResource(int taskId, int runTime_ms){
  int delay_tick = 10;
  int currentTime_tick = 0;
  int stopTime_tick = runTime_ms/portTICK_PERIOD_MS;
  if(xSemaphoreGive(xMutex)!=true){
      printf("Something wrong in giving first mutex's token in task id: %d\n", taskId);
  }

  while(xSemaphoreTake(xMutex, 10000/portTICK_PERIOD_MS) != true){
    vTaskDelay(1000/portTICK_PERIOD_MS);
  }
// notify that the task with <<task id>> is currently running and using this resource
  switch (taskId)
  {
  case 1:
    xEventGroupClearBits(xMutexEvent, EVENTMASK_MUTEXTSK1);
    xEventGroupSetBits(xMutexEvent, EVENTRUN_MUTEXTSK1);
    break;
  case 2:
    xEventGroupClearBits(xMutexEvent, EVENTMASK_MUTEXTSK2);
    xEventGroupSetBits(xMutexEvent, EVENTRUN_MUTEXTSK2);
    break;
  case 3:
    xEventGroupClearBits(xMutexEvent, EVENTMASK_MUTEXTSK3);
    xEventGroupSetBits(xMutexEvent, EVENTRUN_MUTEXTSK3);
    break;
  default:
    break;
  }
  // start running the resource
  while(currentTime_tick<stopTime_tick){
    vTaskDelay(delay_tick);
    currentTime_tick += delay_tick;
  }
  // gives back the token
  if(xSemaphoreGive(xMutex)!=true){
    printf("Something wrong in giving mutex's token in task id: %d\n", taskId);
  }

}

您会第一次注意到,在处理器中启动 运行 的第一个任务将打印出第一条错误消息,因为它无法在仍有令牌的情况下提供令牌mutex holder,这很正常,所以我就忽略了。

希望有人可以向我解释互斥锁如何使用 freeRTOS 中的代码保证所有权。首先,我没有使用第一个 xSemaphoreGive 函数,它运行良好。但这并不意味着它可以保证任何事情。或者我编码不正确。

谢谢。

你的例子很复杂,我也没有看到 task_Atask_Btask_C 的清晰代码,所以我会尝试解释一个更简单的例子,希望能解释互斥体如何保证资源所有权。

使用互斥量的一般方法如下:

void doWork()
{
  // attempt to take mutex
  if(xSemaphoreTake(mutex, WAIT_TIME) == pdTRUE)
  {
    // mutex taken - do work
    ...
    // release mutex
    xSemaphoreGive(mutex);
  }
  else
  {
    // failed to take mutex for 'WAIT_TIME' amount of time
  }
}

上面的doWork函数是可能被多个线程同时调用的函数,需要保护。对于需要保护的给定资源上的每个功能,都会重复此模式。如果资源更复杂,一个好的方法是保护线程可调用的最顶层函数,然后如果成功获取互斥锁,则调用执行实际工作的内部函数。

您所说的所有权保证是这样一个事实,即 if(xSemaphoreTake(mutex, WAIT_TIME) == pdTRUE) 语句下的上下文(线程,还有中断)可能不超过一个。换句话说,如果一个上下文成功获取互斥量,则可以保证没有其他上下文也能够获取它,除非原始上下文首先使用 xSemaphoreGive 释放它。

现在关于您的场景 - 虽然我不完全清楚它应该如何工作,但我可以看到您的代码有两个问题:

  1. xSemaphoreGive 在函数的开头 - 不要那样做。互斥锁默认是 "given",如果你不是第一个 "taking" 它,你就不应该是 "giving" 它。始终将 xSemaphoreGive 放在成功的 xSemaphoreTake 下,而不是其他地方。

  2. 此代码块:

    while(xSemaphoreTake(xMutex, 10000/portTICK_PERIOD_MS) != true){
      vTaskDelay(1000/portTICK_PERIOD_MS);
    }
    

    如果您需要等待更长时间的互斥 - 指定更长的时间。如果您想要无限等待,只需指定最长的可能时间 (0xFFFFFFFF)。在您的场景中,您每 10 秒轮询一次互斥锁,然后延迟 1 秒,在此期间实际上并未检查互斥锁,这意味着在某些情况下,在互斥锁被其他线程释放到在请求它的当前线程中开始工作。等待互斥锁已经由 RTOS 以最佳方式完成 - 它会在释放互斥锁后立即唤醒当前等待互斥锁的最高优先级任务,无需做不必要的事情。

如果我要就如何修复您的示例给出建议 - 简化它并且不要做超出需要的事情,例如对 xSemaphoreGive 的额外调用或实现您自己的等待互斥体。将执行某些工作的代码部分隔离到一个单独的函数,该函数在最顶部对 xSemaphoreTake 执行一次调用,仅当 xSemaphoreTake 成功时才对 xSemaphoreGive 进行一次调用。然后从不同的线程调用这个函数来测试它是否有效。