linux的Top学习

学习文档

https://www.cnblogs.com/liulianzhen99/articles/17638178.html

TOP

问题 1:top 输出的利用率信息是如何计算出来的,它精确吗?
在这里插入图片描述

  • top 命令访问 /proc/stat 获取各项 cpu 利用率使用值
  • 内核调用 stat_open 函数来处理对 /proc/stat 的访问
  • 内核访问的数据来源于 kernel_cpustat 数组,并汇总
  • 打印输出给用户态

strace 跟踪 top 命令的系统调用

# strace top
...
openat(AT_FDCWD, "/proc/stat", O_RDONLY) = 4
openat(AT_FDCWD, "/proc/2351514/stat", O_RDONLY) = 8
openat(AT_FDCWD, "/proc/2393539/stat", O_RDONLY) = 8
...

除了 /proc/stat 外,还有各个进程细分的 /proc/{pid}/stat,是用来计算各个进程的 cpu 利用率时使用。

proc/stat伪文件

内核为各个伪文件都定义了处理函数,/proc/stat 文件的处理方法是 proc_stat_operations

//file:fs/proc/stat.c
static int __init proc_stat_init(void)
{proc_create("stat", 0, NULL, &proc_stat_operations);return 0;
}static const struct file_operations proc_stat_operations = {.open  = stat_open,...
};

proc_stat_operations 中包含了操作该文件时对应的操作方法,当打开 /proc/stat 文件时,stat_open 就会被调用
stat_open 依次调用 single_open_size->show_stat 来输出数据内容:

//file:fs/proc/stat.c
static int show_stat(struct seq_file *p, void *v)
{u64 user, nice, system, idle, iowait, irq, softirq, steal;for_each_possible_cpu(i) {struct kernel_cpustat *kcs = &kcpustat_cpu(i);user += kcs->cpustat[CPUTIME_USER];nice += kcs->cpustat[CPUTIME_NICE];system += kcs->cpustat[CPUTIME_SYSTEM];idle += get_idle_time(kcs, i);iowait += get_iowait_time(kcs, i);irq += kcs->cpustat[CPUTIME_IRQ];softirq += kcs->cpustat[CPUTIME_SOFTIRQ];...}//转换成节拍数并打印出来seq_put_decimal_ull(p, "cpu  ", nsec_to_clock_t(user));seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));seq_put_decimal_ull(p, " ", nsec_to_clock_t(system));seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle));seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait));seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq));seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq));...
}

for_each_possible_cpu 是在遍历存储着 cpu 使用率数据的 kcpustat_cpu 变量
该变量是一个 percpu 变量,它为每一个逻辑核都准备了一个数组元素
里面存储着当前核所对应各种事件,包括 user、nice、system、idel、iowait、irq、softirq 等
在这里插入图片描述
在这个循环中,将每一个核的每种使用率都加起来,最后通过 seq_put_decimal_ull 将这些数据输出

注意
在内核中实际每个时间记录的是纳秒数,但是在输出时统一转化成节拍单位
/proc/stat 的输出是从 kernel_cpustat 这个 percpu 变量中读取出来

统计数据怎么来的

内核是以采样的方式来统计 cpu 使用率,这个采样周期依赖的是 Linux 时间子系统中的定时器:
在这里插入图片描述
Linux 内核每隔固定周期会发出 timer interrupt (IRQ 0),这有点像乐谱中的节拍。
每隔一段时间,就打出一个拍子,Linux 响应并处理一些事情。

一个节拍的长度是多长时间,是通过 CONFIG_HZ 来定义。它定义的方式是每一秒有几次 timer interrupts。
不同的系统中这个节拍的大小可能不同,通常在 1 ms 到 10 ms 之间。
每次当时间中断到来时,调用 update_process_times 来更新系统时间,更新后的时间存储在 percpu 变量 kcpustat_cpu 中。
在这里插入图片描述
问题 2:ni 这一列是 nice,它输出的是 cpu 在处理啥时的开销?

问题 3:wa 代表的是 io wait,那么这段时间中 cpu 到底是忙碌还是空闲?

CPU的idle和iowait的区别

idle 状态:
表示 CPU 处于空闲状态,即没有任何任务正在被执行
这意味着 CPU 此时没有工作可做,可以随时接受新的任务分配
例如,当系统处于待机状态,用户没有进行操作,且后台也没有运行任务时,CPU 可能处于 idle 状态

iowait 状态:
指 CPU 等待 I/O 操作完成所花费的时间
当系统正在进行磁盘读写、网络通信等 I/O 操作时,如果这些操作还未完成,CPU 就会处于 iowait 状态
比如,当从硬盘读取大量数据或者向网络发送大量数据时,可能会导致 CPU 进入 iowait 状态

总结:
idle 是 CPU 真正的空闲,没有任务需要处理
iowait 是 CPU 因为等待 I/O 操作而暂时无法执行计算任务

过高的 idle 可能表示系统资源未被充分利用
过高的 iowait 可能暗示 I/O 子系统存在性能瓶颈,需要优化 I/O 设备或相关的操作流程
如果一个数据库服务器经常出现高 iowait,可能需要考虑升级存储设备、优化数据库的读写操作、增加缓存,来减少 I/O 操作次数

update_process_times 简介

普通定时器: arch_timer_handler_phys->tick_handle_periodic->update_process_times
高精度定时器: tick_sched_handle->update_process_times

linux 进程可以同时执行,是因为采用时间片轮转方案。
在这里插入图片描述
每个进程都会分得相应的时间片,当前进程时间片用完,CPU就会停止执行当前进程,选择其他合适进程。

什么时候判断当前进程的时间片是否用完?
依赖于系统timer,timer周期性产生时钟中断,在中断处理函数中,会更新当前进程时间等统计信息
并判断当前进程的时间片是否用完,是否需要切换到其他进程执行。
这个工作由update_process_times函数来实现。

(引入采用周期的概念,定时每 1 毫秒采样一次。如果采样的瞬时,cpu 在运行,就将这 1 ms 记录为使用,这时会得出一个瞬时的 cpu 使用率,把它都存起来。当统计 3 秒内的 cpu 使用率时,比如 t1 ~ t2 这段时间范围。那就把这段时间内所有瞬时值全加,取个平均值,统计相对准确,避免瞬时值剧烈震荡且粒度过粗问题了)
在这里插入图片描述
update_process_times函数不会做实际的进程切换动作,只会设置是否需要做进程切换的标记,真正的切换在schedule函数中实现。

linux每个时钟中断(又称tick中断)处理中都会更新进程时间,即update_process_times。

void update_process_times(int user_tick)
{struct task_struct *p=current;/* 找到多核中的cpu id */int cpu = smp_processor_id();/* // user_tick 根据 cpu 模式判断是用户态还是内核态// // linux统计时间的方式:// 1 基于整个cpu的统计// user_tick 表示 cpu 在用户态、内核态、中断状态// 此处把一个 tick 的时间累加到 kstat_cpu(i).cpustat.xx// /proc/stat的统计值是在此处统计的// 表示cpu在用户态、内核态中断中各占用多少时间// 对应 stat.c(fs/proc):static int __init proc_stat_init(void)// // 2 基于进程的统计// linux还有一种统计时间的方法更细化// 统计的是调度实体上的时间 sum_exec_runtime// 它在 sched_clock_cpu 函数中基于 timer 计算// /proc/pid/stat、/proc/pid/task/tid/stat 中的时间是在此处统计// 它统计了一个进程/线程占用 cpu 的时间,对应 do_task_stat 实现*/account_process_tick(p, user_tick);/*// 此处负责系统中的定时器到期操作,并未真正处理,只是实现 raise_softirq(TIMER_SOFTIRQ)// 当这个 tick 中断退出 irq_exit 时,会处理 TIMER_SOFTIRQ 软中断// TIMER_SOFTIRQ 软中断处理函数 run_timer_softirq() 负责处理到期的定时器*/run_locl_timers();/* 与进程和调度用过的时间参数 */scheduler_tick();
}

根据 Tick 产生是在用户态/内核态,以及idle进程的上下文等信息,选择不同函数进行处理
这里是把 TICK_NSEC ,也就是每 Tick 对应的 ns 加到对应的 cpustat 数组中
即 cpustat 数组中的数据,虽然是 ns 级,但精度是按照 Tick 的频率而定,不算准确

//file:kernel/sched/cputime.c
void account_process_tick(struct task_struct *p, int user_tick)
{cputime = TICK_NSEC;...if (user_tick)//3.1 统计用户态时间account_user_time(p, cputime);else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET))//3.2 统计内核态时间account_system_time(p, HARDIRQ_OFFSET, cputime);else//3.3 统计空闲时间account_idle_time(cputime);
}

首先设置 cputime = TICK_NSEC
一个 TICK_NSEC 的定义是一个节拍所占的纳秒数。
接下来根据判断结果分别执行 account_user_time、account_system_time 和 account_idle_time 来统计用户态、内核态和空闲时间

用户态时间统计

//file:kernel/sched/cputime.c
void account_user_time(struct task_struct *p, u64 cputime)
{//分两种种情况统计用户态 CPU 的使用情况int index;index = (task_nice(p) > 0) ? CPUTIME_NICE : CPUTIME_USER;//将时间累积到 /proc/stat 中task_group_account_field(p, index, cputime);......
}

account_user_time 函数主要分两种情况统计:

  • 如果进程的 nice 值大于 0,那么将会增加到 CPU 统计结构的 nice 字段中
  • 如果进程的 nice 值小于等于 0,那么增加到 CPU 统计结构的 user 字段中

其实用户态的时间不只是 user 字段,nice 也是
之所以要把 nice 分出来,是为了让 Linux 用户更一目了然地看到调过 nice 的进程所占的 cpu 周期有多少
如果想要观察系统的用户态消耗的时间,应将 top 中输出的 user + nice ,不是只看 user

接着调用 task_group_account_field 来把时间加到前面提到的 kernel_cpustat 内核变量中。

//file:kernel/sched/cputime.c
static inline void task_group_account_field(struct task_struct *p, int index, u64 tmp)
{__this_cpu_add(kernel_cpustat.cpustat[index], tmp);...
}

内核态时间统计

//file:kernel/sched/cputime.c
void account_system_time(struct task_struct *p, int hardirq_offset, u64 cputime)
{if (hardirq_count() - hardirq_offset)index = CPUTIME_IRQ;else if (in_serving_softirq())index = CPUTIME_SOFTIRQ;elseindex = CPUTIME_SYSTEM;account_system_index_time(p, cputime, index);
}

内核态的时间主要分 3 种情况进行统计

  • 如果当前处于硬中断执行上下文,那么统计到 irq 字段中
  • 如果当前处于软中断执行上下文,那么统计到 softirq 字段中
  • 否则统计到 system 字段中

判断好要加到哪个统计项中后,依次调用 account_system_index_time、task_group_account_field
来将这段时间加到内核变量 kernel_cpustat 中

空闲时间的累积

在内核变量 kernel_cpustat 中不仅仅是统计了各种用户态、内核态的使用统计,空闲也一并统计起来。
如果在采样的瞬间,cpu 既不在内核态也不在用户态的话,就将当前节拍的时间都累加到 idle 中。

//file:kernel/sched/cputime.c
void account_idle_time(u64 cputime)
{u64 *cpustat = kcpustat_this_cpu->cpustat;struct rq *rq = this_rq();if (atomic_read(&rq->nr_iowait) > 0)cpustat[CPUTIME_IOWAIT] += cputime;elsecpustat[CPUTIME_IDLE] += cputime;
}

在 cpu 空闲的情况下,进一步判断当前是不是在等待 IO(例如磁盘 IO):

  • 是的话,这段空闲时间会加到 iowait 中
  • 否则,加到 idle 中

iowait 其实是 cpu 的空闲时间,只不过是在等待 IO 完成而已
io wait 其实是 cpu 在空闲状态的一项统计,只不过这种状态和 idle 的区别是 cpu 是因为等待 io 而空闲

总结

实际查看一下服务器的 cpu 利用率的demo
https://github.com/yanfeizhang/coder-kung-fu/blob/main/tests/cpu/test06/cpu_stat.sh

在这里插入图片描述

TOP 中输出的 cpu 时间项目其实大致可以分为三类:
第一类:用户态消耗时间,包括 user 和 nice;如果想看用户态的消耗,要将 user 和 nice 加起来
第二类:内核态消耗时间,包括 irq、softirq 和 system
第三类:空闲时间,包括 io_wait 和 idle;其中 io_wait 也是 cpu 的空闲状态,只不过是在等 io 完成而已;如果只是想看 cpu 到底有多闲,应该把 io_wait 和 idle 加起来。

其他

当前程序使用的堆栈一个是内核堆栈,一个是用户态堆栈
普通的内核态可以视为是系统调用进入
是否在 hardirq 环境以及 softirq 环境,可以通过 preempt_count(thread_info 的一个成员)进行判断
idle状态,只需要判断当前进程是否是idle进程就可以
iowait :在 idle 进程上下文,但 rq->nr_iowait 仍大于 0 的情况

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/864976.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AcWing 1256:扩展二叉树

【题目来源】https://www.acwing.com/problem/content/1258/【题目描述】 由于先序、中序和后序序列中的任一个都不能唯一确定一棵二叉树,所以对二叉树做如下处理,将二叉树的空结点用 补齐,如图所示。 我们把这样处理后的二叉树称为原二叉树…

持续集成(Continuous Integration)

定义 持续集成(Continuous Integration,简称CI)是一种软件开发实践,开发者频繁地将代码集成到共享的代码库中,每次集成都通过自动化构建和测试来验证,从而尽早发现并修复错误。CI的目标是提高软件开发的质量…

[C++] 退出清理函数解读(exit、_exit、abort、atexit)

说明:在C中,exit、_exit(或_Exit)、abort和atexit是用于控制程序退出和清理的标准库函数。下面是对这些函数的详细解读: exit 函数原型:void exit(int status);作用:exit函数用于正常退出程序…

基于Java的早教系统的设计与实现【附源码】

摘要:随着家长对孩子教育的重视程度越来越高,早教也越来越受家长的青睐,因为它可以有针对性地单独授课,能显著提高学生学习的效果。同时互联网的兴起,对教育的形式也产生了重大影响,为此基于B/S的早教平台应…

零知识证明技术:隐私保护的利器

在当今信息时代,数据安全和隐私保护的重要性日益凸显。随着技术的发展,密码学在保障信息安全方面发挥着越来越重要的作用。其中,零知识证明技术作为一种新兴的密码学方法,为隐私保护提供了强有力的支持。本文将简要介绍零知识证明…

3.js - premultiplyAlpha

你瞅啥啊!!! 先看效果图吧 代码 // ts-nocheck // 引入three.js import * as THREE from three // 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls // 导入lil.gui import { GUI } from three/ex…

c#与倍福Plc通信

bcdedit /set hypervisorlaunchtype off

pycharm中新建的临时python文件存放在哪里?

在pycharm中建立的临时python文件,从哪里可以找到呢? 1.我们打开cmd窗口,进入根目录,用dos命令“dir scratch*.py/a/s”进行查找,发现这些临时文件存放在Roaming\JetBrains\PyCharmCE2022.2\scratches 的目录里面 2.…

base64字符串空格问题

客户端使用的Content-Type为application/x-www-form-urlencoded时,字符串中出现了空格,base64解码时出错了,因为原来的字符有号, Spring Boot 对于Content-Type为application/x-www-form-urlencoded的HTTP请求,默认情…

我全都要,全网聚合神器!绝了!

哈喽,各位小伙伴们好,我是给大家带来各类黑科技与前沿资讯的小武。 现在有不少开发者都会以“壳源”的方式(如TVBox、阅读APP等),为用户提供了更为灵活性的选择。 而今天给大家安利的是一款“壳源”的聚合神器&#…

Profibus DP主站转Modbus网关连接智能化电表通讯

Profibus DP主站转Modbus网关(XD-MDPBM20),是实现不同工业通信协议之间互联互通的设备,主要将Profibus DP协议转换为Modbus协议,实现数据的双向传输。通过Profibus DP主站转Modbus网关(XD-MDPBM20&#xff…

记一次阿里云服务器java应用无法响应且无法远程连接的问题排查

问题表现 java服务无响应,无法远程链接到服务器。 今天中午12点多,应用直接崩溃。后续进入到服务器,发现java进程都不在了, 排查过程 先安装atop工具 安装、配置并使用atop监控工具 等下次再出现时看相关时间点日志&#xff…

编译原理3-自底向上的语法分析

自底向上分析 ,就是自左至右扫描输入串,自底向上进 行分析;通过反复查找当前句型的 句柄, 并使 用产生式规则 将找到的句柄归约为相应的非终结符 。逐步进行“ 归约 ”,直到至文法的开始符号; 对于规范推导…

使用华为物联网平台API联机设备[C#灯带开发]

开发智能灯带涉及到物联网、嵌入式系统和应用软件的结合。下面我来为你提供一个简单的示例,展示如何通过华为物联网平台来控制智能灯带的开关和颜色。 示例:控制智能灯带 准备工作 注册华为云账号,并创建物联网平台实例。在华为物联网平台…

现代工作场所中的睡岗检测算法应用

在现代职场环境中,员工的工作状态直接影响到公司的整体效益。睡岗现象,即员工在工作时间内打瞌睡或睡觉,不仅降低了生产力,还可能带来安全隐患。因此,如何有效地检测和预防睡岗行为成为了企业管理中的一个重要课题。随…

小程序管理系统设计

小程序管理系统的设计是一个涉及多方面考量的复杂过程,旨在提高效率、优化用户体验、并确保数据安全。以下是一些关键组件和步骤,用于指导设计一个高效的小程序管理系统: 1. 需求分析 目标用户:明确小程序的目标用户群体&#xf…

试用笔记之-免费的汇通总账财务软件

首先下载免费汇通总账财务软件 http://www.htsoft.com.cn/download/htcaiwu.rar

不改代码,实现web.config或app.config的连接字符串加密解密

目的:加密字符串,防止明文显示。 好处:不用修改代码,微软自带功能,自动解密。 web.config 参考相关文章: Walkthrough: Encrypting Configuration Information Using Protected Configuration | Microso…

用MySQL+node+vue做一个学生信息管理系统(四):制作增加、删除、修改的组件和对应的路由

1.下载依赖: npm install vue-router 在src目录下新建一个文件夹router,在router文件夹下新建一个文件router.js文件,在component目录下新建增加删除和修改的组件,引入router.js当中 此时的init组件为主页面((二、三&…

Git、Github、tortoiseGit下载安装调试全套教程

一、Git 1.下载安装Git 编辑器可默认Vim,可换成别的,此处换成VScode,换成VScode或别的都需要单独下载和调用 (1)Git安装:https://www.cnblogs.com/xiuxingzhe/p/9300905.html (2&#xff09…