在Linux中,线程是由进程来实现,线程就是轻量级进程( lightweight process ),因此在Linux中,线程的调度是按照进程的调度方式来进行调度的,也就是说线程是调度单元。Linux这样实现的线程的好处的之一是:线程调度直接使用进程调度就可以了,没必要再搞一个进程内的线程调度器。在Linux中,调度器是基于线程的调度策略(scheduling policy)和静态调度优先级(static scheduling priority)来决定那个线程来运行。
对于下面三种调度策略SCHED_OTHER, SCHED_IDLE, SCHED_BATCH,其调度优先级sched_priority是不起作用的,即可以看成其调度优先级为0;调度策略SCHED_FIFO和SCHED_RR是实时策略,他们的调度值范围是1到99,数值越大优先级越高,另外实时调度策略的线程总是比前面三种通常的调度策略优先级更高。通常,调度器会为每个可能的调度优先级(sched_priority value)维护一个可运行的线程列表,并且是以最高静态优先级列表头部的线程作为下次调度的线程。所有的调度都是抢占式的:如果一个具有更高静态优先级的线程转换为可以运行了,那么当前运行的线程会被强制进入其等待的队列中。下面介绍几种常见的调度策略:
ps -eo state,uid,pid,ppid,rtprio,time,comm
来查看进程对应的实时优先级(位于RTPRIO列下),如果有进程对应的列显示“-”,则说明它不是实时进程。注意任何实时策略进程的优先级都高于普通的进程,也就说实时优先级和nice优先级处于互不相交的两个范畴。
在Linux中,与调度相关的常见接口如下:
#include <sched.h>
int sched_get_priority_max(int policy);
该接口获取指定调度策略可以设置的最大优先级,类似的 sched_get_priority_min接口获取调度策略可以设置的最小优先级。在Linux中,对于SCHED_FIFO和SCHED_RR调度策略其优先级为1到99,其他调度策略优先级为0。注意在不同系统上,这个优先级范围可能不一样。
#include <pthread.h>
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
该接口可以用来设置线程的调度策略,即设置线程属性attr。参数policy可以是CHED_FIFO, SCHED_RR和SCHED_OTHER。系统创建线程时,默认的线程调度策略是SCHED_OTHER。类似可以通过接口:
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)
获取线程的调度策略:
#include <pthread.h>
int pthread_attr_setschedparam(pthread_attr_t *attr,
const struct sched_param *param);
该接口可以用来设置线程的调度优先级。结构sched_param定义如下:
struct sched_param {
int sched_priority; /* Scheduling priority */
};
类似的的接口,可以用来获取线程调度的优先级:
int pthread_attr_getschedparam(const pthread_attr_t *attr,
struct sched_param *param);
#include <sched.h>
int sched_yield(void);
调用该接口可以使得当前线程主动交出CPU,并把该线程放到相应调度队列的末尾。如果当前线程是最高优先级队列中唯一的线程,则在调用sched_yield后,该线程继续保持运行:
#include <sched.h>
int sched_setaffinity(pid_t pid,
size_t cpusetsize,const cpu_set_t *mask);
该接口可以用来设置线程的CPU亲和性(CPU affinity),设置线程的亲和性可以使得线程绑定到一个或多个指定的CPU上运行。在多处理器系统上,设置CPU亲和性可以提高性能(主要原因是尽可能避免了cache失效和切换到其他CPU的消耗)。CPU亲和性掩码是由cpu_set_t结果来实现的,该结构体需要用预定义好的宏来操作;参数pid是指定线程的TID,可以通过gettid()来获取,即线程在内核中对应进程id,若pid为0,则设置的是调用线程的CPU亲和性,注意用getpid()获取的是主线程的id;参数cpusetsize的值通常是mask的大小,即sizeof(mask)。除了这个接口外,设置线程亲和性接口还有:
#include <pthread.h>
int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
const cpu_set_t *cpuset);
int pthread_attr_setaffinity_np(pthread_attr_t *attr,
size_t cpusetsize, const cpu_set_t *cpuset);
通过fork创建的子进程继承父进程的CPU亲和性,通过 execve()后,亲和性仍然保持不变。我们可以下面命令来查看多核cpu的负载:
下面是测试代码:
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <assert.h>
static int get_thread_policy(pthread_attr_t *attr)
{
int policy;
int rs = pthread_attr_getschedpolicy(attr,&policy);
assert(rs==0);
switch(policy)
{
case SCHED_FIFO:
printf("policy=SCHED_FIFO\n");
break;
case SCHED_RR:
printf("policy=SCHED_RR\n");
break;
case SCHED_OTHER:
printf("policy=SCHED_OTHER\n");
break;
default:
printf("policy=UNKNOWN\n");
break;
}
return policy;
}
static void show_thread_priority(pthread_attr_t *attr,int policy)
{
int priority = sched_get_priority_max(policy);
assert(priority != -1);
printf("max_priority=%d\n",priority);
priority= sched_get_priority_min(policy);
assert(priority != -1);
printf("min_priority=%d\n",priority);
}
static int get_thread_priority(pthread_attr_t *attr)
{
struct sched_param param;
int rs = pthread_attr_getschedparam(attr,¶m);
assert(rs == 0);
printf("priority=%d\n",param.__sched_priority);
return param.__sched_priority;
}
static void set_thread_policy(pthread_attr_t *attr,int policy)
{
int rs = pthread_attr_setschedpolicy(attr,policy);
assert(rs==0);
}
int main(void)
{
pthread_attr_t attr;
int rs;
rs = pthread_attr_init(&attr);
assert(rs==0);
int policy = get_thread_policy(&attr);
printf("Show current configuration of priority\n");
get_thread_policy(&attr);
show_thread_priority(&attr,policy);
printf("show SCHED_FIFO of priority\n");
show_thread_priority(&attr,SCHED_FIFO);
printf("show SCHED_RR of priority\n");
show_thread_priority(&attr,SCHED_RR);
printf("show priority of current thread\n");
get_thread_priority(&attr);
printf("Set thread policy\n");
printf("set SCHED_FIFO policy\n");
set_thread_policy(&attr,SCHED_FIFO);
get_thread_policy(&attr);
get_thread_priority(&attr);
printf("set SCHED_RR policy\n");
set_thread_policy(&attr,SCHED_RR);
get_thread_policy(&attr);
printf("Restore current policy\n");
set_thread_policy(&attr,policy);
get_thread_priority(&attr);
rs = pthread_attr_destroy(&attr);
assert(rs==0);
return 0;
}
编译和运行程序结果如下:
$gcc -Wall -lpthread hack_thread_sched.c -o hack_thread_sched
$./hack_thread_sched
policy=SCHED_OTHER
Show current configuration of priority
policy=SCHED_OTHER
max_priority=0
min_priority=0
show SCHED_FIFO of priority
max_priority=99
min_priority=1
show SCHED_RR of priority
max_priority=99
min_priority=1
show priority of current thread
priority=0
Set thread policy
set SCHED_FIFO policy
policy=SCHED_FIFO
priority=0
set SCHED_RR policy
policy=SCHED_RR
Restore current policy
priority=0
从输出结果,我们可以看到:
在Linux中,调度程序是一个叫schedule()的函数,该函数调用的频率很高,由它来决定是否要执行进程的切换,如果要切换的话,切换到那个进程等。那么在Linux中,在什么情况下要执行这个调度程序呢?我们把这种情况叫作调度时机。Linux调度时机主要有:
文章参考:https://blog.csdn.net/MaximusZhou/article/details/42042161