1. Mutually exclusive semaphore
A mutually exclusive semaphore is actually a binary semaphore with priority inheritance, which is mostly used for mutually exclusive access to shared resources between different tasks.
2. Mutual exclusion semaphore reduces the impact of priority flipping
When a mutex semaphore is being used by task L, and a task H also attempts to obtain the mutex semaphore, it will be blocked. However, this task H will raise the priority of task L to the same priority as itself. This process is priority inheritance. Priority inheritance reduces the time that high-priority tasks are blocked as much as possible and minimizes the impact of “priority flips” that have already occurred.
serial number |
illustrate |
---|---|
(1) |
Task H and task M are in a blocked state, waiting for a certain event to occur. At this time, task L is running. |
(2) |
At this time, if task L wants to access the shared resource, it needs to obtain a mutex lock. At the same time, the system will pull the priority of L to the same as H. |
(3) |
Task L successfully acquires the mutex lock. At this time, the lock is already occupied, and Task L begins to access the shared resource. |
(4) |
When the time slice cycle time is up, task H is ready and starts running. |
(5) |
Task H starts running |
(6) |
At this time, task H wants to access the shared resource, so it needs to acquire the lock, but the lock is not released at this time, so task H blocks and waits for the mutex lock to be released. |
(7) |
Since L and H have the same priority at this time and are greater than M, task L continues to run. |
(8) |
Task L releases the mutex lock |
(9) |
Task H obtains the mutex lock resource, unblocks it and starts running. At this time, L’s priority returns to its original value. |
(10) |
Task H finishes running and enters blocking |
(11) |
Task M is now the highest priority in the ready list and starts running. |
(12) |
Task M finishes running and enters blocking |
(13) |
Task L starts a new round of running |
3. API function of mutually exclusive semaphore
3.1, Create
|
|
|
|
Parameters |
Description |
void |
null |
Return value |
Description |
NULL |
Failed to create mutex semaphore |
other values |
Mutex semaphore created successfully |
3.2, Release
Like the binary semaphore, please refer to the binary semaphore release function
3.3. Obtain
Like the binary semaphore, please refer to the binary semaphore release function
4. Experiment
4.1. Experimental design
The experimental design is the same as the process in the “Priority Flip” chapter, except that the “binary semaphore” is replaced by a “mutually exclusive semaphore”.
4.2, Experimental code
//Task 1 k_task_handle_t task1_handle; //Task 1 handle #define TSK1_PRIO 2 //Task 1 priority #define TASK1_STK_SIZE (1*512) //Stack size allocated by task 1 //Task 2 k_task_handle_t task2_handle; //Task 2 handle #define TSK2_PRIO 3 //Task 2 priority #define TASK2_STK_SIZE (1*512) //Stack size allocated by task 2 //Task 3 k_task_handle_t task3_handle; //Task 3 handle #define TSK3_PRIO 4 //Task 3 priority #define TASK3_STK_SIZE (1*512) //Stack size allocated by task 3 //Start task k_task_handle_t start_task_handle; //Start task handle #define START_TSK_PRIO 1 //Start task priority #define START_TSK_STK_SIZE 1024 //Start task allocated stack size #define TEST_TIME_QUANTA 100 k_mutex_handle_t g_Mutex; //mutex void task1(void) { uint32_t i = 0; while(1) { csi_kernel_mutex_lock(g_Mutex,portMAX_DELAY); my_printf("Low task running!\r\ "); for(i=0;i<2000000;i + + ) { taskYIELD(); } csi_kernel_mutex_unlock(g_Mutex); csi_kernel_delay_ms(1000); } } void task2(void) { while(1) { my_printf("Middle task running!\r\ "); csi_kernel_delay_ms(1000); } } void task3(void) { while(1) { csi_kernel_delay_ms(500); my_printf("High task Pend Semaphore\r\ "); csi_kernel_mutex_lock(g_Mutex, portMAX_DELAY); my_printf("High task running!\r\ "); csi_kernel_mutex_unlock(g_Mutex); csi_kernel_delay_ms(500); } } void start_task(void) { //Enter the critical section taskENTER_CRITICAL(); //Create mutex g_Mutex = csi_kernel_mutex_new(); if(g_Mutex == NULL) { printf("fail to create mutex.\ "); } //Create task 1 csi_kernel_task_new((k_task_entry_t)task1, "task1", NULL, TSK1_PRIO, TEST_TIME_QUANTA, NULL, TASK1_STK_SIZE, & amp;task1_handle); if (task1_handle == NULL) { csi_kernel_sched_resume(0); my_printf("Fail to create task 1!\r\ "); } //Create task 2 csi_kernel_task_new((k_task_entry_t)task2, "task2", NULL, TSK2_PRIO, TEST_TIME_QUANTA, NULL, TASK2_STK_SIZE, & amp;task2_handle); if (task2_handle == NULL) { csi_kernel_sched_resume(0); my_printf("Fail to create task 2!\r\ "); } //Create task 3 csi_kernel_task_new((k_task_entry_t)task3, "task3", NULL, TSK3_PRIO, TEST_TIME_QUANTA, NULL, TASK3_STK_SIZE, & amp;task3_handle); if (task3_handle == NULL) { csi_kernel_sched_resume(0); my_printf("Fail to create task 3!\r\ "); } //Delete the start task if(0 != csi_kernel_task_del(csi_kernel_task_get_cur())) { my_printf("Fail to delete start_task!\r\ "); } else { my_printf("start_task is deleted!\r\ "); } //Exit critical section taskEXIT_CRITICAL(); } void freertos_demo(void) { my_printf("\r\ -->this is freertos task test demo!!!\r\ "); //print message //system initialization csi_kernel_init(); //Create a start task csi_kernel_task_new((k_task_entry_t)start_task, "start_task", 0, START_TSK_PRIO, 0, 0, START_TSK_STK_SIZE, & amp;start_task_handle); //Task scheduling starts csi_kernel_start(); }
4.3. Experimental phenomena
-
Task 3 (High task) will soon get a chance to run and will not be delayed for a long time by Task 2 (Middle task).