Linux性能优化实战
倪朋飞
微软资深工程师,Kubernetes项目维护者
立即订阅
23395 人已学习
课程目录
已完结 64 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (2讲)
开篇词 | 别再让Linux性能问题成为你的绊脚石
免费
01 | 如何学习Linux性能优化?
CPU 性能篇 (13讲)
02 | 基础篇:到底应该怎么理解“平均负载”?
03 | 基础篇:经常说的 CPU 上下文切换是什么意思?(上)
04 | 基础篇:经常说的 CPU 上下文切换是什么意思?(下)
05 | 基础篇:某个应用的CPU使用率居然达到100%,我该怎么办?
06 | 案例篇:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?
07 | 案例篇:系统中出现大量不可中断进程和僵尸进程怎么办?(上)
08 | 案例篇:系统中出现大量不可中断进程和僵尸进程怎么办?(下)
09 | 基础篇:怎么理解Linux软中断?
10 | 案例篇:系统的软中断CPU使用率升高,我该怎么办?
11 | 套路篇:如何迅速分析出系统CPU的瓶颈在哪里?
12 | 套路篇:CPU 性能优化的几个思路
13 | 答疑(一):无法模拟出 RES 中断的问题,怎么办?
14 | 答疑(二):如何用perf工具分析Java程序?
内存性能篇 (8讲)
15 | 基础篇:Linux内存是怎么工作的?
16 | 基础篇:怎么理解内存中的Buffer和Cache?
17 | 案例篇:如何利用系统缓存优化程序的运行效率?
18 | 案例篇:内存泄漏了,我该如何定位和处理?
19 | 案例篇:为什么系统的Swap变高了(上)
20 | 案例篇:为什么系统的Swap变高了?(下)
21 | 套路篇:如何“快准狠”找到系统内存的问题?
22 | 答疑(三):文件系统与磁盘的区别是什么?
I/O 性能篇 (10讲)
23 | 基础篇:Linux 文件系统是怎么工作的?
24 | 基础篇:Linux 磁盘I/O是怎么工作的(上)
25 | 基础篇:Linux 磁盘I/O是怎么工作的(下)
26 | 案例篇:如何找出狂打日志的“内鬼”?
27 | 案例篇:为什么我的磁盘I/O延迟很高?
28 | 案例篇:一个SQL查询要15秒,这是怎么回事?
29 | 案例篇:Redis响应严重延迟,如何解决?
30 | 套路篇:如何迅速分析出系统I/O的瓶颈在哪里?
31 | 套路篇:磁盘 I/O 性能优化的几个思路
32 | 答疑(四):阻塞、非阻塞 I/O 与同步、异步 I/O 的区别和联系
网络性能篇 (13讲)
33 | 关于 Linux 网络,你必须知道这些(上)
34 | 关于 Linux 网络,你必须知道这些(下)
35 | 基础篇:C10K 和 C1000K 回顾
36 | 套路篇:怎么评估系统的网络性能?
37 | 案例篇:DNS 解析时快时慢,我该怎么办?
38 | 案例篇:怎么使用 tcpdump 和 Wireshark 分析网络流量?
39 | 案例篇:怎么缓解 DDoS 攻击带来的性能下降问题?
40 | 案例篇:网络请求延迟变大了,我该怎么办?
41 | 案例篇:如何优化 NAT 性能?(上)
42 | 案例篇:如何优化 NAT 性能?(下)
43 | 套路篇:网络性能优化的几个思路(上)
44 | 套路篇:网络性能优化的几个思路(下)
45 | 答疑(五):网络收发过程中,缓冲区位置在哪里?
综合实战篇 (13讲)
46 | 案例篇:为什么应用容器化后,启动慢了很多?
47 | 案例篇:服务器总是时不时丢包,我该怎么办?(上)
48 | 案例篇:服务器总是时不时丢包,我该怎么办?(下)
49 | 案例篇:内核线程 CPU 利用率太高,我该怎么办?
50 | 案例篇:动态追踪怎么用?(上)
51 | 案例篇:动态追踪怎么用?(下)
52 | 案例篇:服务吞吐量下降很厉害,怎么分析?
53 | 套路篇:系统监控的综合思路
54 | 套路篇:应用监控的一般思路
55 | 套路篇:分析性能问题的一般步骤
56 | 套路篇:优化性能问题的一般方法
57 | 套路篇:Linux 性能工具速查
58 | 答疑(六):容器冷启动如何性能分析?
加餐篇 (4讲)
加餐(一) | 书单推荐:性能优化和Linux 系统原理
加餐(二) | 书单推荐:网络原理和 Linux 内核实现
用户故事 | “半路出家 ”,也要顺利拿下性能优化!
用户故事 | 运维和开发工程师们怎么说?
结束语 (1讲)
结束语 | 愿你攻克性能难关
Linux性能优化实战
登录|注册

07 | 案例篇:系统中出现大量不可中断进程和僵尸进程怎么办?(上)

倪朋飞 2018-12-05
你好,我是倪朋飞。
上一节,我用一个 Nginx+PHP 的案例,给你讲了服务器 CPU 使用率高的分析和应对方法。这里你一定要记得,当碰到无法解释的 CPU 使用率问题时,先要检查一下是不是短时应用在捣鬼。
短时应用的运行时间比较短,很难在 top 或者 ps 这类展示系统概要和进程快照的工具中发现,你需要使用记录事件的工具来配合诊断,比如 execsnoop 或者 perf top。
这些思路你不用刻意去背,多练习几次,多在操作中思考,你便能灵活运用。
另外,我们还讲到 CPU 使用率的类型。除了上一节提到的用户 CPU 之外,它还包括系统 CPU(比如上下文切换)、等待 I/O 的 CPU(比如等待磁盘的响应)以及中断 CPU(包括软中断和硬中断)等。
我们已经在上下文切换的文章中,一起分析了系统 CPU 使用率高的问题,剩下的等待 I/O 的 CPU 使用率(以下简称为 iowait)升高,也是最常见的一个服务器性能问题。今天我们就来看一个多进程 I/O 的案例,并分析这种情况。

进程状态

当 iowait 升高时,进程很可能因为得不到硬件的响应,而长时间处于不可中断状态。从 ps 或者 top 命令的输出中,你可以发现它们都处于 D 状态,也就是不可中断状态(Uninterruptible Sleep)。既然说到了进程的状态,进程有哪些状态你还记得吗?我们先来回顾一下。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Linux性能优化实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(98)

  • 白华
    老师以后的案例能不能使用centos7系统进行操作?做的很多实验和你的都会有部分偏差,这次偏差更大,相信学习你课程的大部分都是用虚拟机跑的项目,用centos系统使用率会很高,而且实际生产中用centos系统肯定大于Ubuntu,造成的实验偏差会不会也是系统的原因。我也遇到了没有出现D状态的进程,出现了大量Z进程。平均负载并没有提升,反而是下降了。iowait并没有变化。所以恳请您使用centos系统来教学吧

    作者回复: iowait不高是因为案例IO操作不够大导致的。我重新推了一个docker镜像,麻烦再试下看看

    2018-12-05
    1
    35
  • 书林
    每个人的机器配置不一样,所以会出现有的机器iowait不明显,有的机器被打爆。解决办法是用docker cgroup选项对 block io做限制。假设硬盘设备为 /dev/nvme0n1,测试如下:
    1. 限制块设备的读写 iops 为 3: `docker run --privileged --name=app9 --device /dev/nvme0n1:/dev/nvme0n1 --device-write-iops /dev/nvme0n1:3 --device-read-iops /dev/nvme0n1:3 -itd feisky/app:iowait-new2`
    2. 可以查看host机器 cgroup 已为对应 docker container 添加了相关限制:
    ```
    cat /sys/fs/cgroup/blkio/docker/"docker-container-id"/blkio.throttle.write_iops_device
    259:0 3
    cat /sys/fs/cgroup/blkio/docker/"docker-container-id"/blkio.throttle.read_iops_device
    259:0 3
    ```
    3.
    ```
    docker exec -it "docker-container-id" /bin/bash
    root@4cc5e6c74cc0:/# dd iflag=direct if=/dev/nvme0n1 of=/dev/null bs=1k count=1000
    1000+0 records in
    1000+0 records out
    1024000 bytes (1.0 MB, 1000 KiB) copied, 340.004 s, 3.0 kB/s
    ```
    `dd` 每次从 /dev/nvme0n1 设备读取数据写到 /dev/null 设备,每次读取 1kB,一共1000次,必须为 direct 选项。可以观测到拷贝速度为 3 kB/s,即 1kB * 3,说明cgroup 限制 `blkio.throttle.read_iops_device` 生效。

    4. 观察host机器 iowait 已经上去。
    ```
    top - 12:10:22 up 1:25, 1 user, load average: 0.88, 0.81, 0.83
    任务: 780 total, 1 running, 227 sleeping, 0 stopped, 552 zombie
    %Cpu0 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 s
    %Cpu1 : 2.7 us, 0.0 sy, 0.0 ni, 97.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 s
    %Cpu2 : 0.0 us, 0.0 sy, 0.0 ni, 0.0 id,100.0 wa, 0.0 hi, 0.0 si, 0.0 s
    %Cpu3 : 5.3 us, 7.9 sy, 0.0 ni, 84.2 id, 0.0 wa, 0.0 hi, 2.6 si, 0.0 s
    MiB Mem : 7863.3 total, 230.4 free, 3847.2 used, 3785.8 buff/cache
    MiB Swap: 8192.0 total, 8191.5 free, 0.5 used. 3191.1 avail Mem
    ```
    zombie数那么高是因为这个 docker container 已经运行20多分钟了。

    供大家参考:)

    作者回复: 谢谢分享,见到Docker高手了😊。 这样的确可以达到IO限制的目的,不过使用系统级工具分析的时候,会有很大不同,比如iostat看看磁盘使用率可能还是很空闲;或者看看内核调用栈也有些不同。

    不过这倒是不错的性能隔离方案👍

    2018-12-09
    1
    20
  • songgoogle
    麻烦换centos吧,更接近实际工作需求
    2018-12-07
    16
  • 丁兆鹏
    centos7 中模拟一下一起docker中无法启动app
     docker ps -a
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    54a43bfd9ddb feisky/app:iowait "/app" 7 seconds ago Exited (1) 6 seconds ago app

    docker logs app也为空。

    既然是c程序,使用gdb调试,发现在select_disk()

    const char *sd_prefix = "sd";
    const char *xvd_prefix = "xvd";
    ...
    if (strncmp(sd_prefix, entry->d_name, 2) == 0 || strncmp(xvd_prefix, entry->d_name, 3) == 0)

    看看机器上磁盘格式如下:
    df -h
    Filesystem Size Used Avail Use% Mounted on
    /dev/vda1 99G 16G 79G 17% /
    /dev/vdb1 99G 5.5G 88G 6% /data1

    找到原因了,磁盘前缀不同,无法找到的盘,修改app.c代码
            const char *sd_prefix = "vd";
            const char *xvd_prefix = "vdb";

    然后就可以正常启动了。
    docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    ceb6eec8129a feisky/app:iowait "/app" 2 seconds ago Up 1 second app

    作者回复: 👍 高手,这是案例考虑不周了,已经在github上增加了参数

    2018-12-17
    14
  • Johnson
    遇到有大量的D状态的进程,导致负载到7000多,但是cpu和iowait都不高,除了重启设备还有什么办法解决?
    2018-12-05
    12
  • 每天晒白牙
    【D7打卡】
    今天主要学习进程的状态,可以通过ps或top查看进程的状态
    R:运行 Running或Runnable的缩写 表示进程在CPU的就绪队列中,正在运行或正在等待运行
    I:空闲 Idle的缩写,用在不可中断睡眠的内核线程上。空闲线程不会导致平均负载升高,D状态的会导致平均负载升高
    D:不可中断睡眠 Dist Sleep的缩写 表示进程正在跟硬件交互,并且交互过程中不允许被其他进程或中断打断
    S:可中断睡眠 Interruptible Sleep的缩写 表示进程因为等待某个事件而被系统挂起。当进程等待的事件发生时,它会被唤醒并进入R状态
    Z:僵尸 Zombie缩写 进程已经结束,但父进程还没有回收它的资源(如进程的描述符、PID等)
    T:暂停 Stopped或Traced的缩写,表示进程处于暂停或者跟踪状态
    今天实战的结果和老师的出入很大,我的系统是centos的
    ps aux | grep /app结果最开始是
    [root@localhost ~]# ps aux | grep /app
    root 3468 0.0 0.0 4364 380 pts/0 Ss+ 07:38 0:00 /app
    root 3568 12.0 3.3 37268 33036 pts/0 D+ 07:39 0:00 /app
    root 3569 7.0 3.3 37268 33036 pts/0 D+ 07:39 0:00 /app
    root 3571 0.0 0.0 112728 988 pts/4 S+ 07:39 0:00 grep --color=auto /app
    [root@localhost ~]# ps aux | grep /app
    root 3468 0.0 0.0 4364 424 pts/0 Ss+ 07:38 0:00 /app
    root 3590 15.0 3.3 37268 33016 pts/0 R+ 07:39 0:00 /app
    root 3591 15.0 3.3 37268 33016 pts/0 R+ 07:39 0:00 /app
    root 3593 0.0 0.0 112724 988 pts/4 R+ 07:39 0:00 grep --color=auto /app
    [root@localhost ~]# ps aux | grep /app
    root 3468 0.0 0.0 4364 424 pts/0 Ss+ 07:38 0:00 /app
    root 3597 0.0 0.0 112728 988 pts/4 S+ 07:39 0:00 grep --color=auto /app
    同样用top命令观察,平均负载也不高,然后wa也很低,也没看到处于D状态的进程。符合的只有 处于僵尸状态的进程比较多
    [root@localhost ~]# top
    top - 07:41:45 up 2:35, 8 users, load average: 0.00, 0.03, 0.31
    Tasks: 224 total, 1 running, 135 sleeping, 0 stopped, 88 zombie
    %Cpu0 : 0.7 us, 5.8 sy, 0.0 ni, 88.1 id, 4.1 wa, 0.0 hi, 1.4 si, 0.0 st
    %Cpu1 : 1.0 us, 7.1 sy, 0.0 ni, 86.7 id, 2.7 wa, 0.0 hi, 2.4 si, 0.0 st
    KiB Mem : 999720 total, 165196 free, 389676 used, 444848 buff/cache
    KiB Swap: 2097148 total, 1788172 free, 308976 used. 335844 avail Mem
    和老师反映了情况,老师说可能是案例里I/O线程还不够多,效果不明显,等着老师修改案例重新发镜像,再实验。
    2018-12-05
    6
  • 白华
    今天重新进行了测试,用了你docker hub上的fix2,fix1,new,new2都进行测试了,发现还是不行,iowait没有升高,平均负载没有上升,没有发现D状态。希望你以后使用centos7系统进行操作,性能会好很多
    2018-12-06
    1
    5
  • Brown羊羊
    没有模拟出来系统I/O瓶颈,可以帮忙看下吗:
    容器运行起来后只发现一个app进程
    [root@liyang2 ~]# ps aux|grep /app
    root 23619 0.0 0.0 4368 380 pts/0 Ss+ 17:12 0:00 /app
    root 23777 0.0 0.0 112648 952 pts/0 S+ 17:12 0:00 grep --color=auto /app

    CPU情况 wa也没有很高
    %Cpu(s): 1.0 us, 1.5 sy, 0.0 ni, 94.0 id, 3.3 wa, 0.0 hi, 0.2 si, 0.0 st

    系统:redhat7 3.10.0-327.el7.x86_64 x86_64 GNU/Linux

    作者回复: 我的机器配置太弱了,IO已经跑满还是好多人都没有观察到iowait的现象。重新推了一个镜像,加大了IO操作,再试试看现在怎么样

    2018-12-05
    3
  • 黄涛
    我是centOS,也是无法启动,参考评论中的方式,修改启动参数为:
    docker run --privileged --name=app -itd feisky/app:iowait /app -d /dev/vdb1
    就可以了

    作者回复: 谢谢分享😊

    2019-06-05
    2
  • 每天晒白牙
    【D7补卡】
    在和老师多次交流下,终于逼得老师发布一个把自己机器跑死的镜像,就可以了,结果和老师的温和了。看到老师之前的例子io压力还是不够啊
    docker run --privileged --name=app -itd feisky/app:iowait-new2
    执行这个镜像,iowait打满,直接把我的微信给挤掉了,浏览器都打不开了,不过结果是好的。
    继续坚持下去!
    2018-12-05
    2
  • 姜小鱼
    这个案例的iowait比较高,但是并不影响cpu使用率。因为准确来说,iowait也是属于cpu idle状态的一部分,他和僵尸进程影响的只是平均负载和系统资源

    作者回复: 确切的说是CPU繁忙程度,因为iowait也是CPU使用率的一种类型

    2018-12-05
    2
  • 耿长学
    学习了,每天上班路上听听音频看一看,晚上回家整理学习
    2018-12-05
    2
  • 一生一世
    我的思路是用1、pidstat看看上下文交换情况;2、vmstat看看wa;把僵尸进程的父进程停掉
    2018-12-05
    2
  • LA
    网络io也是属于不可中断的么?有些困惑,求老师指点下
    2019-05-28
    1
  • 林贻民
    老师您好,我有个疑惑:不可中断状态睡眠不能被其他进程或者中断打断,那照道理说如果不可中断进程很多(超过CPU)逻辑核数时,系统应该处于卡死状态,可是实际上并不是这样的,其他进程照样在执行,说明是存在进程调度,也就是存在重调度中断,键盘输入也有响应,说明也存在硬件中断,这个似乎和不可中断进程状态进程的不可中断产生了冲突?是我有什么地方理解错了吗?

    作者回复: 中断跟不可中断睡眠状态不是一回事,中断是会打断进程运行,而不可中断睡眠是指不可以被信号中断,而不是占着CPU不放了

    2019-03-25
    1
  • 辣椒
    评论里是高手辈出啊,两个问题都是看评论区的评论都解决了

    作者回复: 😊 高手如云

    2019-01-08
    1
  • 深蓝
    同问,关于Uninterruptible sleep(D)状态
    的进程如何有效的处理,以前运维的时候遇到过,貌似只能重启机器,不知道还有什么更好的办法

    作者回复: 一个基本的思路是要找出进程处于 D 状态的原因,是在等待什么样的I/O资源。比如分析系统调用、进程堆栈等等。

    找出根源之后,再去分析这些根源里面到底发生了什么,才导致的没有响应。

    当然,也有其他比较hack的方法,但生产环境中不推荐,以免给系统带来未知的损坏。

    2018-12-07
    1
  • CYH
    hi,老师:我晚上做的实验,操作系统是cenos7.5,我看您回复留言说已经更新可以提高iowaite了,但我这验证执行ps aux|grep app并没有发现D不可中断的进程从而导致io并没有提升,只是出现了很多僵尸进程。
    2018-12-06
    1
  • mj4ever
    通过实际操作和资料查阅,本次课程学到了以下知识:
    1、进程的多种状态,D (Disk Sleep) 状态的进程,会导致平均负载升高
    2、僵尸进程:
    (1)父子进程的运行是异步的过程,父进程需要知道子进程是何时关闭的
    (2)子进程需要父进程来收尸,但父进程没有安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,或是子进程执行太快,父进程还没来得及处理子进程状态
    (3)父进程退出后,init进程会自动接手子进程,进行清理
    (4)僵尸进程已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集
    (5)大量的僵尸进程会用尽 PID 进程号,导致新进程不能创建
    3、会话和进程组
    4、实操
    (1)查看了僵尸进程 查看僵尸进程的命令 ps -e -o stat,ppid,pid,cmd | egrep '^[Zz]' 或 ps -ef | grep "defunct",停止父进程后,僵尸进程会被回收
    (2)没有观察到io的问题,可能是和机器配置有关系,不知道是否可以暴露个参数让我们启动docker的时候传参来施加压力。
    2018-12-06
    1
  • xiao豪
    打卡,太坑了,开着docker边跟着老师操作,结果卡死了T_T
    2018-12-05
    1
收起评论
98
返回
顶部