Linux 进程,线程的调度策略API

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  课程中的样例

 进程线程调度示例:

#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上运行结果如下, 优先级高的线程抢占完成所有累计运算

[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值越小优先级越高。由于采用分时调度策略, 所有三个线程都有计数操作。

[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。


线程调度示例:

#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,如果为高优先级别 则没有什么意义。 

[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()?

 

Be First to Comment

发表回复