作者: 雪山肥鱼
时间:20210523 21:08
目的:进程调度中的负载均衡
负载均衡
linux中,每个核都会跑相同的调度算法
RT
FIFO
RR
Normal
CFS(New) 红黑树,左右滚,自动奖罚
nice值奖励与惩罚(Legacy)
RT 进程(task_struct): N 个优先级最高的RT 分布到 N 个核上,每个核乐于工作
核心空的时候,pull_rt_task( ) <<<< 从其他核拿任务
核心忙的时候,push_rt_task( ) >>>> 推给其他核
RT 自动均分到多核上,及时抢到CPU,更多强调的是实时性。
普通进程(normal)
周期性负载均衡 :操作系统的时钟节拍来的时候,对比旁边CPU 的 繁忙程度,如果闲则推给旁边的CPU
IDLE时负载均衡:CPU 准备去跑idle,会看下旁边的核是不是很忙,会pull task 过来,继续干。
fork和exec时负载均衡:主动将 创建的 task_struct 推到其他比较闲的CPU
所以一个线程,可能一会在CPU 1 一会再CPU2...,但可以控制线程在哪个核上
CPU task affinity 亲和性
//np: not posix 非posix api
int pthread_attr_setaffinity_np(pthread_attr_t*, size_t, const cpu_set_t *);
int pthread_attr_getaffinity_np(pthread_attr_t*, size_t, cpu_set_t*);
int sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask);
int sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask);
设置线程向哪个CPU 亲和,设置cpu_set_t 掩码: 0x6(110),指向在 1 2 两个核上跑。设置为0x4:(100), 则指在2号CPU上跑。
taskset -a -p 01 29991
//命令设置进程在哪个核上跑。如果2个死循环线程跑在两个CPU上
//则从200% 降低到100%。因为所有线程都跑在一个核上啦
IRQ affinity 中断的负载均衡
分配IRQ到某个CPU
多队列网卡.png
网卡有4个队列,每个队列的收发中断,分别设置成 1 2 4 8,则均分到4个核上,这就是中断的负载均衡。
如果一张网卡只有一个队列,但是有8个核,中断发给了第0个核,当CPU0 收到中断IRQ后,如果在cpu0 中又调了一个 soft IRQ,这个中断也会运行在cpu0,中断调度的软中断也会运行在同一cpu上的。所以对于cpu0来说,中断和软中断的负载都很重,TCP/IP处理都丢到软中断里去了。cpu0 忙的要死,旁边7个核看热闹,包的吞吐率肯定上不来。
linux提交的RPS补丁,将包处理负载均衡到多个cpu。
//软中断负载均衡到1-15个核上去,4个f 则 0-15 负载均衡。
//fffe 将自己的软中断派发到其他cpu上,所以的cpu 核参与到 包的吞吐
echo fffe > /sys/class/net/eth1/queues/rx-0/rps_cpus fffe
cgroups 进程的分群
示例:2个用户 在一台服务器上工作,比如编译,A用户创了1000个线程, B用户创建了32个线程,如果nice值都是0,则A线程占CPU的比例远远大于B线程占CPU的比例。
所以再引出一层: 分组。群与群之间先做完全公平调度。
群与群之间进行FC调度,群内部再进行CFS调度
cgourp 有一系列的操作,后续遇到再好好总结吧。
Hard realtime 可预期性
硬实时
在一定时间内必须有响应。比如导弹发射,从按按钮到发射,1ms。超过1ms 后果时灾难性的
软实时
可以超过截止期限,就算超过也非灾难性。linux是软实时的。
硬实时与软实时是按照需求来的。
kernal 越来越支持抢占
linux 抢占区间.png
可抢占区域逐步增加,linux内核也会被抢占
当linux 被打上硬实时补丁,就会到第四种情况,只存在点状不可调度区域。
linux 为什么不是硬实时
从linux不可调度实际,以及四类区间进行分析 。
四个区间.png
linux大部分运行在四个区间中.
中断区间
被中断是 RT进程被唤醒,拒绝调度
软中断区间
软中断过程中,RT进程被唤醒,也拒绝调度
进程
3.1 spin_lock(线程拿了把锁)
整个核的调度都被关啦,RT 当然拒绝调度
3.2 可调度的上下文
只有此时可以调度。
只有当1、2、3类区间都结束,才会开启抢断,RT进程运行。
抢占时机.png
T0 时刻 系统调用陷入内核
T1 拿到自旋锁
T2 软中断
T3 时刻 RT 进程被唤醒(RR/FIFO)
T4 时刻 再被中断
T5 出中断,但依旧不会响应 RT进程
出了临界区,RT 才被响应.
从T3 到T6 的延迟都是不可预知的,所以 linux 不是硬实时的,而是软实时的。
当linux 打上RT补丁后
spinlock 迁移为可调度的mutex, 同时报了raw_spin_lock_t
spinlock 编程了可以睡的mutex,mutex是不会锁调度器的。
对于mutex,t1 拿到了mutex, t2就睡,直到t1 放mutex, t2才醒,拿不到就睡,放了就醒
spinlock:有两个cpu,cpu0 拿到了spinlock,cpu0的调度器被关掉,cpu1要想拿到spinlock 就要原地打转。spinlock 发生在两个核之间。
实现优先级继承协议(优先级反转等)
非RT补丁,已经进到linux内核中了。
反转
Low level 进程抢到锁,H level 在等,但是期间一直有 M level 进程在折腾Low level 进程,一直在抢low level的锁,H 一直抢不到。所以看上去像 M level 的优先级 高于 H level。H level 很久才拿到这个锁
继承
Low level 优先级低,过了一段时间,H level 要拿L 的锁,linux会做一件事,临时把Low leve 优先级提高和 H level 一样,这样 M level 就不会折腾这个Low level 的 进程。
中断线程化
中断丢到线程里做,那么就就可以被其他线程强调
软中断线程化
同理 丢到线程里做,也会被抢占。
其实就是把区间1,2,3 都变成了四类区间。所以linux 变成了硬实时。(中断服务也变成及其的短)
RT补丁就是第4个完全抢占
No forced Preemption(Server)
Voluntary kernal preemption(Desktop)
Preemptible Kernal (Low latency Desktop)
Complete Preemption(RealTime)