Embedded real-time operating system 7 - task priority table

Posted by essexboy on Thu, 23 Dec 2021 10:01:11 +0100

1. Purpose of task priority table

The priority table is used to indicate whether there are ready tasks under the corresponding priority. The operating system kernel always selects the task execution from the highest priority in the priority table, and the ready table is dynamically updated.
For example, let's understand the task priority table: Driving School and learning to drive. There are cars, coaches and students in the driving school. We assume that the driving school coach has only one car. The people who enter the car for practice are in the running state, and the students waiting in line are in the ready state. Due to the different registration fees, the students are divided into VIP level and ordinary level. When the coach asks the students to get on the bus, the VIP level students are given priority.

The coach made a queuing table for the students:

The coach first judges whether there are VIP level students. When there are VIP level students, the VIP level value in the level table is 1. The coach will select one of the VIP students to practice on the bus. Only when the VIP level students have finished practicing and give up the practice, the VIP level value in the level table is 0. The coach will judge whether there are ordinary level students. When there are ordinary level students, the ordinary level value in the level table is 1, and the coach will select one of the ordinary level students to practice.

According to the description, we conclude that the priority table has two characteristics:
1. The priority table indicates whether there are ready tasks under the corresponding priority.
2. The task status will be updated dynamically, and the priority table will be updated dynamically.

2. Implementation of task priority table

The task priority table is used to indicate whether there are ready tasks under the corresponding priority, and each element of the priority table is used to indicate whether there are ready tasks under the corresponding priority.
Therefore, the static array method is used to implement a task priority table, which is implemented in C language as follows:

The relationship diagram between task priority table and task is as follows:

Task is the element with the largest subscript in the array of task priority table_ priority_ Table [15] represents the highest priority and task_priority_table[0] represents the lowest priority. The task priority table has the following three operations:
1. Priority set to 1.
2. Priority 0.
3. Find the highest priority.

C language implementation priority is set to 1:

C language implementation priority 0:

C language realizes the highest priority of searching:

The method of using static array can realize a ready table. Is there a more efficient method?
There are some answers. A priority table can be realized by using BIT bit table, which is more efficient. The C language implementation is as follows:

A u16 type of data contains 16 bits, and each bit can correspond to a priority. Set the highest bit (bit 15) as the highest priority and the lowest bit (bit 0) as the lowest priority. The relationship between task priority table and task is as follows:

C language implementation priority is set to 1:

C language implementation priority is set to 0:

Just mentioned that the BIT bit table method is more efficient. The efficiency of this method is reflected in the operation of finding the highest priority. Some CPU s support the calculation of the leading zero number instruction CLZ. Using the calculation of the leading zero number instruction CLZ can speed up the operation of finding the highest priority and realize the highest priority:

Since the operation of finding the highest task priority is very frequent in the operation of the operating system kernel, the combination of BIT bit table and calculation of leading zero number instructions can improve the operation efficiency.

3.FreeRTOS task priority table analysis

The codes related to the task priority table in the FreeRTOS source code are as follows:

FreeRTOS uses a 32-bit uxTopReadyPriority as the task priority bit table. Each bit in uxTopReadyPriority corresponds to a priority. The highest bit (bit 31) is the highest priority and the lowest bit (bit 0) is the lowest priority. When each bit is 1, it means that there are ready tasks in the task priority, and when bit is 0, it means that there are no ready tasks in the task priority.

The uxTopReadyPriority task priority table has three operations: set the priority to 1, clear the priority to 0, and find the highest priority.

portRESET_READY_PRIORITY ,portRECORD_READY_PRIORITY ,portGET_HIGHEST_PRIORITY

Where the highest priority is found using_ clz computes the leading zero number instruction.

Priority set 1 policy
When a task is added to the ready table, the corresponding bit position is 1, regardless of the total number of ready tasks under the priority, because after adding a task, the total number of ready tasks under the priority must be greater than or equal to 1.
The function calling process with priority set to 1 is as follows:

prvAddTaskToReadyList -> taskRECORD_READY_PRIORITY -> portRECORD_READY_PRIORITY

Priority clearing 0 policy
When a task is removed from the ready table, the total number of ready tasks under the priority needs to be reduced by one. If the total number of ready tasks under the priority is 0, the priority needs to be cleared to 0. The code is as follows:

/*	Remove the current task from the ready table */
if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
	/*	Priority table corresponding bit clear 0 */
	portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); 
else
{
	mtCOVERAGE_TEST_MARKER();
}

Find highest priority
When task switching is required, the highest priority needs to be found. The function call process is as follows:

taskSELECT_HIGHEST_PRIORITY_TASK ->portGET_HIGHEST_PRIORITY

The relationship block diagram between FreeRTOS task priority table and ready table is as follows:

4. Update of task priority table

The above describes three operations of the task priority table: set priority to 1, clear priority to 0, and find the highest priority. Then the following problems arise:
Under what circumstances will the task priority table be operated? When is the task priority table updated?
Referring to the FreeRTOS source code, the operation update points of the priority table are as follows (clearing 0 will determine the total number of ready tasks under this priority):

xTaskCreate
xTaskCreate is used to create a task. The calling process of the system function is as follows:

xTaskCreate ->prvAddNewTaskToReadyList  -> prvAddTaskToReadyList  ->taskRECORD_READY_PRIORITY  -> portRECORD_READY_PRIORITY

Ready table change: the task is inserted at the end of the priority list corresponding to the ready table, and the corresponding priority position is 1.

vTaskDelete
vTaskDelete is used to delete a task. The calling process of the system function is as follows:

vTaskDelete -> taskRESET_READY_PRIORITY -> portRESET_READY_PRIORITY

Ready table change: the task is removed from the priority list corresponding to the ready table, and the corresponding priority bit is cleared to 0.

vTaskSuspend
Vtask suspend is used to suspend a task. The calling process of the system function is as follows:

vTaskSuspend -> taskRESET_READY_PRIORITY-> portRESET_READY_PRIORITY

Ready table change: the task is removed from the corresponding priority list, and the corresponding priority bit is cleared to 0.

vTaskResume
Vtask resume is used to restore a task. The calling process of the system function is as follows:

vTaskResume -> prvAddTaskToReadyList -> portRECORD_READY_PRIORITY

Ready table change: the task is inserted into the tail of the corresponding priority list in the ready table, and the corresponding priority position is 1.

vTaskPrioritySet
Vtask priorityset is used to change a task priority. The calling process of the system function is as follows:

vTaskPrioritySet ->uxListRemove -> prvAddTaskToReadyList -> portRECORD_READY_PRIORITY

Ready table change: remove the task from the current priority list in the ready table, and insert the task at the end of the set priority list, corresponding to priority position 1.

vTaskDelay
Vtask delay is used to move the current task from the ready table to the waiting table. The calling process of this system function is as follows:

vTaskDelay -> taskRESET_READY_PRIORITY -> portRESET_READY_PRIORITY

Ready table change: the task is removed from the ready table and inserted into the waiting table, and the corresponding priority bit is cleared to 0.

xQueueSemaphoreTake
The function of xQueueSemaphoreTake is to wait for a signal in the current task. The calling process of the system function is as follows:

xQueueSemaphoreTake ->  vListInsert -> prvAddCurrentTaskToDelayedList -> portRESET_READY_PRIORITY

Change of ready table: insert the current task into the pending, remove the current task from the ready table, and finally insert the current task into the waiting table, with the corresponding priority bit cleared to 0.

xQueueGenericSend
The function of xQueueSemaphoreTake is to generate a signal. The calling process of the system function is as follows:

xQueueGenericSend -> xTaskRemoveFromEventList-> uxListRemove -> prvAddTaskToReadyList -> portRECORD_READY_PRIORITY

Ready table change: the first task in the pending table will be removed and inserted into the tail of the corresponding priority list in the ready table, corresponding to priority position 1.

5. Source code

typedef unsigned long UBaseType_t;

	PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority 		= tskIDLE_PRIORITY;  

	/* Remove the task from the ready list before adding it to the blocked list
	as the same list item is used for both lists. */
	if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
	{
		/* The current task must be in a ready list, so there is no need to
		check, and the port reset macro can be called directly. */
		portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); /*lint !e931 pxCurrentTCB cannot change as it is the calling task.  pxCurrentTCB->uxPriority and uxTopReadyPriority cannot change as called with scheduler suspended or in a critical section. */
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )
	#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )

	/*-----------------------------------------------------------*/

	#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) )

To be continued
The real-time operating system family will be continuously updated
Creation is not easy. I hope friends like, forward, comment and pay attention.
Your likes, forwarding, comments and attention will be the driving force for my continuous update
Author: Li Wei
Github: liyinuoman2017

Topics: C Linux IoT ARM RTOS