1.线程的负载均衡

对task_struct做负载均衡;

分布式系统中,linux的每个核都自动以劳动为乐,(共产主义社会)。

分别对RT任务和普通线程做负载均衡

RT任务:将n个优先级最高的线程自动分配到n个核;

pull_rt_task()
push_rt_task()

普通任务:

周期性负载均衡,在时钟tick会检查哪个核空闲,优先使空闲核工作(从负载重的核pull任务,或push任务给空闲核,每个CPU以劳动为乐);

idle时负载均衡;某个核进入idle状态,会主动pull任务执行;

fork和exec时负载均衡;创建的新进程,会放到最闲的核去跑;

软亲和性(affinity) Linux 内核进程调度器天生就具有被称为软 CPU
亲和性(affinity)的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。

硬亲和性(affinity): 就是利用linux内核提供给用户的API,强行将进程或者线程绑定到某一个指定的cpu核运行。

需要绑定CPU的原因:

1.提高cache命中率;

2.在一些非对称CPU,比如NUMA中,防止一个CPU去另外一个插槽的内存读写数据;

Linux内核之进程4:CPU的负载均衡_中断负载均衡

也可以用taskset工具设置:-a 所有线程

pidof ./a.out

Linux内核之进程4:CPU的负载均衡_cgroup_02

进程a.out占用CPU800%(八核)

Linux内核之进程4:CPU的负载均衡_中断负载均衡_03

设置只在2号核跑

taskset -a -p 02 554

Linux内核之进程4:CPU的负载均衡_cgroup_04

CPU占有率降至100%,所有线程只在2号核上跑;

2.中断负载均衡

负载除了进程还有中断,执行完中断,才会去执行task_struct任务;

硬件中断一般比较短(linux2.6.34后内核不支持中断嵌套),比如网卡收包,在硬中断接收完包,会调用软中断(处理tcp/ip包,软中断可以嵌套),软中断结束后,会调用vtime最小的线程;

top命令的hi表示硬中断时间(isr,屏蔽中断),si表示软中断,

Linux内核之进程4:CPU的负载均衡_中断负载均衡_05

当网络流量大的时候,CPU花在硬中断和软中断时间比较多,这时候需要做中断的负载均衡;

现在新网卡一般有多个队列,假如在一个4核的系统,网卡有4个队列,每个队列可以单独产生中断,硬件支持负载均衡;那么可以将一个队列绑定到一个核,这样,所有CPU都会参与网卡发送包服务;

Linux内核之进程4:CPU的负载均衡_linux_06

设定方法:将每个中断号,分别设置affinity,绑定到指定CPU

Linux内核之进程4:CPU的负载均衡_软中断_07

cat /proc/interrupts |grep ‘enp’

124: 0 0 0 0 0 0 0 34 415207449 PCI-MSI 1572864-edge enp3s0

Linux内核之进程4:CPU的负载均衡_聚在均衡_08

sudo sh -c “echo 3 > /proc/irq/124/smp_affinity”

Linux内核之进程4:CPU的负载均衡_聚在均衡_09

有的网卡只有一个队列:单个核抛出的软中断只能在这个核跑,那么一个队列抛出的软中断(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上执行

Linux内核之进程4:CPU的负载均衡_中断负载均衡_10

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

/proc/interrupt

/proc/softirq

分别查看硬中断和软中断次数

top +1显示多核的负载

Linux内核之进程4:CPU的负载均衡_中断负载均衡_11

由上可以看到CPU0/1都开始处理网络接收包

经实验得出结论

  1. 硬中断绑定的CPU核,一定会响应softirqs,不管对应rps_cpus位设置与否;

2.当rps_cpus位设置时,对应CPU核会响应softirqs;

RPS原理:由单一CPU核响应硬件中断,将大量网络接收包,通过多核间中断方式分发给其他空闲核,如下网络图清晰说明情况

Linux内核之进程4:CPU的负载均衡_聚在均衡_12


4.linux不是硬实时系统:

硬实时:满足可预期,非一定越快越好,强调的是截止期限的可预期性;

Linux内核之进程4:CPU的负载均衡_软中断_13

Linux内核之进程4:CPU的负载均衡_聚在均衡_14

Linux的设计决定了她不是硬实时系统,有些情况(比如spin_lock)不可抢占,其不可预期,是软实时系统;

用什么系统由场景决定,并非硬实时一定优于软实时。

Linux的抢占算法演变,越新的内核支持抢占越多;

Linux内核之进程4:CPU的负载均衡_中断负载均衡_15

Linux的CPU消耗主要分以下几种:

Linux内核之进程4:CPU的负载均衡_聚在均衡_16

内核态的spin_lock,是自旋锁,用户态不是;

1,2,3都不可抢占,所以linux不是一个硬实时系统,硬实时系统要求任何时刻都可以抢占

Linux内核之进程4:CPU的负载均衡_cgroup_17

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),同时吞吐会下降。

打上补丁后如下图

Linux内核之进程4:CPU的负载均衡_软中断_18

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;

Linux内核之进程4:CPU的负载均衡_聚在均衡_19

用传统RT或CFS调度算法,必须设置洗衣机优先级更高才能满足需求。(核电站优先级更高将独占CPU超过0.5s,
下一个周期无法满足CPU需求)

Linux内核之进程4:CPU的负载均衡_软中断_20

但当系统有多个任务,比如核电站要求800ms周期内必须跑500ms,洗碗机要求300/900ms,
洗衣机要求100/800ms:

Linux内核之进程4:CPU的负载均衡_cgroup_21

总的CPU消耗是小于100%的。但是按照传统RR算法,无法满足调度需求;

Linux内核之进程4:CPU的负载均衡_软中断_22

Linux提供了一个Rate Monotonic Scheduling:

Deadline调度算法思想:当产生一个调度点的时候,DL调度器总是选择其Deadline距离当前时间点最近的那个任务并调度它执行,谁更紧急,谁先跑;

Deadline需要设置三个参数,周期Period,Deadline, Runtime

Linux内核之进程4:CPU的负载均衡_linux_23

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进程,