1、内核进程

我们知道,在 Linux 中,用户态进程的“祖先”,都是 PID 号为 1 的 init 进程。比如,现在主流的 Linux 发行版中,init 都是 systemd 进程;而其他的用户态进程,会通过 systemd 来进行管理。

Linux 在启动过程中,有三个特殊的进程,也就是 PID 号最小的三个进程。

  • 0 号进程为 idle 进程,这也是系统创建的第一个进程,它在初始化 1 号和 2 号进程后,演变为空闲任务。当 CPU上没有其他任务执行时,就会运行它。
  • 1 号进程为 init 进程,通常是 systemd 进程,在用户态运行,用来管理其他用户态进程。
  • 2号进程为 kthreadd进程,在内核态运行,用来管理内核线程。

所以,要查找内核线程,我们只需要从 2 号进程开始,查找它的子孙进程即可。比如,你可以使用 ps 命令,来查找 kthreadd 的子进程:

所以,要查找内核线程,我们只需要从 2 号进程开始,查找它的子孙进程即可。比如,你可以使用 ps 命令,来查找 kthreadd 的子进程:

$ ps -f --ppid 2 -p 2
UID         PID   PPID  C STIME TTY          TIME CMD
root          2      0  0 12:02 ?        00:00:01 [kthreadd]
root          9      2  0 12:02 ?        00:00:21 [ksoftirqd/0]
root         10      2  0 12:02 ?        00:11:47 [rcu_sched]
root         11      2  0 12:02 ?        00:00:18 [migration/0]
...
root      11094      2  0 14:20 ?        00:00:00 [kworker/1:0-eve]
root      11647      2  0 14:27 ?        00:00:00 [kworker/0:2-cgr]

使用中括号查找内核进程

$ ps -ef | grep "\[.*\]"
root         2     0  0 08:14 ?        00:00:00 [kthreadd]
root         3     2  0 08:14 ?        00:00:00 [rcu_gp]
root         4     2  0 08:14 ?        00:00:00 [rcu_par_gp]
...

常见内核进程

  • kswapd0:用于内存回收。在 Swap 变高 案例中,我曾介绍过它的工作原理。
  • kworker:用于执行内核工作队列,分为绑定 CPU(名称格式为 kworker/CPU86330)和未绑定 CPU(名称格式为
    kworker/uPOOL86330)两类。
  • migration:在负载均衡过程中,把进程迁移到 CPU 上。每个 CPU都有一个migration 内核线程。
  • jbd2/sda1-8:jbd 是 JournalingBlockDevice的缩写,用来为文件系统提供日志功能,以保证数据的完整性;名称中的
    sda1-8,表示磁盘分区名称和设备号。每个使用了ext4文件系统的磁盘分区,都会有一个 jbd2 内核线程。
  • pdflush:用于将内存中的脏页(被修改过,但还未写入磁盘的文件页)写入磁盘(已经在3.10 中合并入了 kworker 中)。

2、环境准备

# 运行Nginx服务并对外开放80端口
$ docker run -itd --name=nginx -p 80:80 nginx

在客户端测试服务端

$ curl http://192.168.0.30/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

在客户端发送请求

# -S参数表示设置TCP协议的SYN(同步序列号),-p表示目的端口为80
# -i u10表示每隔10微秒发送一个网络帧
# 注:如果你在实践过程中现象不明显,可以尝试把10调小,比如调成5甚至1
$ hping3 -S -p 80 -i u10 192.168.0.30

在服务端top查看

$ top
top - 08:31:43 up 17 min,  1 user,  load average: 0.00, 0.00, 0.02
Tasks: 128 total,   1 running,  69 sleeping,   0 stopped,   0 zombie
%Cpu0  :  0.3 us,  0.3 sy,  0.0 ni, 66.8 id,  0.3 wa,  0.0 hi, 32.4 si,  0.0 st
%Cpu1  :  0.0 us,  0.3 sy,  0.0 ni, 65.2 id,  0.0 wa,  0.0 hi, 34.5 si,  0.0 st
KiB Mem :  8167040 total,  7234236 free,   358976 used,   573828 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  7560460 avail MemPID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND9 root      20   0       0      0      0 S   7.0  0.0   0:00.48 ksoftirqd/018 root      20   0       0      0      0 S   6.9  0.0   0:00.56 ksoftirqd/12489 root      20   0  876896  38408  21520 S   0.3  0.5   0:01.50 docker-containe3008 root      20   0   44536   3936   3304 R   0.3  0.0   0:00.09 top1 root      20   0   78116   9000   6432 S   0.0  0.1   0:11.77 systemd...

从 top 的输出中,你可以看到,两个 CPU 的软中断使用率都超过了 30%;而 CPU 使用率最高的进程,正好是软中断内核线程 ksoftirqd/0 和 ksoftirqd/1。

虽然,我们已经知道了 ksoftirqd 的基本功能,可以猜测是因为大量网络收发,引起了 CPU 使用率升高;但它到底在执行什么逻辑,我们却并不知道。

3、使用perf分析内核进程

执行下面的 perf record 命令;并指定进程号 9 ,以便记录 ksoftirqd 的行为:

# 采样30s后退出
$ perf record -a -g -p 9 -- sleep 30

稍等一会儿,在上述命令结束后,继续执行 perf report命令,你就可以得到 perf 的汇总报告。按上下方向键以及回车键,展开比例最高的 ksoftirqd 后,你就可以得到下面这个调用关系链图:

从这个图中,你可以清楚看到 ksoftirqd 执行最多的调用过程。虽然你可能不太熟悉内核源码,但通过这些函数,我们可以大致看出它的调用栈过程。

  • net_rx_action 和 netif_receive_skb,表明这是接收网络包(rx 表示receive)。
  • br_handle_frame ,表明网络包经过了网桥(br 表示 bridge)。
  • br_nf_pre_routing,表明在网桥上执行了 netfilter 的 PREROUTING(nf 表示 netfilter)。而我们已经知道 PREROUTING
    主要用来执行 DNAT,所以可以猜测这里有 DNAT发生。
  • br_pass_frame_up,表明网桥处理后,再交给桥接的其他桥接网卡进一步处理。比如,在新的网卡上接收网络包、执行netfilter过滤规则等等。

前面 perf report 界面的调用链还可以继续展开。但很不幸,我的屏幕不够大,如果展开更多的层级,最后几个层级会超出屏幕范围。这样,即使我们能看到大部分的调用过程,却也不能说明后面层级就没问题。

4、生成perf火焰图

下载火焰图

$ git clone https://github.com/brendangregg/FlameGraph
$ cd FlameGraph

安装好工具后,要生成火焰图,其实主要需要三个步骤:

  • 执行 perf script ,将 perf record 的记录转换成可读的采样记录;
  • 执行 stackcollapse-perf.pl脚本,合并调用栈信息;
  • 执行 flamegraph.pl 脚本,生成火焰图。

不过,在 Linux 中,我们可以使用管道,来简化这三个步骤的执行过程。假设刚才用 perf record 生成的文件路径为 /root/perf.data,执行下面的命令,你就可以直接生成火焰图:

$ perf script -i /root/perf.data | ./stackcollapse-perf.pl --all |  ./flamegraph.pl > ksoftirqd.svg

执行成功后,使用浏览器打开 ksoftirqd.svg ,你就可以看到生成的火焰图了。如下图所示:

要理解火焰图,我们最重要的是区分清楚横轴和纵轴的含义。

  • 横轴表示采样数和采样比例。一个函数占用的横轴越宽,就代表它的执行时间越长。同一层的多个函数,则是按照字母来排序。
  • 纵轴表示调用栈,由下往上根据调用关系逐个展开。换句话说,上下相邻的两个函数中,下面的函数,是上面函数的父函数。这样,调用栈越深,纵轴就越高。

另外,要注意图中的颜色,并没有特殊含义,只是用来区分不同的函数。火焰图是动态的矢量图格式,所以它还支持一些动态特性。

比如,鼠标悬停到某个函数上时,就会自动显示这个函数的采样数和采样比例。而当你用鼠标点击函数时,火焰图就会把该层及其上的各层放大,方便你观察这些处于火焰图顶部的调用栈的细节。

  • 我们顺着调用栈由下往上看(顺着图中蓝色箭头),就可以得到跟刚才 perf report 中一样的结果:
  • 最开始,还是 net_rx_action 到 netif_receive_skb 处理网络收包;
  • 然后, br_handle_frame 到 br_nf_pre_routing ,在网桥中接收并执行 netfilter 钩子函数;
  • 再向上, br_pass_frame_up 到 netif_receive_skb ,从网桥转到其他网络设备又一次接收。

不过最后,到了 ip_forward 这里,已经看不清函数名称了。所以我们需要点击 ip_forward,展开最上面这一块调用栈:

这样,就可以进一步看到 ip_forward 后的行为,也就是把网络包发送出去。根据这个调用过程,再结合我们前面学习的网络收发和 TCP/IP 协议栈原理,这个流程中的网络接收、网桥以及 netfilter 调用等,都是导致软中断 CPU 升高的重要因素,也就是影响网络性能的潜在瓶颈。

linux性能优化——利用perf火焰图分析内核调用相关推荐

  1. 查看linux内存优化,Linux性能优化和监控系列(三) 分析Memory使用状况

    Linux性能优化和监控系列(三) 分析Mem 分析Memory使用状况 内存是影响服务器性能的一个主要因素, 当进程已经驻留内存或者系能够分配给进程足够的内存给它, CPU能顺利自如的运行. 如果发 ...

  2. perf + 火焰图分析程序性能

    From: https://www.cnblogs.com/happyliu/p/6142929.html 1.perf命令简要介绍 性能调优时,我们通常需要分析查找到程序百分比高的热点代码片段,这便 ...

  3. Linux性能优化实战: 套路篇-分析性能问题的一般步骤(55)

    一.上节回顾 上一节,我们一起学习了,应用程序监控的基本思路,先简单回顾一下.应用程序的监控,可以分为指标监控和日志监控两大块. 指标监控,主要是对一定时间段内的性能指标进行测量,然后再通过时间序列的 ...

  4. C++ 之 perf+火焰图分析与Debug

    0. 简介 在遇到一些内存异常的时候,经常这部分的代码是很难去进行分析的,之前作者就专门写过两篇博客(Ubuntu环境下便于调试代码的工具.Valgrind对ROS程序的可视化分析)来介绍性能分析的问 ...

  5. linux内存的active,Linux性能优化和监控系列(三)——分析Memory使用状况

    分析Memory使用状况 内存是影响服务器性能的一个主要因素, 当进程已经驻留内存或者系能够分配给进程足够的内存给它, CPU能顺利自如的运行. 如果发生内存不足, 服务器使用I/O channel获 ...

  6. linux cpu监控方案,Linux性能优化和监控系列(二)分析CPU性能

    分析CPU性能 top命令提供了监控CPU性能的基本功能, 如果需要更加深入的挖掘CPU的性能问题, top所提供的信息不足以做到. 由于大多数人认为CPU性能是体现服务器性能的主要因素, 所以在遇到 ...

  7. mysql火焰图_【性能】如何使用perf和火焰图分析系统性能?

    一.实验环境 二.实验案例分析 安装完成后,我们先在第一个终端,执行下面的命令运行案例,也就是一个最基本的 Nginx 应用: 运行 Nginx 服务并对外开放 80 端口 # docker run ...

  8. 【性能】perf + 火焰图分析软件性能瓶颈

    目录 零.即看即用 一.perf 命令 perf简介 perf record参数 命令例子 二.火焰图的含义 三.互动性 四.火焰图示例 五.局限 六.Node 应用的火焰图 七.浏览器的火焰图 八. ...

  9. 【linux性能优化】内核线程CPU利用率高分析

    在排查网络问题时经常碰到的一个问题,就是内核线程的CPU使用率很高 比如,在高并发的场景中内核线程ksoftirqd的CPU使用率通常就会比较高,根据CPU和网络模块知识可以得知,这是网络收发的软中断 ...

最新文章

  1. 【原】Github系列之三:开源iOS下 渐变颜色的进度条WGradientProgress
  2. 2021春季学期-创新设计与实践-Lesson5
  3. hadoop完全分布式集群群起
  4. 简单哈弗曼树(Java)
  5. 【python数据挖掘课程】二十二.Basemap地图包安装入门及基础知识讲解
  6. python 管道队列_20.2、python进程间通信——队列和管道
  7. 软件项目开发 学校自行开发_自行开发游戏
  8. leetcode 665. 非递减数列(贪心算法)
  9. LwIP Application Developers Manual9---LwIP and multithreading
  10. 【Tomcat】Tomcat下设置项目为默认项目
  11. 完整的Flex多文件上传实例
  12. 编程算法/面试 - K链表翻转
  13. JavaScript函数setInterval()和setTimeout()正确的写法
  14. jQuery之筛选函数
  15. IDEA 插件开发 向主菜单注册菜单项目
  16. 清华大学计算机系张昕,程序设计语言的研究与发展——如何推进国内程序设计语言的教育和研究?丨CNCC技术论坛...
  17. iOS-监听UITextView、UITextField键盘删除键
  18. phpstudy下载安装简明教程+软件下载(图文)
  19. 不想沦为新一代的“农民工”那就脱掉程序猿的旧外套来架构解密吧
  20. DataFrame groupby+agg出现SpecificationError: nested renamer is not supported的错误

热门文章

  1. 【转帖】NAT在NDIS中间层驱动中的实现
  2. rabbitmq集群安装与配置(故障恢复)
  3. c语言s型曲线方程,电机控制 | S曲线加减速
  4. (016) 反射 API
  5. 百度网盘隐藏空间显示服务器,百度网盘隐藏空间在哪怎么打开?百度网盘隐藏空间这样开...
  6. 说说JavaScript的原型链
  7. linux安装globalsign证书,globalsign代码签名证书
  8. 【js-xlsx和file-saver插件】前端html的table导出数据到excel的表格合并显示boder
  9. 广州大学数据库实验三——数据库系统设计综合实验
  10. Linux下ffmpeg的完整安装