浅析Linux线程调度
在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)维护一个可运行的线程列表,并且是以最高静态优先级列表头部的线程作为下次调度的线程。所有的调度都是抢占式的:如果一个具有更高静态优先级的线程转换为可以运行了,那么当前运行的线程会被强制进入其等待的队列中。下面介绍几种常见的调度策略:
SCHED_OTHER:该策略是是默认的Linux分时调度(time-sharing scheduling)策略,它是Linux线程默认的调度策略。SCHED_OTHER策略的静态优先级总是为0,对于该策略列表上的线程,调度器是基于动态优先级(dynamic priority)来调度的,动态优先级是跟nice中相关(nice值可以由接口nice, setpriority,sched_setattr来设置),该值会随着线程的运行时间而动态改变,以确保所有具有SCHED_OTHER策略的线程公平运行。在Linux上,nice值的范围是-20到+19,默认值为0;nice值越大则优先级越低,相比高nice值(低优先级)的进程,低nice值(高优先级)的进程可以获得更多的处理器时间。使用命令ps -el查看系统的进程列表,其中NI列就是进程对应的nice值;使用top命令,看到的NI列也是nice值。运行命令的时候可用nice –n xx cmd来调整cmd任务的nice值,xx的范围是-20~19之间。
SCHED_FIFO:先入先出调度策略(First in-first out scheduling)。该策略简单的说就是一旦线程占用cpu则一直运行,一直运行直到有更高优先级任务到达或自己放弃。
SCHED_RR:时间片轮转调度(Round-robin scheduling)。该策略是SCHED_FIFO基础上改进来的,他给每个线程增加了一个时间片限制,当时间片用完后,系统将把该线程置于队列末尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。使用top命令,如果PR列的值为RT,则说明该进程采用的是实时策略,即调度策略是SCHED_FIFO或者为SCHED_RR,而对于非实时调度策略(比如SCHED_OTHER)的进程,该列的值是NI+20,以供Linux内核使用。我们可以通过命令:
- 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的负载:
I)cat /proc/cpuinfo 查看所有cpu的信息;
II)top命令,然后再输入1,则显示多个cpu的使用信息;
III)top命令,然后按下f,进入top Current Fields设置页面,然后按下j,表示要求显示进程使用那个cpu,回车后,回到刚才界面,此时P 显示此进程使用哪个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
从输出结果,我们可以看到:
I)线程默认的调度策略为SCHED_OTHER,并且最大和最小调度优先级都是0。
II)调度策略SCHED_FIFO和SCHED_RR的优先级范围为1到99,并且初始设置时对应的调度优先级初始值为0。
在Linux中,调度程序是一个叫schedule()的函数,该函数调用的频率很高,由它来决定是否要执行进程的切换,如果要切换的话,切换到那个进程等。那么在Linux中,在什么情况下要执行这个调度程序呢?我们把这种情况叫作调度时机。Linux调度时机主要有:
I)进程状态转换的时刻:进程终止、进程睡眠(比如I/O阻塞就会导致这种情况),还比如进程调用sleep()或exit()等函数进行状态转换。
II)当前进程的时间片用完时。
III)设备驱动程序,当设备驱动程序执行长而重复的任务时,在每次反复循环中,驱动程序读检查是否需要调度,如果必要,则调用调度程序schedule()放弃CPU。
IV)进程从中断、异常及系统调用返回到用户态时。
参考资料:
http://man7.org/linux/man-pages/man7/sched.7.html
http://man7.org/linux/man-pages/man2/sched_getaffinity.2.html
http://www.cnblogs.com/xiaotlili/p/3510224.html
http://www.708luo.com/?p=78 Linux
http://www.quora.com/What-is-the-difference-between-the-NI-and-PR-values-in-the-top-1-commands-output
http://blog.csdn.net/hanchaoman/article/details/6697636
http://www.ibm.com/developerworks/cn/linux/l-affinity.html
http://blog.csdn.net/chenggong2dm/article/details/6131052
《Linux内核设计与实现》(第3版)
《深入分析Linux内核源代码》(陈莉君著)
浅析Linux线程调度的更多相关文章
- 浅析 Linux 初始化 init 系统
近年来,Linux 系统的 init 进程经历了两次重大的演进,传统的 sysvinit 已经逐渐淡出历史舞台,新的 UpStart 和 systemd 各有特点,越来越多的 Linux 发行版采纳了 ...
- 浅析Linux下进程间通信:共享内存
浅析Linux下进程间通信:共享内存 共享内存允许两个或多个进程共享一给定的存储区.因为数据不需要在客户进程和服务器进程之间复制,所以它是最快的一种IPC.使用共享内存要注意的是,多个进程之间对一给定 ...
- 浅析 Linux 初始化 init 系统,第 1 部分: sysvinit 第 2 部分: UpStart 第 3 部分: Systemd
浅析 Linux 初始化 init 系统,第 1 部分: sysvinit 第 2 部分: UpStart 第 3 部分: Systemd http://www.ibm.com/developerw ...
- 浅析Linux操作系统工作的基础
环境:lubuntu 13.04 kernel 3.9.7 作者:SA12226265 katao 简介: 本文根据 Linux™ 系统工作基础的分析,对存储程序计算机.堆栈(函数调用堆栈)机制和 ...
- 浅析Linux服务器集群系统技术
浅析Linux服务器集群系统技术 目录 前言 常用的服务器集群 集群系统的优势 LVS集群的通用体系结构 为什么使用层次的体系结构 为什么是共享存储 可伸缩Web服务 前言 总结两篇技术文章,努力学习 ...
- 浅析linux内核中timer定时器的生成和sofirq软中断调用流程(转自http://blog.chinaunix.net/uid-20564848-id-73480.html)
浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_timer添加的定时器timer在内核的软中断中发生调用,__run_timers会spin_lock_irq(& ...
- 浅析linux内核中timer定时器的生成和sofirq软中断调用流程【转】
转自:http://blog.chinaunix.net/uid-20564848-id-73480.html 浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_time ...
- 浅析 Linux 中的时间编程和实现原理一—— Linux 应用层的时间编程【转】
本文转载自:http://www.cnblogs.com/qingchen1984/p/7007631.html 本篇文章主要介绍了"浅析 Linux 中的时间编程和实现原理一—— Linu ...
- 【转】浅析Linux中的零拷贝技术
本文探讨Linux中主要的几种零拷贝技术以及零拷贝技术适用的场景.为了迅速建立起零拷贝的概念,我们拿一个常用的场景进行引入: 引文## 在写一个服务端程序时(Web Server或者文件服务器),文件 ...
随机推荐
- 洛谷 P2042 [NOI2005]维护数列-Splay(插入 删除 修改 翻转 求和 最大的子序列)
因为要讲座,随便写一下,等讲完有时间好好写一篇splay的博客. 先直接上题目然后贴代码,具体讲解都写代码里了. 参考的博客等的链接都贴代码里了,有空再好好写. P2042 [NOI2005]维护数列 ...
- ubuntu16.04(64位)建立交叉编译环境,并配置编译内核
安装步骤 1.准备工具安装目录 将压缩包gcc-linaro-arm-linux-bnueabihf-4.9-2014.07_linux.tar存放在一个目录下,这个目录就是你等会解压缩的目录,以后这 ...
- JavaScript基础-DAY2
JavaScript对象 在JavaScript中除了null和undefined以外其他的数据类型都被定义成了对象,也可以用创建对象的方法定义变量,String.Math.Array.Date.Re ...
- Swift2.0语言教程之类的嵌套与可选链接
Swift2.0语言教程之类的嵌套与可选链接 Swift2.0语言类的嵌套 在一个类中可以嵌套一个或者多个类.它们的嵌套形式也是不同的,大致分为了两种:直接嵌套和多次嵌套.下面依次讲解这两种方式. S ...
- python opencv3 FLANN单应性匹配
git:https://github.com/linyi0604/Computer-Vision 匹配准确率非常高. 单应性指的是图像在投影发生了 畸变后仍然能够有较高的检测和匹配准确率 # codi ...
- Spring AOP笔记
AOP的核心概念 AOP(Aspect-Oriented Programming)面向切面编程可以实现横切点与他们所影响的对象之间的解耦.如将公共的日志.权限.事务等业务隔离出来,但不影响原来程序的逻 ...
- python日常碎碎念--PIL
昨天在处理网站相关图片的时候,发现图片都大小不一样,虽然一下就能想起PIL这个库,但是用法却不记得了. 简单记录一下用法. 可以直接用 Image.open 来打开图片,PIL库为这个文件对象提供了各 ...
- Codeforces Round #360 (Div. 2) A. Opponents 水题
A. Opponents 题目连接: http://www.codeforces.com/contest/688/problem/A Description Arya has n opponents ...
- poj 1273 Drainage Ditches 网络流最大流基础
Drainage Ditches Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 59176 Accepted: 2272 ...
- SQL Server Management Studio 教程一:设置sa用户登录
今天在net项目中添加数据库过程中出现了小问题,就是使用sql server身份验证没登录成功,经过一番调试,终于解决问题. 使用sa账户登录sql server 2008 的方法步骤如下: 1.首先 ...