收藏官网首页
查看: 975|回复: 0

[经验分享] 【一起来玩RTOS系列】之RT-Thread 信号量优先级反转

发表于 2017-12-9 21:30:05 | 显示全部楼层 |阅读模式
教您5分钟接入机智云,实现傻瓜式开发

在一般的二值信号量用于资源互斥访问时,因为已经不存在实例,线程递归持有会发生主动挂起(最终形成死锁)。使用信号量会导致的另一个潜在问题是线程优先级翻转。所谓优先级翻转问题即当一个高优先级线程试图通过信号量机制访问共享资源时,如果该信号量已被一低优先级线程持有,而这个低优先级线程在运行过程中可能又被其它一些中等优先级的线程抢占,因此造成高优先级线程被许多具有较低优先级的线程阻塞,实时性难以得到保证。例如:有优先级为A、B和C的三个线程,优先级A> B > C。线程A,B处于挂起状态,等待某一事件触发,线程C正在运行,此时线程C开始使用某一共享资源M。在使用过程中,线程A等待的事件到来,线程A转为就绪态,因为它比线程C优先级高,所以立即执行。但是当线程A要使用共享资源M时,由于其正在被线程C使用,因此线程A被挂起切换到线程C运行。如果此时线程B等待的事件到来,则线程B转为就绪态。由于线程B的优先级比线程C高,因此线程B开始运行,直到其运行完毕,线程C才开始运行。只有当线程C释放共享资源M后,线程A才得以执行。在这种情况下,优先级发生了翻转,线程B先于线程A运行。这样便不能保证高优先级线程的响应时间。

下面在机智云gokit智能硬件开发板上演示使用信号量是如何发生优先级反转的,程序中创建了3个不同优先级的线程:线程1、线程2和线程3,优先级线程1> 线程2 > 线程3。线程1,2处于挂起状态,等待延时事件的触发,线程3正在运行,此时线程3开始使用某一共享资源:一个初始值为1的二值信号量。在使用过程中,线程1等待的事件到来,线程1转为就绪态,因为它比线程3优先级高,所以立即执行。但是当线程1要使用共享资源时,由于其正在被线程3使用,因此线程1被挂起切换到线程3运行。如果此时线程2等待的事件到来,则线程2转为就绪态。由于线程2的优先级比线程3高,因此线程2开始运行,直到其运行完毕,线程3才开始运行。只有当线程3释放共享资源后,线程1才得以执行。

注意:在线程3中使用了无限占用CPU的“死等”延时,而不是内核延时函数。

  1. /**
  2.   ****************************************************
  3.   * File Name          : main.c
  4.   * Description        : Main program body
  5.   ****************************************************
  6.   ** This notice applies to any and all portions of this file
  7.   * that are not between comment pairs USER CODE BEGIN and
  8.   * USER CODE END. Other portions of this file, whether
  9.   * inserted by the user or by software development tools
  10.   * are owned by their respective copyright owners.
  11.   *
  12.   * COPYRIGHT(c) 2017 STMicroelectronics
  13.   *
  14.   * Redistribution and use in source and binary forms, with or without modification,
  15.   * are permitted provided that the following conditions are met:
  16.   *   1. Redistributions of source code must retain the above copyright notice,
  17.   *      this list of conditions and the following disclaimer.
  18.   *   2. Redistributions in binary form must reproduce the above copyright notice,
  19.   *      this list of conditions and the following disclaimer in the documentation
  20.   *      and/or other materials provided with the distribution.
  21.   *   3. Neither the name of STMicroelectronics nor the names of its contributors
  22.   *      may be used to endorse or promote products derived from this software
  23.   *      without specific prior written permission.
  24.   *
  25.   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  26.   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27.   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  28.   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  29.   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  30.   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  31.   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  32.   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  33.   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  34.   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  35.   *
  36.   ****************************************************
  37.   */
  38. /* Includes ------------------------------------------------------------------*/
  39. #include "main.h"
  40. #include "STM32f1xx_hal.h"
  41. #include "usart.h"
  42. #include "gpio.h"

  43. /* USER CODE BEGIN Includes */
  44. #include "rtthread.h"
  45. #include "string.h"
  46. /* USER CODE END Includes */

  47. /* Private variables ---------------------------------------------------------*/

  48. /* USER CODE BEGIN PV */
  49. /* Private variables ---------------------------------------------------------*/

  50. /* USER CODE END PV */

  51. /* Private function prototypes -----------------------------------------------*/
  52. void SystemClock_Config(void);

  53. /* USER CODE BEGIN PFP */
  54. /* Private function prototypes -----------------------------------------------*/

  55. /* USER CODE END PFP */

  56. /* USER CODE BEGIN 0 */
  57. //重映射串口1到rt_kprintf
  58. void rt_hw_console_output(const char *str)
  59. {
  60.     /* empty console output */
  61.         char aa='\r';
  62.           rt_enter_critical();

  63.                 while(*str!='\0')
  64.                 {
  65.                         if(*str=='\n')
  66.                         {
  67.                                 HAL_UART_Transmit(&huart1, (uint8_t *)&aa, 1, 10);
  68.                         }
  69.                                 HAL_UART_Transmit(&huart1, (uint8_t *)(str++), 1, 10);
  70.                 }
  71.                
  72.                 rt_exit_critical();
  73. }

  74. void rt_hw_us_delay(int us)
  75. {
  76.     rt_uint32_t delta;

  77.     /* 获得延时经过的tick数 */
  78.     us = us * (SysTick->LOAD/(1000000/RT_TICK_PER_SECOND));

  79.     /* 获得当前时间 */
  80.     delta = SysTick->VAL;

  81.     /* 循环获得当前时间,直到达到指定的时间后退出循环 */
  82.     while (delta - SysTick->VAL< us);
  83. }

  84. void rt_hw_ms_delay(int ms)
  85. {
  86.         int i=0,j=0;
  87.         for(j=0;j<ms;j++)
  88.         {
  89.                 for (i=0;i<2;i++)
  90.                 rt_hw_us_delay(500);
  91.         }
  92. }
  93. uint32_t rt_hw_delay_Init(void)
  94. {
  95. #if !defined(STM32F0xx)
  96.         uint32_t c;
  97.         
  98.     /* Enable TRC */
  99.     CoreDebug->DEMCR &= ~0x01000000;
  100.     CoreDebug->DEMCR |=  0x01000000;
  101.         
  102.     /* Enable counter */
  103.     DWT->CTRL &= ~0x00000001;
  104.     DWT->CTRL |=  0x00000001;
  105.         
  106.     /* Reset counter */
  107.     DWT->CYCCNT = 0;
  108.         
  109.         /* Check if DWT has started */
  110.         c = DWT->CYCCNT;
  111.         
  112.         /* 2 dummys */
  113.         __ASM volatile ("NOP");
  114.         __ASM volatile ("NOP");
  115.         
  116.         /* Return difference, if result is zero, DWT has not started */
  117.         return (DWT->CYCCNT - c);
  118. #else
  119.         /* Return OK */
  120.         return 1;
  121. #endif
  122. }
  123. void rt_hw_delay_us(__IO uint32_t micros)
  124. {
  125. #if !defined(STM32F0xx)
  126.         uint32_t start = DWT->CYCCNT;
  127.         
  128.         /* Go to number of cycles for system */
  129.         micros *= (HAL_RCC_GetHCLKFreq() / 1000000);
  130.         
  131.         /* Delay till end */
  132.         while ((DWT->CYCCNT - start) < micros);
  133. #else
  134.         /* Go to clock cycles */
  135.         micros *= (SystemCoreClock / 1000000) / 5;
  136.         
  137.         /* Wait till done */
  138.         while (micros--);
  139. #endif
  140. }
  141. void rt_hw_delay_ms(__IO uint32_t mills)
  142. {
  143.         rt_hw_delay_us(1000*mills);
  144. }
  145. /*
  146. * 程序清单:有优先级为A、B和C的三个线程,优先级A> B > C。
  147. 线程A,B处于挂起状态,等待某一事件触发,线程C正在运行,此时线程C开始使用某一共享资源M。
  148. 在使用过程中,线程A等待的事件到来,线程A转为就绪态,因为它比线程C优先级高,所以立即执行。
  149. 但是当线程A要使用共享资源M时,由于其正在被线程C使用,因此线程A被挂起切换到线程C运行。
  150. 如果此时线程B等待的事件到来,则线程B转为就绪态。
  151. 由于线程B的优先级比线程C高,因此线程B开始运行,直到其运行完毕,线程C才开始运行。
  152. 只有当线程C释放共享资源M后,线程A才得以执行。
  153. 在这种情况下,优先级发生了翻转,线程B先于线程A运行。
  154. *
  155. */

  156. /* 指向线程控制块的指针 */
  157. static rt_thread_t tid1 = RT_NULL;
  158. static rt_thread_t tid2 = RT_NULL;
  159. static rt_thread_t tid3 = RT_NULL;

  160. struct rt_semaphore sem;

  161. /* 线程1入口 */
  162. void thread1_entry(void* parameter)
  163. {

  164.     while(1)
  165.     {
  166.                         rt_thread_delay(100);
  167.                         rt_sem_take(&sem, RT_WAITING_FOREVER);
  168.                         rt_kprintf("thread1 is running.\n");       
  169.                         rt_sem_release(&sem);                                       
  170.     }

  171. }
  172. /* 线程2入口 */
  173. void thread2_entry(void* parameter)
  174. {

  175.     while(1)
  176.     {
  177.                         rt_thread_delay(300);
  178.                         rt_kprintf("thread2 is running.\n");                               
  179.     }

  180. }
  181. /* 线程3入口 */
  182. void thread3_entry(void* parameter)
  183. {

  184.     while(1)
  185.     {
  186.                         rt_sem_take(&sem, RT_WAITING_FOREVER);
  187.                         rt_kprintf("thread3 is running.\n");
  188.                         rt_hw_delay_ms(500);                               
  189.                         rt_sem_release(&sem);       
  190.     }

  191. }
  192. /* USER CODE END 0 */

  193. int main(void)
  194. {

  195.   /* USER CODE BEGIN 1 */

  196.   /* USER CODE END 1 */

  197.   /* MCU Configuration----------------------------------------------------------*/

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

  200. //  /* USER CODE BEGIN Init */

  201. //  /* USER CODE END Init */

  202. //  /* Configure the system clock */
  203. //  SystemClock_Config();

  204. //  /* USER CODE BEGIN SysInit */

  205. //  /* USER CODE END SysInit */

  206. //  /* Initialize all configured peripherals */
  207. //  MX_GPIO_Init();
  208. //  MX_USART1_UART_Init();

  209.   /* USER CODE BEGIN 2 */
  210.        
  211.         rt_sem_init(&sem, "sem",   1,    RT_IPC_FLAG_FIFO);//初始化信号量,初值为0

  212. /* 创建线程1 */
  213.     tid1 = rt_thread_create("thread1",
  214.         thread1_entry, /* 线程入口是thread_entry */
  215.         RT_NULL, /* 入口参数是RT_NULL */
  216.         512, //堆栈大小
  217.                                 2, //优先级
  218.                                 20);//时间片
  219.     if (tid1 != RT_NULL)
  220.         rt_thread_startup(tid1);
  221.                
  222.                 /* 创建线程2 */
  223.     tid2 = rt_thread_create("thread2",
  224.         thread2_entry, /* 线程入口是thread_entry */
  225.         RT_NULL, /* 入口参数是RT_NULL */
  226.         512, //堆栈大小
  227.                                 4, //优先级
  228.                                 20);//时间片
  229.     if (tid2 != RT_NULL)
  230.         rt_thread_startup(tid2);
  231.                
  232.                 /* 创建线程3 */
  233.     tid3 = rt_thread_create("thread3",
  234.         thread3_entry, /* 线程入口是thread_entry */
  235.         RT_NULL, /* 入口参数是RT_NULL */
  236.         512, //堆栈大小
  237.                                 6, //优先级
  238.                                 20);//时间片
  239.     if (tid3 != RT_NULL)
  240.         rt_thread_startup(tid3);
  241.                
  242.         rt_hw_delay_Init();
  243.                
  244.         printf("\r\n机智云  只为智能硬件而生\r\n");
  245.         printf("Gizwits Smart Cloud for Smart Products\r\n");
  246.         printf("链接|增值|开放|中立|安全|自有|自由|生态\r\n");
  247.         printf("www.gizwits.com\r\n");
  248.         printf("\r\nGokit RT-Thread Demo\r\n\r\n");
  249.        
  250.         return 0;
  251.        
  252.        


  253.   /* USER CODE END 2 */

  254.   /* Infinite loop */
  255.   /* USER CODE BEGIN WHILE */
  256. //  while (1)
  257. //  {
  258.   /* USER CODE END WHILE */

  259.   /* USER CODE BEGIN 3 */
  260. //               
  261. //  }
  262.   /* USER CODE END 3 */

  263. }

  264. /** System Clock Configuration
  265. */
  266. void SystemClock_Config(void)
  267. {

  268.   RCC_OscInitTypeDef RCC_OscInitStruct;
  269.   RCC_ClkInitTypeDef RCC_ClkInitStruct;

  270.     /**Initializes the CPU, AHB and APB busses clocks
  271.     */
  272.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  273.   RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  274.   RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  275.   RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  276.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  277.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  278.   RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  279.   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  280.   {
  281.     _Error_Handler(__FILE__, __LINE__);
  282.   }

  283.     /**Initializes the CPU, AHB and APB busses clocks
  284.     */
  285.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  286.                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  287.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  288.   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  289.   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  290.   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  291.   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  292.   {
  293.     _Error_Handler(__FILE__, __LINE__);
  294.   }

  295.     /**Configure the Systick interrupt time
  296.     */
  297.   HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

  298.     /**Configure the Systick
  299.     */
  300.   HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  301.   /* SysTick_IRQn interrupt configuration */
  302.   HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
  303. }

  304. /* USER CODE BEGIN 4 */

  305. /* USER CODE END 4 */

  306. /**
  307.   * @brief  This function is executed in case of error occurrence.
  308.   * @param  None
  309.   * @retval None
  310.   */
  311. void _Error_Handler(char * file, int line)
  312. {
  313.   /* USER CODE BEGIN Error_Handler_Debug */
  314.   /* User can add his own implementation to report the HAL error return state */
  315.   while(1)
  316.   {
  317.   }
  318.   /* USER CODE END Error_Handler_Debug */
  319. }

  320. #ifdef USE_FULL_ASSERT

  321. /**
  322.    * @brief Reports the name of the source file and the source line number
  323.    * where the assert_param error has occurred.
  324.    * @param file: pointer to the source file name
  325.    * @param line: assert_param error line source number
  326.    * @retval None
  327.    */
  328. void assert_failed(uint8_t* file, uint32_t line)
  329. {
  330.   /* USER CODE BEGIN 6 */
  331.   /* User can add his own implementation to report the file name and line number,
  332.     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  333.   /* USER CODE END 6 */

  334. }

  335. #endif

  336. /**
  337.   * @}
  338.   */

  339. /**
  340.   * @}
  341. */

  342. /**************** (C) COPYRIGHT STMicroelectronics ****END OF FILE***/
复制代码

串口打印信息如下:可以看到优先级低的线程2却比优先级高的线程1先运行了。。。
信号量优先级反转.png

源码下载:
Gokit_RT-Thread.zip (8.65 MB, 下载次数: 1, 售价: 1 金钱)
1、机智云QQ群:G1群:104975951 G2群:491509598 G3群:287087942
机智云爱好者-APP开发群: 599735135
QQ群目前非常活跃,欢迎大家参与进来,交流,讨论,答疑,解惑~~
2、机智云微信公众号: 机智云 gizwits、 机智云智能宠物屋go-iot
关注机智云Gizwits官方公众号随时掌握最新资讯和活动信息
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

加入Q群 返回顶部

© 2011-2017 Gizwits ( 粤ICP备11090211号 )

快速回复 返回顶部 返回列表