Linux 多核究竟是怎么一回事

多核的负载均衡

Linux 在多线程的情况下,进程是如何调度的呢?我们先来看看下面流程图:

Linux 多核调度流程图要理解多核首先要理解单核,前面的文章中我已经对单核的调度算法学习了。其实多核的情况下,需要理解负载均衡的概念。所以,

  • 在多核的 CPU 上,每个 core 上面的还是使用FIFO、RR、CFS算法调度进程的
  • 在core与core之间,使用一些算法进行负载均衡。
    • 对于 RT 的进程,core和core之间使用 pull_rt_request 和 push_rt_request 的进行负载均衡。这是一种主动的平衡策略。
    • 对于 normal 的进程,采用的是的跟随 check 的方式,比较被动。

pull_rt_request:是当一个 core 闲下来的时候,会主动的去忙的 core 里面拉去进程,帮繁忙的 core 来运行进程。

push_rt_request:是当一个 core 繁忙的时候,它会主动的将它上面的进程推送给闲暇的 core,让闲暇的 core 帮忙来执行。

普通进程会在三个时机触发 core 负责平衡:

  • 当一个时钟周期结束的时候
  • 当一个 core 进入 IDLE 状态的时候
  • 当一个 core 执行 fork 或者 exec 的时候

来来,我们来测试一下,请看下面的代码:

1  #include <stdio.h>
2  #include <pthread.h>
3  #include <sys/types.h>
4
5  void *thread_fun(void *param)
6  {
7          printf("thread pid:%d, tid:%lu\n", getpid(), pthread_self());
8          while (1) ;
9          return NULL;
10  }
11
12  int main(void)
13  {
14          pthread_t tid1, tid2;
15          int ret;
16
17          printf("main pid:%d, tid:%lu\n", getpid(), pthread_self());
18
19          ret = pthread_create(&tid1, NULL, thread_fun, NULL);
20          if (ret == -1) {
21                  perror("cannot create new thread");
22                  return 1;
23          }
24
25          ret = pthread_create(&tid2, NULL, thread_fun, NULL);
26          if (ret == -1) {
27                  perror("cannot create new thread");
28                  return 1;
29          }
30
31          if (pthread_join(tid1, NULL) != 0) {
32                  perror("call pthread_join function fail");
33                  return 1;
34          }
35
36          if (pthread_join(tid2, NULL) != 0) {
37                  perror("call pthread_join function fail");
38                  return 1;
39          }
40
41          return 0;
42  }
~> gcc show_cores.c -pthread
~> ./a.out

执行结果如下所示:
在这里插入图片描述在上图中可以看到,a.out 的进程 cpu 占用率是 200% ,这是因为 Linux 操作系统会帮我们将 a.out 里面两个线程的负载平均到这两个 core 里面。
使用 time 这个命令,我们也可以看到一些 Linux 做 core 之间负载均衡的痕迹,下图所示,real 是程序真正在 cpu 执行的时间,user 是站在用户的角度执行的时候,user 大概是 real 的两倍,这说明 Linux 将两个线程执行时间平衡到两个 core 里面执行了,因为为我们节省了一半的时间了。
time

我们可以使用 taskset这个 shell 命令来设置进程在那个 core 里面跑。例如

taskset -a -p 01 2000 # 将 2000 进程所有的线程放到 01 这个 core 里面跑

taskset

taskset -a -p 01 2000 # 将 2000 进程所有的线程放到 01 这个 core 里面跑

taskset

中断的负载均衡

我们以网卡的中断来说明,如何做到网络的中断的负载均衡的。
如何下图所示:

在这里插入图片描述在上图中,可以发现我的虚拟机上,有两个网卡驱动,我们看看 ens33 的队列的负载均衡是怎么做的。 rps_cpus 里面可以设置中断队列的中断发送给那个 cpu 来处理。这是 linux 的 RPS(Receive Packet Steering) 补丁,这个补丁解决了中断分配不均匀的问题。我们可以做下面的修改:

~> echo 'fffe' > /sys/class/net/lo/queues/rx-0/rps_cpus

这样设计就把中断分给了 01、02、03 三个 core 里面了。
可以使用下面的 shell 语句进行观察:

watch -d "cat /proc/softirqs | grep 'NET_RX'"

cgroup

cgroup 是 Linux 提供一种 cpu 资源隔离的机制。它是为了解决用户级的进程调度公平。
假如有一个用户底下有 1024 个进程,另外一个用户下面有 52 个进程,那A用户很明显会占用更多的资源,因为 CFS 尽量的让每个进程公平得获得 CPU 资源,这样积累下来第一个用户获得比第二个进程获得更多得资源,如果,如果第二个进程是 CPU 消耗型的虽然进程数量少,但是需要的 CPU资源更多。所以CFS可能能会造成用户级的不公平。

那咋办呢?当然是是有办法了,这就是 cgroup 了。

先不解释概念,先一波如下一顿操作:

# 启动三个 a.out 程序
~> ./a.out &
~> ./a.out &
~> ./a.out &

~> cd /sys/fs/cgroup/cpu
~> mkdir A 
~> mkdir B
~> echo 1834 > A/cgroup.procs
~> echo 1837 > A/cgroup.procs
~> echo 1840 > B/cgroup.procs

在这一波操作前三个 a.out 的 cpu 占用率如下所示:
a.out进行完这波操作完,再来看看三个 a.out 的 cpu 占用率:
在这里插入图片描述在路径 /sys/fs/cgroup/cpu 下面我们我们可以像新建文件夹一样的创建一个 cgroup ,然后我们可以在 cgroup.proc 里面写入进程ID好,这样就把 pid 进程放到了这个 cgroup 里面了。

可以调整几个参数来调整 cgroup 里面进程的 cpu 占用率。

先来看看看 pid 和 cgroup 的关系。如下所示:

pidcgroup
1840B
1834A
1837A

它们的 cpu 占用率如下所示:
cpu我们进行如下设置:

~> echo 512 > B/cpu.shares

这个命令会将 B 的CPU占用的优先级设置为 512 , A 的 cpu.shares 的值为 1024 。A 是 B的二倍,所以 A 可以分到三个二的 CPU ,B可以分到三分之一的 CPU时间。

结果如下下图所示:

在这里插入图片描述
在来看看其的参数:

  • cpu.rt_period_us: RT型进程一个周期的微秒数
  • cpu.rt_runtime_us:RT型进程能在 cpu.rt_period_us 中跑多长时间
  • cpu.cfs_period_us:normal 型进程一个周期的微秒数
  • cpu.cfs_quota_us:normal 型进程能在 cpu.rt_period_us 中跑多长时间

来看看 cpu.cfs_quota 是咋用的:

~> echo 10000 > A/cpu.cfs_quota_us

在执行之前:

在这里插入图片描述在执行之后:

在这里插入图片描述我们知道,1837和1834 是在 A 中执行的,我们把 A 中 normal 类型的进程的运行时间减少后,CPU 资源大多给了 A , 所以 1840 这个进程就获得大部分的 CPU资源。

这就是cgroup.

Linux 不是一个实时操作系统

什么样子的系统称之为实时操作系统呢?真相只有一个,那就是进程从就绪到执行的等待时间是可预期的。

在这里插入图片描述怎么理解可预期呢?就是一个进程 RT 从唤醒到获得 CPU 资源的时间不会超过一个阈值。如果超过了那这个操作系统就不能称为实时操作系统。

原因是 RT 进程在好三类 CPU 区间下不能抢到 CPU 资源。

不能抢的情况是有下面几个:

  • 进程上下切换过程中拿到 spinlock,进程中有 spinlock
  • 中断和软中断
  • 内核进程不能抢

这三类 CPU区间 CPU 占用时长都是不确定的。所以 Linux 不是硬实时操作系统。

可以安装 preempt_rt 这补丁来把 Linux 做成一个近似的硬实时操作系统。

这个补丁实现下面的功能:

  • spinlock 迁移为可调度的 mutex ,其中 mutex 是可以抢的。
  • 实现优先级继承协议,举个例子, A B C 三个进程, 他们优先级是 1 2 3 , A 和 C 两个之间存在锁,C 控制了锁,但是 C 的优先级比较低,C 总是抢不到 CPU,所以 A 有等 C 好长时间。优先级继承协议是将 C 从优先级设置成 1 ,这样的话,C 就能比较快速的干完自己的事情,干完后就可以把锁给 A了
  • 中断和软中断线程化,什么是中断线程化,我也不知道?需要进一步的了解。

番外知识点

fork 我已经知道是个什么东西了,exce 不执行,exce 说白了就是挂羊头买狗肉的做法,首先就是用 fork 出来进程的内存、代码端、程序计数器等一个完整的进程;然后使用把这些都擦除掉,然后将我们希望运行的程序放到刚才说的进程结构里面去,酒瓶子就装上了新酒。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值