Linux thread scheduling strategy and priority experiment
What is thread scheduling policy?
The Linux kernel has three scheduling algorithms:
1,SCHED_OTHER time-sharing scheduling strategy,
2,SCHED_FIFO real-time scheduling strategy, first come first serve
3,SCHED_RR, real-time scheduling strategy, time slice rotation
Where, SCHED_FIFO and SCHED_RR belongs to real-time policy, SCHED_OTHER belongs to time-sharing strategy.
The real-time policy thread will be called preferentially. The real-time policy thread determines the scheduling weight according to the real-time priority. The time-sharing policy thread determines the weight through nice and counter values. The smaller the nice, the larger the counter, and the greater the probability of being scheduled, that is, the thread that once used the least cpu will be scheduled preferentially.
Three Scheduling Strategies
Time sharing scheduling strategy
SCHED_OTHER time-sharing scheduling strategy:
Non real time, unable to set priority
Real time scheduling strategy
SCHED_FIFO real-time scheduling strategy:
First come, first served. Once the cpu is occupied, it runs all the time. Run until a higher priority task arrives or you give up
SCHED_RR real time scheduling policy:
Time slice rotation. When the time slice of the process runs out, the system will reallocate the time slice and place it at the end of the ready queue. It is placed at the end of the queue to ensure the fair scheduling of all RR tasks with the same priority
Scheduling strategy combination
When all tasks adopt linux time-sharing scheduling strategy (SCHED_OTHER):
When creating, specify the priority nice value, determine the execution time (counter) on the cpu according to the nice value, and select the run according to the calculation (counter + 20 NICE) result of the dynamic priority of each task. When the time slice is used up (the counter is reduced to 0) or the cpu is actively abandoned, the task is placed at the end of the ready queue (the time slice is used up) or in the waiting queue (the cpu is abandoned due to waiting for resources).
When FIFO is used for all tasks:
The task with higher priority runs first, and the task will occupy the CPU until the task with higher priority is ready or voluntarily abandoned. The same priority first in queue runs first.
When RR is used for all tasks
The first two strategies are integrated. The one with high priority runs first, the one with the same priority is divided into time slices, and the one that runs out of time slices goes to the end of the ready queue.
The task contains both real-time and time-sharing
When the real-time process is ready, if the current cpu is running a non real-time process, the real-time process immediately preempts the non real-time process.
Test experiment
The main purpose is to test the preemption of threads under different scheduling strategies and different priority settings.
The test method is mainly to create three threads after setting the thread policy and priority, observe their log output, and use busybox top to check the CPU usage.
The code for modifying thread properties is as follows:
param1.sched_priority = 40; //Modify priority pthread_attr_setinheritsched(&attr1,PTHREAD_EXPLICIT_SCHED); pthread_attr_setschedpolicy(&attr1,SCHED_FIFO); //Modify scheduling policy if(0!=pthread_attr_setschedparam(&attr1,¶m1)) { printf("setschedpolicy attr1 NG! \n"); }
All experimental Codes:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sched.h> #include <assert.h> #include <time.h> #if 1 void *Thread1(void* pp) { sleep(1); int i; int policy; struct sched_param param; pthread_getschedparam(pthread_self(),&policy,¶m); switch(policy) { case SCHED_OTHER: printf("SCHED_OTHER 1\n"); break; case SCHED_RR: printf("SCHED_RR 1 \n"); break; case SCHED_FIFO: printf("SCHED_FIFO 1\n"); break; } while(1) { for(i=1;i<5000000;i++) { } printf("Pthread 1\n"); //usleep(500); } printf("Pthread 1 exit\n"); } void *Thread2(void* pp) { sleep(2); int i; int policy; struct sched_param param; pthread_getschedparam(pthread_self(),&policy,¶m); switch(policy) { case SCHED_OTHER: printf("SCHED_OTHER 2\n"); break; case SCHED_RR: printf("SCHED_RR 2 \n"); break; case SCHED_FIFO: printf("SCHED_FIFO 2\n"); break; } while(1) { for(i=1;i<5000000;i++) { } printf("Pthread 2\n"); //usleep(500); } printf("Pthread 2 exit\n"); } void *Thread3(void* pp) { sleep(3); int i; int policy; struct sched_param param; pthread_getschedparam(pthread_self(),&policy,¶m); switch(policy) { case SCHED_OTHER: printf("SCHED_OTHER 3\n"); break; case SCHED_RR: printf("SCHED_RR 3 \n"); break; case SCHED_FIFO: printf("SCHED_FIFO 3\n"); break; } while(1) { for(i=1;i<5000000;i++) { } printf("Pthread 3\n"); //usleep(500); } printf("Pthread 3 exit\n"); } #endif int main() { int uid; uid = getuid(); if(uid==0) printf("The current user is root\n"); else printf("The current user is not root\n"); pthread_t ppid1,ppid2,ppid3; struct sched_param param1; struct sched_param param2; struct sched_param param3; pthread_attr_t attr1,attr2,attr3; pthread_attr_init(&attr1); pthread_attr_init(&attr2); pthread_attr_init(&attr3); param1.sched_priority = 40; pthread_attr_setinheritsched(&attr1,PTHREAD_EXPLICIT_SCHED); pthread_attr_setschedpolicy(&attr1,SCHED_OTHER); if(0!=pthread_attr_setschedparam(&attr1,¶m1)) { printf("setschedpolicy attr1 NG! \n"); } param2.sched_priority = 20; pthread_attr_setinheritsched(&attr2,PTHREAD_EXPLICIT_SCHED); pthread_attr_setschedpolicy(&attr2,SCHED_RR); pthread_attr_setschedparam(&attr2,¶m2); if(0!=pthread_attr_setschedparam(&attr2,¶m2)) { printf("setschedpolicy attr2 NG! \n"); } param3.sched_priority = 20; pthread_attr_setinheritsched(&attr3,PTHREAD_EXPLICIT_SCHED); pthread_attr_setschedpolicy(&attr3,SCHED_RR); pthread_attr_setschedparam(&attr3,¶m3); if(0!=pthread_attr_setschedparam(&attr3,¶m3)) { printf("setschedpolicy attr3 NG! \n"); } pthread_create(&ppid1,&attr1,Thread1,NULL); pthread_create(&ppid2,&attr2,Thread2,NULL); pthread_create(&ppid3,&attr3,Thread3,NULL); pthread_join(ppid1,NULL); pthread_join(ppid2,NULL); pthread_join(ppid3,NULL); //while(1) { //sleep(100); //printf("main loop\n"); //} pthread_attr_destroy(&attr1); pthread_attr_destroy(&attr2); pthread_attr_destroy(&attr3); return 0; }
Experimental process phenomenon
SCHED_OTHER
Create three time-sharing threads:
Since the time-sharing policy cannot set priority, tests with different priorities are not conducted.
The experimental results are as follows:
SCHED_FIFO
Create three real-time FIFO threads with the same priority of 30.
The experimental results are as follows:
SCHED_RR
Create three real-time RR threads:
And the three of them have the same priority, which is 30.
The experimental results are as follows:
All in real time with different priorities
Create three real-time threads with different priorities:
Time sharing and real-time simultaneous existence
Create two real-time / time-sharing threads with different priorities. The experimental results are as follows:
empirical conclusion
Experimental conclusion:
1. The three scheduling strategies meet the policy characteristics of threads.
2. The real-time thread unconditionally preempts the time-sharing thread, and the time-sharing thread cannot preempt the real-time thread.
3. In real-time threads, high priority threads preempt low priority threads.
4. In real-time threads, high priority threads will not be interrupted by low priority threads.
Experimental subject (the following description is not measured in the experiment):
linux has a group policy that allocates real-time and time-sharing time slices
sysctl -n kernel.sched_ rt_ period_ Unit CPU time of us # real-time process scheduling: 1s
1000000
sysctl -n kernel.sched_ rt_ runtime_ CPU time actually occupied by us # real-time process in 1 second, 0.95 seconds
950000
This setting indicates that the real-time process does not completely occupy the CPU when running. There is 0.05 seconds for other processes to run every 1 second
In this way, the response time of the real-time process will not be greatly affected, and the whole system will not respond when the real-time process is stuck