1. 线程的负载均衡
对 task_struct 做负载均衡;
分布式系统中,linux 的每个核都自动以劳动为乐,(共产主义社会)。
分别对 RT 任务和普通线程做负载均衡:
RT 任务:将 n 个优先级最高的线程自动分配到 n 个核;
pull_rt_task()
push_rt_task()
负载均衡时机:(普通任务,cfs 调度)
-
周期性负载均衡,在 时钟 tick会检查哪个核空闲,优先使空闲核工作(从负载重的核 pull 任务,或 push 任务给空闲核,每个 CPU 以劳动为乐);
-
idle 时负载均衡;某个核进入idle 状态,会主动 pull 任务执行;
-
fork 和 exec 时负载均衡;创建的新进程,会放到最闲的核去跑;
软亲和性(affinity) Linux 内核进程调度器天生就具有被称为软 CPU
亲和性(affinity)的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。
硬亲和性(affinity): 就是利用 linux 内核提供给用户的 API,强行将进程或者线程绑定到某一个指定的 cpu 核运行。
需要绑定 CPU 的原因:
1. 提高 cache 命中率;
2. 在一些非对称 CPU,比如 NUMA 中,防止一个 CPU 去另外一个插槽的内存读写数据;
也可以用 taskset 工具设置:-a 所有线程
pidof ./a.out
进程 a.out 占用 CPU800%(八核)
设置只在 2 号核跑
taskset -a -p 02 554
CPU 占有率降至 100%,所有线程只在 2 号核上跑;
2. 中断负载均衡
负载除了进程还有中断,执行完中断,才会去执行 task_struct 任务;
硬件中断一般比较短(linux2.6.34 后内核不支持中断嵌套),比如网卡收包,在硬中断接收完包,会调用软中断(处理 tcp/ip 包,软中断可以嵌套),软中断结束后,会调用 vtime 最小的线程;
top 命令的 hi 表示硬中断时间(isr,屏蔽中断),si 表示软中断,
当网络流量大的时候,CPU 花在硬中断和软中断时间比较多,这时候需要做中断的负载均衡;
现在新网卡一般有多个队列,假如在一个 4 核的系统,网卡有 4 个队列,每个队列可以单独产生中断,硬件支持负载均衡;那么可以将一个队列绑定到一个核,这样,所有 CPU 都会参与网卡发送包服务;
设定方法:将每个中断号,分别设置 affinity,绑定到指定 CPU
cat /proc/interrupts |grep 'enp'
124: 0 0 0 0 0 0 0 34 415207449 PCI-MSI 1572864-edge enp3s0
sudo sh -c "echo 3 > /proc/irq/124/smp_affinity"
有的网卡只有一个队列:单个核抛出的软中断只能在这个核跑,那么一个队列抛出的软中断 (tcp/ip 层处理) 只能在这一个核执行,其他核会空闲;
测试:
客户端:\$ echo " 不要回答!不要回答!不要回答!" | nc 10.10.100.16 1235
服务端:\$ nc -l -p 1235
3 RPS 补丁
Google 推出了 rps 补丁,可以把 TCP/IP 协议栈的处理,均衡到多个核上去,这个技术叫 RPS;
比如我手头板子是单个网卡队列收包,默认是单核上执行
#cat /sys/class/net/eth0/queues/rx-0/rps_cpus
0
收包只在 CPU0 上执行
nc -l -p 1235
监听 1235 端口收到数据时,只有 CPU0 继续增加;
多核间的 softIRQ scaling
设置 rps 在 CPU0/ 1 上均衡
echo 3 > /sys/class/net/eth0/queues/rx-0/rps_cpus
watch –d“cat /proc/softirqs | grep NET_RX”
/proc/interrupt
/proc/softirq
分别查看硬中断和软中断次数
top + 1 显示多核的负载
由上可以看到 CPU0/ 1 都开始处理网络接收包
经实验得出结论:
- 硬中断绑定的 CPU 核,一定会响应 softirqs,不管对应 rps_cpus 位设置与否;
2. 当 rps_cpus 位设置时,对应 CPU 核会响应 softirqs;
RPS 原理:由单一 CPU 核响应硬件中断,将大量网络接收包,通过多核间中断方式分发给其他空闲核,如下网络图清晰说明情况
上图引用自https://blog.csdn.net/dog250/article/details/46705657
4.linux 不是硬实时系统:
硬实时:满足可预期,非一定越快越好,强调的是截止期限的可预期性;
Linux 的设计决定了她不是硬实时系统,有些情况 (比如 spin_lock) 不可抢占,其不可预期,是软实时系统;
用什么系统由场景决定,并非硬实时一定优于软实时。
Linux 的抢占算法演变,越新的内核支持抢占越多;
Linux 的 CPU 消耗主要分以下几种:
内核态的 spin_lock,是自旋锁,用户态不是;
1,2,3 都不可抢占,所以 linux 不是一个硬实时系统,硬实时系统要求任何时刻都可以抢占。
T3 时刻唤醒一个更高优先级 RT 线程,
RT 线程只能到 T5 时刻 (硬中断,软中断都执行完,并且释放 spin_lock 之后) 才能抢占,等待时间是未知的。
要解决 linux 硬实时性,需要打 rt 补丁,
补丁不在 mainline 中,维护网站;
https://wiki.linuxfoundation.org/realtime/start
https://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-stable-rt.git/
rt 补丁
将所有中断线程化,
去掉软中断,
spin_lock 改成 mutex 实现,即系统中只有上图第 4 类进程,任何线程都可以被抢占;
使延迟变为可预期,即成为硬实时系统。
不是每一个内核版本都有相应 rt 补丁。
rt 可以将 linux 延迟做到 100us 量级(1G CPU),同时吞吐会下降。
打上补丁后如下图
Server: 不抢占
Desktop: kernel 不抢占
Low-Latency Destktop:手机用,kernel 也抢占
Real-Time:完全抢占;
调度器实时后,linux 还不一定实时,有无数内存坑,比如内存管理部分,ROW,写内存时发现内存尚未分配,此时无法保持实时性。
其他方法:
同时运行两个系统,实时任务放在 RTOS 跑,非实时任务放在 Linux 跑。
比如单反,一般用两个系统,传统拍照用实时系统,涉及网络相关任务放在 linux,利用 linux 强大的网络协议栈。
5.Deadline 调度算法
https://elinux.org/images/f/fe/Using_SCHED_DEADLINE.pdf
如下场景需求,系统有两个任务,核电站任务要求运行 1 /2s, 洗衣机要求运行 20ms/200ms;
用传统 RT 或 CFS 调度算法,必须设置洗衣机优先级更高才能满足需求。(核电站优先级更高将独占 CPU 超过 0.5s,
下一个周期无法满足 CPU 需求)
但当系统有多个任务,比如核电站要求 800ms 周期内必须跑 500ms,洗碗机要求 300/900ms,
洗衣机要求 100/800ms:
总的 CPU 消耗是小于 100% 的。但是按照传统 RR 算法,无法满足调度需求;
Linux 提供了一个 Rate Monotonic Scheduling:
Deadline 调度算法思想:当产生一个调度点的时候,DL 调度器总是选择其 Deadline 距离当前时间点最近的那个任务并调度它执行,谁更紧急,谁先跑;
Deadline 需要设置三个参数,周期 Period,Deadline, Runtime
deadline 调度器虽然可以保证每个 RT 任务在 deadline 之前完成,但是并不能保证每一个任务的最小响应时间。
内核实例,sched-deadline.c
设置允许普通用户修改 app 调度策略
sudo setcap 'cap_sys_nice=eip' ./a.out
pidof ./a.out
sudo chrt -d -T 1000000 -P 3000000 -D 2000000 0 pid
sudo chrt -p pid
$ sudo chrt -p 6276
chrt: unknown scheduling policy
pid 6276's current scheduling policy: pid 6276's current scheduling priority: 0
chrt 版本太低,未支持显示 deadline 参数;
Deadline 在 top 显示 rt 进程,