1. 程式人生 > >FreeRTOS筆記(五)任務狀態

FreeRTOS筆記(五)任務狀態

文章目錄


上一文連結:FreeRTOS筆記(四)初識任務

01 - 任務的狀態

  任務被建立後,它可能正在執行,可能暫停執行,任務有狀態之分是由於排程器的存在,排程器需要決定哪些任務可以去執行,於是在FreeRTOS中任務具有4種狀態,分別是就緒態、執行態、阻塞態和掛起態,它們之間的轉化關係如下:

  4個狀態的含義如下:
  就緒態:已經可以執行,等待排程器的切入
  執行態:正在佔用CPU執行
  阻塞態

:等待某個事件的到來,定時或者同步
  掛起態:退出排程系統,排程器不可見,只能使用vTaskSuspend()掛起和vTaskResume()喚醒後進入就緒態

  雖然官方文件只描述4個狀態,但小白認為應該是5種狀態,第5種是殭屍態,指在任務被刪除後,其TCB控制塊扔保留一段時間,等待核心檢查和回收資源,在核心沒有處理之前,任務其實並沒有被完全刪除,但是再也不能被排程器排程,這稱為殭屍態,在Linux下的程序是存在殭屍態的,而從FreeRTOS的API中就可以看出,FreeRTOS的任務也存在殭屍態。

  要使用eTaskGetState()函式,就要在FreeRTOSConfig.h中配置INCLUDE_eTaskGetState

巨集為1

	#define INCLUDE_eTaskGetState			1

02 - tick時鐘和排程器

  排程器本身也是一段程式,任務需要排程器安排執行順序,那麼排程器本身就需要被執行,這個執行由一個稱為心跳時鐘(tick)的中斷觸發,tick時鐘的頻率需要在FreeRTOSConfig.h檔案中配置,單位是HZ,比如本例程中配置configTICK_RATE_HZ為1000,那麼中斷頻時間是1000/1000 = 1ms,每隔1ms,FreeRTOS就會進入tick中斷,觸發排程器進行工作。

	#define configTICK_RATE_HZ				( ( TickType_t )
1000 )

  排程器被觸發後,會根據事先設定好的排程演算法進行工作,FreeRTOS使用的排程演算法有優先順序搶佔式排程和協作式排程
  優先順序搶佔式排程演算法,給每一個任務分配一個優先順序,排程器每次都選擇優先順序最高的任務執行,如果優先順序相同,就採用時間片輪流執行,每個任務執行一個時間片後切出,再切入下一個任務。
  協作式排程演算法,只可能在執行態任務進入阻塞態或是執行態任務顯式呼叫taskYIELD()主動讓出CPU時,才會進行上下文切換。
  排程演算法的配置也是在FreeRTOSConfig.h中進行。

	#define configUSE_PREEMPTION			1	//0-協作排程,1-搶佔式排程

03 - 任務狀態測試

  下面用優先順序搶佔式排程演算法來測試一下任務的各種狀態,其中掛起和喚醒的API如下:

專案 Value
vTaskSuspend() 掛起一個任務
vTaskResume() 喚醒一個任務

   測試內容:一共有5個任務,分別是start和ABCD,start優先順序最高,其餘優先順序遞增,任務的工作如下:
   start:負責建立另外4個任務,輸出所有任務的狀態,然後刪除自己
   A:輸出所有任務的狀態,開啟LED
   B:輸出所有任務的狀態,關閉LED,使用vTaskDelay()阻塞自己
   C:輸出所有任務的狀態使用vTaskDelay()阻塞自己,然後間隔使用vTaskResume喚醒D
   D:輸出所有任務的狀態,使用vTaskSuspend()掛起自己

/* start 任務 */
void start_task(void *pParam)
{
	//進入臨界區
	taskENTER_CRITICAL();
	
	xTaskCreate( (TaskFunction_t)a_task,
				 (const char*)"a_task",
				 (uint16_t)A_STACK_SIZE,
				 (void*)NULL,
				 (UBaseType_t)A_TASK_PRIO,
				 (TaskHandle_t*)&aTask_Handler
	);
				 
	xTaskCreate( (TaskFunction_t)b_task,
				 (const char*)"b_task",
				 (uint16_t)B_STACK_SIZE,
				 (void*)NULL,
				 (UBaseType_t)B_TASK_PRIO,
				 (TaskHandle_t*)&bTask_Handler
	);
	
	xTaskCreate( (TaskFunction_t)c_task,
				 (const char*)"c_task",
				 (uint16_t)C_STACK_SIZE,
				 (void*)NULL,
				 (UBaseType_t)C_TASK_PRIO,
				 (TaskHandle_t*)&cTask_Handler
	);
			
	xTaskCreate( (TaskFunction_t)d_task,
				 (const char*)"d_task",
				 (uint16_t)D_STACK_SIZE,
				 (void*)NULL,
				 (UBaseType_t)D_TASK_PRIO,
				 (TaskHandle_t*)&dTask_Handler
	);
	
	
	print_state("start",eTaskGetState(StartTask_Handler));
	print_state("A",eTaskGetState(aTask_Handler));
	print_state("B",eTaskGetState(bTask_Handler));
	print_state("C",eTaskGetState(cTask_Handler));
	print_state("D",eTaskGetState(dTask_Handler));
	printf("\r\n");

	//退出臨界區			 
	taskEXIT_CRITICAL();
				 
	//刪除自己
	vTaskDelete(NULL);
}	

/* A 任務 */
void a_task(void *pParam)
{
	for(;;)
	{
		print_all_state("A");
		
		//開啟LED
		GPIO_ResetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);
	}
}		

/* B 任務 */
void b_task(void *pParam)
{
	for(;;)
	{
		//輸出
		print_all_state("B");
		
		//關閉LED
		GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);
		
		//阻塞自己
		vTaskDelay(10);
	}	
}		

/* C 任務 */
void c_task(void *pParam)
{
	for(;;)
	{
		//輸出
		print_all_state("C");
		
		//阻塞自己
		vTaskDelay(10);
		
		//喚醒D
		vTaskResume(dTask_Handler);
	}	
}		

/* D 任務 */
void d_task(void *pParam)
{
	for(;;)
	{
		//輸出
		print_all_state("D");
		
		//掛起自己
		vTaskSuspend(NULL);
	}	
}		

  其中print_all_state()和print_state()是為了方便輸出狀態,程式碼如下:

static void print_state(char *msg,eTaskState state)
{
	printf("%s-",msg);
	switch(state)
	{
		case eRunning:	printf("run");break;
		case eReady:	printf("ready");break;
		case eBlocked:	printf("block");break;
		case eSuspended:printf("suspend");break;
		case eDeleted:	printf("delete");break;
		default:		printf("5");break;
	}
	printf("  ");
}

static void print_all_state(char *msg)
{
		taskENTER_CRITICAL();
		
		printf("%s:",msg);
		
		print_state("start",eTaskGetState(StartTask_Handler));
		print_state("A",eTaskGetState(aTask_Handler));
		print_state("B",eTaskGetState(bTask_Handler));
		print_state("C",eTaskGetState(cTask_Handler));
		print_state("D",eTaskGetState(dTask_Handler));
	
		printf("\r\n");
				 
		taskEXIT_CRITICAL();		
}

  執行結果

  因為延遲太短,看不到LED的閃爍,但是從串列埠可以看出,各個任務有序執行,每個狀態都出現了,狀態之間的轉換幾乎在一瞬間,執行時序圖可以簡述如下:

04 - 總結

  • 任務有4種(5種)狀態,分別為就緒態、執行態、阻塞態、掛起態(殭屍態)
  • 排程器程式在每個tick時鐘中斷裡被執行
  • FreeRTOS排程器有兩種排程演算法,搶佔式和協作式