LINUX进程调度:

      调度策略:

    实时进程的调度策略:

    普通进程的调度策略:

     调度类:

      CFS调度策略:

      普通进程使用的调度策略CFS调度策略,通过平衡各个进程vruntime来实现公平的调度, CFS维护了一个以时间为顺序的红黑树, 对处理器需求时间最多的,vruntime最小的存放在树的最左端. CPU取出运行完毕后,如果进程还在运行,增加运行时间计算vruntime后插会红黑树。

      CFS相关结构:

      cfs_rq队列中存放树根节点和最左端节点rb_root_cached,每个节点都包含在

sched_entity,中,sched_entity中存放vruntime做为红黑树的索引,

sched_entity 又属于 task_struct,  task_struct 中通过  sched_class    指定调度类

          具体可以参考是极客时间 《趣谈操作系统》 15-17节

LINUX进程调度API:

sched - overview of CPU scheduling

Posix Threads API :

相关示例:

(University of Cincinnati  Operating Systems  课程中的样例

 进程线程调度示例:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#define _GNU_SOURCE      //for CPU_ZERO CPU_SET
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

long counter = 1000000;
long sum = 0;
int policy;
int policy;
int prio[3];
pthread_attr_t attr;
pthread_t thread1, thread2, thread3;
pthread_barrier_t barrier;
struct sched_param param[3];
sem_t mutex;
int cpunum;

void *test_func(void *id) {
    int count = 0;
    int my_policy, my_id;
    char polstr[128];
    int i;
    my_id = *(int*) id;
    struct sched_param param;

    //获取线程绑定cpu
    cpu_set_t cpuset;
    if (pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t),&cpuset) == 0){
         for (i=0; i<cpunum; i++) {
             if (CPU_ISSET(i, &cpuset)){
                 printf("%d : CPU %d\n", my_id, i);
             }
         }
    }
     
    // 设置所有线程从此点开始执行
    int rc = pthread_barrier_wait(&barrier);
    if (rc != 0 && rc != PTHREAD_BARRIER_SERIAL_THREAD)
    {
           printf("[%d] Could not wait on barrier\n", my_id);
           return (void*) NULL;
    }

    if (policy == SCHED_OTHER) {
        nice(prio[my_id-1]);
    }
    // 显示线程的调度策略和优先级
    pthread_getschedparam(pthread_self(), &my_policy, &param);

    if (my_policy == SCHED_FIFO) {
        strcpy(polstr, "FIFO");
    } else if (my_policy == SCHED_RR) {
        strcpy(polstr, "RR");
    } else if (my_policy == SCHED_OTHER) {
        strcpy(polstr, "OTHER");
    }
     else
        strcpy(polstr, "Unkown");

    if (my_policy != SCHED_OTHER){
       printf("thread  %d: scheduled at %s, priority %d\n",
                       my_id, polstr, param.sched_priority);
    }else{
       printf("thread  %d: scheduled at %s, nice %d\n",
                       my_id, polstr, prio[my_id-1]);
    }

    while (counter > 0){
       count ++;
       sem_wait(&mutex);
       counter--;
       sem_post(&mutex);
    }
    sem_wait(&mutex);
    sum += count;
    printf("Thread %d :%d, sum = %d\n", my_id, count, sum);
    sem_post(&mutex);
    return NULL;
}

int main(int argc, char **argv){

    int id_1 =1, id_2=2, id_3=3, sch_min, sch_max, onecpu=1;
    if (argc !=6 ){
       printf("Usage: %s <schedule-policy> <td-1-pri> <td-2-pir> <td-3-pri>\n",
                argv[0]);
       printf(" schedule-policy = FIFO | RR | OTHER\n");
       printf(" td-?-pri = integer\n");
       printf(" if policy == OTHER td-?-pri is a nice number\n");
       printf(" Otherwise: td-?-pri is between min and max of allowed prios\n");
       return 0;
    }
    if (!strcmp(argv[1], "RR")) {
        policy = SCHED_RR;
    }
    else if (!strcmp(argv[1], "FIFO")) {
        policy = SCHED_FIFO;
    }
    else
        policy = SCHED_OTHER;

    //获取当前调度策略优先级最大最小值 
    sch_max= sched_get_priority_max(policy);
    sch_min= sched_get_priority_min(policy);
    printf("Scheduling policy %s max = %d, min = %d\n\n", argv[1], sch_max, sch_min);
    fflush(stdout);

    cpunum = sysconf(_SC_NPROCESSORS_CONF);
    printf("CPU NUM =  %d\n", cpunum);

  
    cpu_set_t mask;
    CPU_ZERO(&mask);

    // 仅在一个cpu上运行
    if (!strcmp(argv[5], "1")) {
       CPU_SET(0, &mask);
    }
    else {
        int i;
        for (i=0;i<cpunum;i++){
            CPU_SET(i, &mask);
        }
    }
    
    if (sched_setaffinity (0, sizeof (mask), &mask) == -1)
    {
        printf ("warning: could not set CPU affinity, continuing.../n");
        return 1;
    }

    //初始化barrier变量使线程到达同一点开始运行
    if (pthread_barrier_init(&barrier, NULL, 3)) {
         printf("Count not create barrier\n");
         return 1;
    }

    sem_init (&mutex, 0, 1);
    prio[0] = param[0].sched_priority = atoi (argv[2]);
    prio[1] = param[1].sched_priority = atoi (argv[3]);
    prio[2] = param[2].sched_priority = atoi (argv[4]);

    //设置线程属性
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_attr_setschedpolicy(&attr, policy);
    
    //注意这里要设置PTHREAD_EXPLICIT_SCHED ,默认为PTHREAD_INHERIT_SCHED
    //将集成创建者的调度策略
    pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED);

    //设置优先级进行开始运行
    pthread_attr_setschedparam (&attr, &param[0]);
    pthread_create(&thread1, &attr, test_func, (void*)&id_1);
    pthread_attr_setschedparam (&attr, &param[1]);
    pthread_create(&thread2, &attr, test_func, (void*)&id_2);
    pthread_attr_setschedparam (&attr, &param[2]);
    pthread_create(&thread3, &attr, test_func, (void*)&id_3);

    pthread_exit(NULL);
    return 0;
}

      设置调度策略为 SCHED_RR, 三个线程优先级分别为 1 1 2 在一个cpu上运行结果如下, 优先级高的线程抢占完成所有累计运算

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[root@centosgpt sched]# ./sched RR 1 1 2 1
Scheduling policy RR max = 99, min = 1

CPU NUM =  2
1 : CPU 0
2 : CPU 0
3 : CPU 0
thread  3: scheduled at RR, priority 2
Thread 3 :100000, sum = 100000
thread  1: scheduled at RR, priority 1
Thread 1 :0, sum = 100000
thread  2: scheduled at RR, priority 1
Thread 2 :0, sum = 100000

      设置调度策略为 SCHED_OTHER,三个线程设置nice值未1 1 -20 , nice值越小优先级越高。由于采用分时调度策略, 所有三个线程都有计数操作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[root@centosgpt sched]# ./sched OT 1 1 -20 1
Scheduling policy OT max = 0, min = 0

CPU NUM =  2
1 : CPU 0
2 : CPU 0
3 : CPU 0
thread  1: scheduled at OTHER, nice 1
thread  2: scheduled at OTHER, nice 1
thread  3: scheduled at OTHER, nice -20
Thread 3 :917061, sum = 917061
Thread 1 :66987, sum = 984048
Thread 2 :15954, sum = 1000002

    另外,在实时调度(SCHED_RR, SCHED_FIFO),可以调用sched_yield()主动放弃CPU, 但是该线程再高优先级队列中, 那么再调用sched_yield()还会调用该线程, 如果将这个函数用在普通调度策略里 SCHED_OTHER, 则会破坏现有的应用设计。 

   使用sched_yield() 可以防止同等优先级级的任务一直抢占CPU。


线程调度示例:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/resource.h>
#include <unistd.h>

void *print_message_function (void *ptr);

int count1 = 0;
int count2 = 0;
//int times = 100000;
int times = 10;
int priority_1, priority_2;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_barrier_t barrier;

int
main (int argc, char **argv)
{
  pthread_t thread1, thread2;
  char *message1 = "Thread 1";
  char *message2 = "Thread 2";

  if (argc != 3)
    {
      printf ("Usage: %s <prio-of-thread-1> <prio-of-thread-2>\n", argv[0]);
      exit (0);
    }

  priority_1 = atoi (argv[1]);
  priority_2 = atoi (argv[2]);

  cpu_set_t mask;

  CPU_ZERO (&mask);
  CPU_SET (0, &mask);

  if (pthread_setaffinity_np (pthread_self (), sizeof (mask), &mask) == -1)
    {
      printf ("Could not set CPU Affinity \n");
    }

  if (pthread_barrier_init (&barrier, NULL, 2))
    {
      printf ("Count not create barrier\n");
      return 1;
    }

  struct sched_param parm1, parm2;
  pthread_attr_t attr1, attr2;

  pthread_attr_init (&attr1);
  pthread_attr_getschedparam (&attr1, &parm1);
  parm1.sched_priority = priority_1;
  pthread_attr_setinheritsched (&attr1, PTHREAD_EXPLICIT_SCHED);
  pthread_attr_setschedpolicy (&attr1, SCHED_FIFO);
  pthread_attr_setschedparam (&attr1, &parm1);

  printf ("Creating Thread 1 with priority: %d\n", parm1.sched_priority);
  if (pthread_create
      (&thread1, &attr1, (void *) print_message_function, (void *) message1))
    {
      printf ("Error creating Thread 1\n");
      return 1;
    }

  pthread_attr_init (&attr2);
  pthread_attr_getschedparam (&attr2, &parm2);
  parm2.sched_priority = priority_2;
  pthread_attr_setinheritsched (&attr2, PTHREAD_EXPLICIT_SCHED);
  pthread_attr_setschedpolicy (&attr2, SCHED_FIFO);
  pthread_attr_setschedparam (&attr2, &parm2);

  printf ("Creating Thread 2 with priority: %d\n", parm2.sched_priority);
  if (pthread_create
      (&thread2, &attr2, (void *) print_message_function, (void *) message2))
    {
      printf ("Error creating Thread 2\n");
      return 1;
    }

  pthread_join (thread1, NULL);
  pthread_join (thread2, NULL);

  printf ("Thread 1 count: %d\n", count1);
  printf ("Thread 2 count: %d\n", count2);

  return 0;
}

void *
print_message_function (void *ptr)
{

  int i = 0;
  char *message;
  message = (char *) ptr;

  /* wait for all threads to reach this point */
  int rc = pthread_barrier_wait (&barrier);
  if (rc != 0 && rc != PTHREAD_BARRIER_SERIAL_THREAD)
    {
      printf ("[%s] Could not wait on barrier\n", message);
      return (void *) NULL;
    }

  while (times > 0)
    {

      /* increment the count */
      if (strcmp (message, "Thread 1") == 0)
        count1 += 1;
      else
        count2 += 1;

      pthread_mutex_lock (&mutex);
      times--;
      pthread_mutex_unlock (&mutex);
      sched_yield();
      //usleep(1);
    }

  return (void *) NULL;
}

    调用sched_yield时, 线程优先级优先级相同, 则线程其他线程可以抢占到cpu,如果为高优先级别 则没有什么意义。 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[root@centosgpt sched]# ./thread_fifo 10 10
Creating Thread 1 with priority: 10
Creating Thread 2 with priority: 10
Thread 1 count: 5
Thread 2 count: 5
[root@centosgpt sched]# ./thread_fifo 10 11
Creating Thread 1 with priority: 10
Creating Thread 2 with priority: 11
Thread 1 count: 0
Thread 2 count: 10

 sleep与sched_yield

    另外关于sleep与sched_yield 网上也有一些讨论,sched_yield主要用于实时调度策略的进程, sleep则没有限制,  sleep会等待一定时间后再等待cpu的调入, 而sched_yield会立即释放cpu, 如果当前有等于他的进程则让出, 如果没有则继续运行。 

参考:

Linux 2.6 Completely Fair Scheduler 内幕

Process Scheduling

sched(7)

Linux下的sleep()和sched_yield()

What is the difference between sleep(0) and sched_yield()?