在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内核使用。我们可以通过命令:

[cpp] view plain copy
  1. ps -eo state,uid,pid,ppid,rtprio,time,comm

来查看进程对应的实时优先级(位于RTPRIO列下),如果有进程对应的列显示“-”,则说明它不是实时进程。注意任何实时策略进程的优先级都高于普通的进程,也就说实时优先级和nice优先级处于互不相交的两个范畴。

在Linux中,与调度相关的常见接口如下:

[cpp] view plain copy
  1. #include <sched.h>
  2. int sched_get_priority_max(int policy);

该接口获取指定调度策略可以设置的最大优先级,类似的 sched_get_priority_min接口获取调度策略可以设置的最小优先级。在Linux中,对于SCHED_FIFO和SCHED_RR调度策略其优先级为1到99,其他调度策略优先级为0。注意在不同系统上,这个优先级范围可能不一样。

[cpp] view plain copy
  1. #include <pthread.h>
  2. int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);

该接口可以用来设置线程的调度策略,即设置线程属性attr。参数policy可以是CHED_FIFO, SCHED_RR和SCHED_OTHER。系统创建线程时,默认的线程调度策略是SCHED_OTHER。类似可以通过接口

[cpp] view plain copy
  1. int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)

获取线程的调度策略。

[cpp] view plain copy
  1. #include <pthread.h>
  2. int pthread_attr_setschedparam(pthread_attr_t *attr,
  3. const struct sched_param *param);

该接口可以用来设置线程的调度优先级。结构sched_param定义如下:

[cpp] view plain copy
  1. struct sched_param {
  2. int sched_priority;     /* Scheduling priority */
  3. };

类似的的接口,可以用来获取线程调度的优先级:

[cpp] view plain copy
  1. int pthread_attr_getschedparam(const pthread_attr_t *attr,
  2. struct sched_param *param);
[cpp] view plain copy
  1. #include <sched.h>
  2. int sched_yield(void);

调用该接口可以使得当前线程主动交出CPU,并把该线程放到相应调度队列的末尾。如果当前线程是最高优先级队列中唯一的线程,则在调用sched_yield后,该线程继续保持运行。

[cpp] view plain copy
  1. #include <sched.h>
  2. int sched_setaffinity(pid_t pid,
  3. 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)。除了这个接口外,设置线程亲和性接口还有:

[cpp] view plain copy
  1. #include <pthread.h>
  2. int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
  3. const cpu_set_t *cpuset);
  4. int pthread_attr_setaffinity_np(pthread_attr_t *attr,
  5. 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。

下面是测试代码:

[cpp] view plain copy
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <sched.h>
  4. #include <assert.h>
  5. static int get_thread_policy(pthread_attr_t *attr)
  6. {
  7. int policy;
  8. int rs = pthread_attr_getschedpolicy(attr,&policy);
  9. assert(rs==0);
  10. switch(policy)
  11. {
  12. case SCHED_FIFO:
  13. printf("policy=SCHED_FIFO\n");
  14. break;
  15. case SCHED_RR:
  16. printf("policy=SCHED_RR\n");
  17. break;
  18. case SCHED_OTHER:
  19. printf("policy=SCHED_OTHER\n");
  20. break;
  21. default:
  22. printf("policy=UNKNOWN\n");
  23. break;
  24. }
  25. return policy;
  26. }
  27. static void show_thread_priority(pthread_attr_t *attr,int policy)
  28. {
  29. int priority = sched_get_priority_max(policy);
  30. assert(priority != -1);
  31. printf("max_priority=%d\n",priority);
  32. priority= sched_get_priority_min(policy);
  33. assert(priority != -1);
  34. printf("min_priority=%d\n",priority);
  35. }
  36. static int get_thread_priority(pthread_attr_t *attr)
  37. {
  38. struct sched_param param;
  39. int rs = pthread_attr_getschedparam(attr,¶m);
  40. assert(rs == 0);
  41. printf("priority=%d\n",param.__sched_priority);
  42. return param.__sched_priority;
  43. }
  44. static void set_thread_policy(pthread_attr_t *attr,int policy)
  45. {
  46. int rs = pthread_attr_setschedpolicy(attr,policy);
  47. assert(rs==0);
  48. }
  49. int main(void)
  50. {
  51. pthread_attr_t attr;
  52. int rs;
  53. rs = pthread_attr_init(&attr);
  54. assert(rs==0);
  55. int policy = get_thread_policy(&attr);
  56. printf("Show current configuration of priority\n");
  57. get_thread_policy(&attr);
  58. show_thread_priority(&attr,policy);
  59. printf("show SCHED_FIFO of priority\n");
  60. show_thread_priority(&attr,SCHED_FIFO);
  61. printf("show SCHED_RR of priority\n");
  62. show_thread_priority(&attr,SCHED_RR);
  63. printf("show priority of current thread\n");
  64. get_thread_priority(&attr);
  65. printf("Set thread policy\n");
  66. printf("set SCHED_FIFO policy\n");
  67. set_thread_policy(&attr,SCHED_FIFO);
  68. get_thread_policy(&attr);
  69. get_thread_priority(&attr);
  70. printf("set SCHED_RR policy\n");
  71. set_thread_policy(&attr,SCHED_RR);
  72. get_thread_policy(&attr);
  73. printf("Restore current policy\n");
  74. set_thread_policy(&attr,policy);
  75. get_thread_priority(&attr);
  76. rs = pthread_attr_destroy(&attr);
  77. assert(rs==0);
  78. return 0;
  79. }

编译和运行程序结果如下:

[cpp] view plain copy
  1. $gcc -Wall -lpthread hack_thread_sched.c -o hack_thread_sched
  2. $./hack_thread_sched
  3. policy=SCHED_OTHER
  4. Show current configuration of priority
  5. policy=SCHED_OTHER
  6. max_priority=0
  7. min_priority=0
  8. show SCHED_FIFO of priority
  9. max_priority=99
  10. min_priority=1
  11. show SCHED_RR of priority
  12. max_priority=99
  13. min_priority=1
  14. show priority of current thread
  15. priority=0
  16. Set thread policy
  17. set SCHED_FIFO policy
  18. policy=SCHED_FIFO
  19. priority=0
  20. set SCHED_RR policy
  21. policy=SCHED_RR
  22. Restore current policy
  23. 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线程调度相关推荐

  1. 浅析 Linux 初始化 init 系统: UpStart

    浅析 Linux 初始化 init 系统: UpStart Upstart 简介 假如您使用的 Linux 发行版是 Ubuntu,很可能会发现在您的计算机上找不到/etc/inittab 文件了,这 ...

  2. pae扩展内存 linux,浅析linux内核内存管理之PAE

    浅析linux内核内存管理之PAE 早期Intel处理器从80386到Pentium使用32位物理地址,理论上,这样可以访问4GB的RAM.然而,大型服务器需要大于4GB的RAM来同时运行数以千计的进 ...

  3. Linux文件系统为,浅析Linux文件系统

    原标题:浅析Linux文件系统 一.文件系统层次分析 由上而下主要分为用户层.VFS层.文件系统层.缓存层.块设备层.磁盘驱动层.磁盘物理层 用户层 最上面用户层就是我们日常使用的各种程序,需要的接口 ...

  4. linux内核定时器死机,浅析linux内核中timer定时器的生成和sofirq软中断调用流程

    浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_timer添加的定时器timer在内核的软中断中发生调用,__run_timers会spin_lock_irq(& ...

  5. linux查看共享内存max,浅析Linux的共享内存与tmpfs文件系统

    浅析Linux的共享内存与tmpfs文件系统 前言 共享内存主要用于进程间通信,Linux有两种共享内存(Shared Memory)机制: (1)** System V shared memory( ...

  6. mtd分区创建linux,浅析linux下mtd设备onenand存储器的分区和节点创建流程及yaffs2文件系统挂载...

    浅析linux下mtd设备onenand存储器的分区和节点创建流程及yaffs2文件系统挂载 在arch/arm/mach-pxa/luther.c这个产品平台文件中,即: MACHINE_START ...

  7. Linux操作系统基础理论(3)-----浅析Linux 与Minix 下进程实现的异同

    Linux操作系统基础理论(3)-----浅析Linux 与Minix 下进程实现的异同 目录 摘要:... 1 1.     引言... 1 1.1  Minix简介... 1 1.2  Linux ...

  8. 嵌入式 linux usbmon,浅析linux下替代usbhound的usb总线sniffer抓包模块usbmon安装和使用...

    浅析linux下替代usbhound的usb总线sniffer抓包模块usbmon安装和使用 操作系统: ubuntu 8.10 内核版本: 2.6.27-7-generic ubuntu 8.10内 ...

  9. 浅析 Linux 系统调用

    浅析 Linux 系统调用 用户态.内核态以及中断 具有高执行级别的程序可以执行特权指令 intel X86 CPU 具有4种级别:0 ~ 3 Linux 只用了0和3(0表示内核态,3表示用户态) ...

最新文章

  1. Github火爆的项目,用聪明的方式学习Vim!
  2. Qt窗口屏幕居中显示 (ZT)
  3. JS+HTML画图的几种方法
  4. JavaScript判断设备类型加载对应网页并设置两端通用事件
  5. css --- 使用媒体查询当屏幕宽度小于某个值时,隐藏掉某个类
  6. Python二级笔记(18,19合集操作篇)
  7. MultiMedia eXtensions - MMX:第一套应用于英特尔 80x86 指令集的 SIMD 扩展
  8. 解决办法——Qtdemo软件无法加载example的问题
  9. Kettle下载国内镜像
  10. linux 光盘刻录命令,Linux中使用命令进行光盘刻录
  11. 小米应用闪退解决方法
  12. 双线macd指标参数最佳设置_一文讲透双线MACD指标及其实战运用
  13. 数字电路仿真软件Minecraft—(3)方块更新感应电路
  14. java程序设计实验报告代写_代写file I/O作业、代写java Scanner I/O程序、代写java编程作业、代做java实验报告...
  15. PostgreSQL插入大量数据:pg_testgen插件
  16. 第四章 序列式容器(sequence containers)
  17. Webstorm2019,最新激活码【永久】
  18. Chino with Equation (隔板法+除法取模)
  19. 《如何创造可信的AI》后记
  20. S7-1212C AC/DC/DLY作为PN主站通过PROFINET转Modbus RTU网关设备与Micro Logix 140

热门文章

  1. Android ContentProvider介绍
  2. scanf_s()函数 (是Microsoft公司VS开发工具提供的一个功能相同的安全标准输入函数)
  3. 英伟达再出GAN神作!多层次特征的风格迁移人脸生成器
  4. 找工作刷题--------Java相关
  5. DBA巡检常用的SQL语句
  6. OGG目标端复制Sequence时Hang住的问题
  7. setup factory 设置自启动
  8. 开源jar包导入查看源码
  9. SkipList 跳跃表
  10. 不可思议的#define