Linux thread scheduling strategy and priority experiment (picture and text)

Keywords: C Linux Multithreading thread

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,&param1))
    {
        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,&param);
  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,&param);
    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,&param);
    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,&param1))
    {
        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,&param2);
    if(0!=pthread_attr_setschedparam(&attr2,&param2))
    {
        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,&param3);
    if(0!=pthread_attr_setschedparam(&attr3,&param3))
    {
        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

Posted by giannis_athens on Thu, 04 Nov 2021 12:49:55 -0700