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
- nice(2) 设置当前进程线程的优先级
- getpriority(2) 获取指定用户的进程,进程组,线程优先级
- setpriority(2) 设置指定用户的进程,进程组,线程优先级
- sched_setscheduler(2) 设置调度策略
- sched_getscheduler(2) 获取调度策略
- sched_setparam(2) 设置调度参数.
- sched_getparam(2) 获取调度参数.
- sched_get_priority_max(2) 返回指定调度策略的优先级最大值
- sched_get_priority_min(2) 返回指定调度策略的优先级最小值
- sched_rr_get_interval(2) 获取SCHED_RR调度策略时间片长度
- sched_yield(2) 主动让出CPU
- sched_setaffinity(2) (Linux-specific) 线程设置到指定CPU
- sched_getaffinity(2) (Linux-specific) 获取相关CPU设置
- sched_setattr(2) 设置调度策略和参数
- sched_getattr(2) 获取调度策略和参数
Posix Threads API :
- pthread_attr_getschedpolicy 获取线程调度策略
- pthread_attr_setschedpolicy 设置线程调度策略
- pthread_attr_setinheritsched 设置继承调度策略
- pthread_attr_getinheritsched 获取继承调度策略
- pthread_attr_setschedparam 设置调度参数属性
- pthread_attr_getschedparam 获取调度参数属性
- pthread_setschedparam 设置调度参数
- pthread_getschedparam 获取调度参数
- pthread_setaffinity_np 设置线程使用CPU
- pthread_getaffinity_np 获取线程使用CPU
相关示例:
(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, ¶m);
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, ¶m[0]);
pthread_create(&thread1, &attr, test_func, (void*)&id_1);
pthread_attr_setschedparam (&attr, ¶m[1]);
pthread_create(&thread2, &attr, test_func, (void*)&id_2);
pthread_attr_setschedparam (&attr, ¶m[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 内幕
What is the difference between sleep(0) and sched_yield()?
Be First to Comment