04 不可中断睡眠对容器的影响

一 引入问题

我们可以使用cpu cgroup对容器的cpu资源进行限制,但是cpu cgroup并非是万能的,它无法限制Load Average,没有这个限制,会影响我们对资源的合理调度,进而导致系统变得很慢

例如:

有时你会遇到所有容器进程、以及宿主机的cpu使用率都很低,但是容器里的应用运行速度非常慢,并且查看宿主机的Load Average的平均负载会发现非常高。

这到底是因为什么导致的呢?

一个是Load Average的统计方式,另外一个就是不可中断睡眠

问题展示

先来查看一下初始的load average,cpu的剩余很充足,load average也不高

然后我来模拟一下,cpu idle剩余率很多、cpu很充足,但是load average非常高

复习:
什么时候进程会处于不可中断睡眠状态,例如,当一个进程向磁盘读写数据时,为了保证数据的一致性,在得到磁盘回复前,它是不能被其他进程或者中断打断的,这个时候的进程就处于不可中断状态。如果此时的进程被打断了,就容易出现磁盘数据与进程数据不一致的问题。

所以,不可中断状态实际上是系统对进程和硬件设备的一种保护机制。

linux系统中也存在容易捕捉的TASK_UNINTERRUPTIBLE状态。执行vfork系统调用后,父进程将进TASK_UNINTERRUPTIBLE状态,直到子进程调用exit或exec 通过下面的代码就能得到处于TASK_UNINTERRUPTIBLE状态的进程:

# linux系统中也存在容易捕捉的TASK_UNINTERRUPTIBLE状态。执行vfork系统调用后,父进程将进TASK_UNINTERRUPTIBLE状态,直到子进程调用exit或exec 
# 通过下面的代码就能得到处于TASK_UNINTERRUPTIBLE状态的进程:

[root@test04 ~]# cat test.c 
void main() {  
if (!vfork()) while (1) {sleep(1);};  
}
[root@test04 ~]# gcc -o test test.c 

[root@test04 ~]# for i in `seq 1 100`;do (./test &) ;done  # 启动100个D状态的进程

让子弹飞一会之后,再次查看你会发现cpu剩余率量大很充足,但是平均负载却很高

接下来问题就来

1、我们查看一个cpu的使用情况,到底是以cpu usage为准,还是以load average为准呢?

2、D状态的进程为何会导致load average升高呢?

二 Load Average的统计方式

top命令可以看到load average,分别对应1分钟、5分钟、15分钟的cpu平均负载

——————–》先说答案

linux起源于unix,在unix系统里cpu负载load average并没有计算不可中断睡眠D状态的进程,但在linux系统里却为load average加入了D状态进程的统计,因为一位linux大佬Matthias发现,D状态代表的是正在做io等待磁盘读写完比如

我们的程序有三行代码,运行到第二行要把一个视频文件读入内存,第三行才能处理,此时因为磁盘速度太慢了,导致io积压,
比如在虚拟机里压缩一个大文件,该压缩进程就有可能进入D状态,总之D与S是有本质区别的,S状态类似python中input接收输入的代码,
代表程序睡眠阻塞住了,什么时候有内容输入并不知道,而D状态代表明确正在做io,
要等待做完才能进行下一个

处于D状态,cpu要循环着来问你做完了没有,这个是消耗cpu的,但是不会统计到占用率里,
所以该linux大佬,就把D状态也加入了load average里,但是每个项目如us、sys、id、wa、hi、si、st依然是不会计算D的,
于是当D状态过多,就可能程序出一种效果:所有进程的cpu占用率非常低,但是cpu的1分钟5分钟15分钟负载非常高,这个会影响k8s的调度

思考

一般来说,IO 设备的读写是比较快的,如果 IO 设备出现瓶颈,势必会造成大量的进程处于等待 IO 的状态,这种情况下,虽然不关 CPU 什么事,整个系统的处理能力其实已经出现的很大的瓶颈,
所以把 D 状态的进程算在平均负载里也还算合理。当系统 load average 比较高时,首先我们需要去甄别,到底是 CPU 的问题还是 IO 的问题。

————-》再来展开介绍

早在unix系统里,就有load average的概念了,load average是一种cpu资源需求的度量。

在大多数unix发行版里:

load average=正在运行的进程数 + 已经存在于操作系统的进程可运行队列的进程(只要申请到cpu资源即可投入运行)数

如果你的物理机有1颗cpu,那么满负载为1,代表可以同时运行1个进程,超过1就代表超载,小于1就代表空闲

如果你的物理机有4颗cpu,那么满负责为4,代表可以同时运行4个进程,超过4则代表超载,小于4就代表空闲

以1颗cpu为例,如果处理器上有一个R的进程,同时在系统的进程可运行队列里有9个进程,那么1分钟的load average=1+9

而在linux系统里:

load average=正在运行的进程数 + 已经存在于操作系统的进程可运行队列的进程(只要申请到cpu资源即可投入运行)数+休眠队列中处于不可中断睡眠D状态的进程数

打个比方,一个cpu就好比是一条车道,每个进程都是一辆车,load average就代表交通情况,具体是指这条路的单位时间通过的车辆数,D状态的进程就好比是车辆在等待红绿灯,linux系统在计算load average时,做个补丁把D状态的进程也考虑了,这种统计更为科学,一条路的路况肯定是需要考虑红绿灯情况的,想想就明白了,哪怕有一条路很空,但是红绿特别多,汽车也需要排队,不可能开快。

所以在系统没有大量的D状态进程时,linux系统的load average与unix的统计方式是一样的

[root@test04 ~]# cat libdead_loop.c 
void DeadLoop() {
     while (1) {
         ; 
     }
} 
[root@test04 ~]# gcc libdead_loop.c -fPIC -shared -o /opt/libdead_loop.so
[root@test04 ~]# cat test.py 
#coding:utf-8
from ctypes import cdll
from threading import Thread
import sys

if __name__ == "__main__":
    if not (len(sys.argv) == 2 and sys.argv[1].isdigit()):
        print("Usage: python xxx.py cpu个数")
        exit()

    num = int(sys.argv[1])
    lib = cdll.LoadLibrary("/opt/libdead_loop.so")
    for i in range(num):
        Thread(target=lib.DeadLoop).start()

[root@test04 ~]# python test.py 2 &
[1] 67657

我们启动一个占用2颗cpu的的进程,会发现一分钟负责为2.08与我们的cpu usage吻合,此外我们有4颗cpu,你占用2颗,所以总使用率为50%

但如果我们启动了大量的D进程,load average便不会与cpu usage一致,而是会超过它

三 D状态导致性能下降分析

联系管理员微信tutu19192010,注册账号

上一篇
下一篇
Copyright © 2022 Egon的知识星球 egonlin.com 版权所有 帮助IT小伙伴学到真正的技术