linux桌面版和服务器版区别_Linux内核系列 单核CPU下的调度策略

回顾下上篇文章:感兴趣可以看上面 《linux系列》专辑

调度器设计的两个目标:

1、吞吐

2、响应

调度器只能满足一个目标,提高吞吐就会降低响应,提高响应就会降低吞吐。

对吞吐的影响不只是上下文切换的耗时,还有cache miss的影响。

进程分两种类型:

1、IO消耗型

2、CPU消耗型

IO消耗型一般和用户体验相关,这种进程特别关注响应,一般CPU占用率很低。

linux操作系统一般来说是抢占式、非实时操作系统。

但是并不表示linux不能做实时系统, 打上RT-Preempt Patch内核补丁,linux就可以改为实时操作系统了。

linux抢占模型:

1、No Forced Preemption (Server)  不强制抢占 适合偏吞吐模式 适合服务器选用,操作系统基本没有抢占调度,吞吐性能越好

2、Voluntary Kernel Preemption (Desktop)   内核不可抢占

3、Preemptible Kernel (Low-Latency Desktop) 可抢占式内核 偏响应模式 一般桌面版选用,内核可以抢占,响应越好

4、Fully Preemptible Kernel (Real-Time) 实时系统 (需要rt补丁)

所以有些linux厂商会将发行的系统分服务器版和桌面版,就是因为linux内核选择了不同的抢占模型。

这里因为分析linux内核的调度策略,所以我们只看第3种 偏响应的抢占模型。

linux2.6内核时期

c036552da10d211e7dbc166869eda12a.png

优先级数组和bitmaps

内核中将优先级划分为0~139,优先级值越小优先级越高。

在内核中0~99属于RT型进程(实时),100~139为非RT普通进程。

多个进程可以设置同样的优先级。

注意:这里的优先级指的是nice值,但是内核中的nice值和top命令在用户空间中看到的(NI)不一样!

调度:

每次调度时,按优先级从高到低(从0到139)找最先有就绪态的进程/线程,哪个优先级有任务就先调度哪个优先级。

实时调度策略:0~99

SCHED_FIFO

    按优先级,最高优先级先跑,跑到让出CPU(比如睡眠),低优先级再跑;同等优先级,按先后顺序跑,先进先出。

e04d1ec03f1fe3c00c5c2cddf8ea3372.png

比如:A、B两个进程优先级都是0,按A、B进入就绪态的时间先后(假设A先就绪,B再就绪),先跑A进程,A进程跑完再跑B。假设后来有个C进程,优先级是1,那么等A、B都跑完,再跑C。

SCHED_RR

    按优先级,高优先级先跑,低优先级再跑。同等优先级,大家轮流跑。

56f46c0eb27dfdff2ba323bfd1386e20.png

RR就是RoundRobin(轮询),比如A、B优先级相同,假设A先就绪,B后就绪,那么先跑A一段时间,再跑B一段时间,然后再跑A一段时间,再跑B…轮流跑,直到进程/线程都跑完。

这时候你可能有个疑问:假如有个进程是RT型进程,调度策略被设置了SCHED_FIFO,如果这个进程有个死循环的bug,那么CPU是不是一直会被这个进程占用?

答案是不会。

linux以前有一个sched_rt补丁,限制RT进程在指定时间周期内,最多可以占用CPU时间片比例。

// sched_rt_period_us 在指定的时间周期 us[root@dev 7]# cat /proc/sys/kernel/sched_rt_period_us1000000// sched_rt_runtime_us 在上述周期内,最多可以运行 xxx us[root@dev 7]# cat /proc/sys/kernel/sched_rt_runtime_us950000

由于各类软件总有各种各样的bug,所以操作系统在设计时,通常把运行的第三方程序看作是个渣渣。linux会用各种操作保证第三方软件的各种异常情况下,操作系统能够稳定的运行。

就像之前提到的,进程死亡(比如bug崩溃)进入僵尸态时,操作系统会将进程下所有资源释放。

fe8caae57ef20afb0a6557c60ee92724.png

对于普通进程(优先级100~139 即 nice值 -20 ~ 19)

普通进程

对于普通进程,体现的是人民的善良性,优先级高不会对优先级低的绝对优势。普通优先级的进程/线程是轮流在cpu上执行。

那么优先级又有什么用处呢?

不同优先级,优先级高的可以分配到更多cpu时间片;高优先级在就绪态时可以抢占低优先级的进程/线程,先执行高优先级。

但是我们之前说过,对于IO消耗型的任务,期望CPU可以尽快响应自己。linux会优先照顾喜欢睡眠的进程,linux在运行时会对进程统计,区分IO消耗型和CPU消耗型,对IO消耗型(经常在睡眠态的进程)奖励,对CPU消耗型惩罚。越喜欢睡,优先级越高;越喜欢跑,优先级越低。让CPU消耗型的进程和IO消耗型进程在抢占时,IO消耗型更容易抢到CPU。linux会在运行时动态的调整进程的优先级。

规则:

1、不同优先级轮转运行

2、按nice优先级抢占。就绪态时,高优先级可以抢占低优先级

3、动态奖励和惩罚

后来的调度策略,linux针对 普通进程 提出了一个CFS调度策略(完全公平调度)。

CFS:完全公平调度

CFS的目标是:追求虚拟时间的最终相等

CFS是基于红黑树实现的调度算法,算法简单到小学生都能理解。

首先我们回顾下什么是红黑树:

红黑树:一种自平衡二叉树,左边结点小于右边结点,右边结点小于最右边结点。

8b7e7a669cc4c6a62125898a947c1946.png

CFS红黑树结构

这个红黑树上的结点值记录的是进程的 “virtual runtime”

virtual runtime = 进程实际运行时间 / 进程权重 * 1024 

我们可以忽略掉1024系数

https://github.com/torvalds/linux/blob/a5ad5742f671de906adbf29fbedf0a04705cebad/kernel/sched/core.c#L8026

// 优先级对应的权重const int sched_prio_to_weight[40] = { /* -20 */ 88761, 71755, 56483, 46273, 36291, /* -15 */ 29154, 23254, 18705, 14949, 11916, /* -10 */ 9548, 7620, 6100, 4904, 3906, /* -5 */ 3121, 2501, 1991, 1586, 1277, /* 0 */ 1024, 820, 655, 526, 423, /* 5 */ 335, 272, 215, 172, 137, /* 10 */ 110, 87, 70, 56, 45, /* 15 */ 36, 29, 23, 18, 15, };

优先级0的时候,权重就是1024。

linux调度普通进程时,优先选择virtual runtime值最小的进程,每次调度完之后更新红黑树上进程的virtual runtime值,因为红黑树的特性,进程在红黑树上的位置会从左移到右,所以整个红黑树会不停的向右滚动。

拿出我们小学的数学知识,相同的分母,分子越小,值越小。所以,同样的进程权重,进程实际运行时间越短,进程的virtual runtime越小,在内核调度时越容易被优先调度。

这算法思想,真是小学生都能懂。

一个进程又喜欢睡(经常在睡眠态),又权重越大(nice值越低),就会一直在CFS红黑树的左边。这种进程最容易被调度器调度。但是只要遇到RT进程,也得乖乖让路。

做个实验:

在linux系统中,每一个进程/线程的调度策略和优先级都可以不一样。

调度的最小单位 task_struct 中定义了 policy (调度策略)、 prio/static_prio/normal_prio/rt_priority(优先级),没错是我。

代码不贴了,测试系统是4核CPU,测试代码中启动4个线程,每个线程都是死循环。

后台运行两次:

[root@dev shell]$ ./a.out &[1] 2722[root@dev shell]$ thread pid:2722, tid:2722 pthread_self:140062450566976thread pid:2722, tid:2723 pthread_self:140062442186496thread pid:2722, tid:2724 pthread_self:140062433793792thread pid:2722, tid:2725 pthread_self:140062425401088thread pid:2722, tid:2726 pthread_self:140062417008384[root@dev shell]$ ./a.out &[2] 2731[root@dev shell]$ thread pid:2731, tid:2731 pthread_self:140036305585984thread pid:2731, tid:2733 pthread_self:140036288812800thread pid:2731, tid:2732 pthread_self:140036297205504thread pid:2731, tid:2735 pthread_self:140036272027392thread pid:2731, tid:2734 pthread_self:140036280420096

这时候我们可以看到cpu占用率

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND 2722 root   20   0   39288    384    300 S 210.3  0.0   4:14.87 a.out 2731 root   20   0   39288    380    300 S 191.7  0.0   2:34.70 a.out

linux启动一个进程的时候,默认为普通进程,nice值为0,按照上面权重和优先级的映射关系,所以分母为1024。

按照我们之前说的nice值越低(对应权重越大),根据完全公平调度的设计目标,追求虚拟时间的最终相等。

当进程2722的分母是进程2731的三倍时,同比例的分子应该也是三倍,这样才能实现虚拟时间的最终相等。

所以我们设置进程2722下所有线程的nice值为 -5 时,它的分母为3121,按道理2722进程的运行时间是2731进程的3倍,即CPU占用率为3倍时,才可以实现虚拟时间的最终相等。

[root@dev dev]# renice -n -5 -g 27222722 (进程组 ID) 旧优先级为 0,新优先级为 -5  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND 2722 root   15  -5   39288    384    300 S 309.9  0.0   3:06.42 a.out 2731 root   20   0   39288    380    300 S  87.4  0.0   1:55.07 a.out
// 设置nice值[root@dev dev]# renice -h用法: renice [-n]  [-p|--pid] ... renice [-n]   -g|--pgrp ... renice [-n]   -u|--user ...选项: -g, --pgrp         将参数解释为进程组 ID -n, --priority   指定 nice 增加值 -p, --pid          将参数解释为进程 ID (默认) -u, --user    将参数解释为用户名或用户 ID -h, --help             显示帮助文本并退出 -V, --version          显示版本信息并退出renice -n  -g 

内核api:支持程序代码内人为干涉调度策略

系统调用

描述

nice()

设置进程的nice值

sched_setscheduler()

设置进程的调度策略,即设置进程采取何种调度算法

sched_getscheduler()

获取进程的调度算法

sched_setparam()

设置进程的实时优先级

sched_getparam()

获取进程的实时优先级

sched_get_priority_max()

获取实时优先级的最大值,由于用户权限的问题,非root用户并不能设置实时优先级为99

sched_get_priority_min()

获取实时优先级的最小值,理由与上面类似

sched_rr_get_interval()

获取进程的时间片

sched_setaffinity()

设置进程的处理亲和力,其实就是保存在task_struct中的cpu_allowed这个掩码标志。该掩码的每一位对应一个系统中可用的处理器,默认所有位都被设置,即该进程可以在系统中所有处理器上执行。

用户可以通过此函数设置不同的掩码,使得进程只能在系统中某一个或某几个处理器上运行。

sched_getaffinity()

获取进程的处理亲和力

sched_yield()

暂时让出处理器

// 查看或设置一个进程调度策略和优先级[root@dev shell]# chrt -hShow or change the real-time scheduling attributes of a process.Set policy: chrt [options]  [...] chrt [options] --pid Get policy: chrt [options] -p Policy options: -b, --batch          set policy to SCHED_BATCH -d, --deadline       set policy to SCHED_DEADLINE -f, --fifo           set policy to SCHED_FIFO -i, --idle           set policy to SCHED_IDLE -o, --other          set policy to SCHED_OTHER -r, --rr             set policy to SCHED_RR (default)Scheduling options: -R, --reset-on-fork       set SCHED_RESET_ON_FORK for FIFO or RR -T, --sched-runtime   runtime parameter for DEADLINE -P, --sched-period    period parameter for DEADLINE -D, --sched-deadline  deadline parameter for DEADLINEOther options: -a, --all-tasks      operate on all the tasks (threads) for a given pid -m, --max            show min and max valid priorities -p, --pid            operate on existing given pid -v, --verbose        display status information -h, --help     显示此帮助并退出 -V, --version  输出版本信息并退出
查看进程的调度策略和优先级[root@dev shell]# chrt --pid 7pid 7's current scheduling policy: SCHED_FIFOpid 7 的当前调度优先级:99

注意,设置进程调度策略为RT进程后,进程会受到系统对RT进程CPU占用率的限制,“限制RT进程在指定时间周期内,最多可以占用CPU时间片比例”。

而且,RT进程由于优先级高于普通进程,会将系统中普通进程抢占,会导致系统反应变慢。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值