【问题标题】:How mutex guarantee ownership in freeRTOS?互斥锁如何保证 freeRTOS 中的所有权?
【发布时间】:2019-12-27 17:13:12
【问题描述】:

我正在使用 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);
  }

}

您会注意到,第一次在处理器中开始运行的第一个任务将打印出第一条错误消息,因为在互斥锁持有者中仍有令牌时它无法提供令牌,这是正常的,所以我忽略了它。

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

谢谢。

【问题讨论】:

    标签: mutex freertos esp32


    【解决方案1】:

    您的示例非常复杂,我也没有看到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 在函数的开头 - 不要那样做。互斥锁默认是“给予”的,如果你不是第一个“接受”它的人,你就不应该“给予”它。始终将xSemaphoreGive 放在成功的xSemaphoreTake 下,别无其他。

    2. 这个代码块:

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

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

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

    【讨论】:

    • 感谢您的解释。正如我在文章开头所说的那样,我只是在使用 freeRTOS 的互斥(信号量)API。当我遇到所有权保证条款时,尽管 API 本身会保证这一点。这意味着即使有人(开发人员)尝试进入关键资源,即使像我一样做这样的事情,API 本身也会返回一个错误,告知该资源由另一个任务拥有。关于我发布的代码,只是一个函数,我创建的每个任务(task_A、task_B 和 task_C)都会调用它们,它们都会做同样的事情。
    • 继续消息,记住我不是在做实际的程序,我只是在使用 freeRTOS API 测试和玩(调整)。所以代码本身除了打印和等待什么都不做。不同时期取决于任务调用参数。因此,作为结论,你想说,我应该使用你发布的算法,这在 freeRTOS 文档中是相同的,并保持这种方式。这将导致互斥体和二进制信号量是相同的,除了会阻止使用哪一个的条件(如果它是中断或不是等...)。
    • Mutex 和 semaphore 至少在两个方面彼此不同:mutex 在创建时默认给出,而 semaphore 不是,mutex 不能从与使用它的上下文不同的上下文中给出,而信号量可以。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-10
    • 2012-06-05
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    相关资源
    最近更新 更多