惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Heimdal Security Blog
A
Arctic Wolf
K
Kaspersky official blog
V
Vulnerabilities – Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Simon Willison's Weblog
Simon Willison's Weblog
L
LINUX DO - 热门话题
MongoDB | Blog
MongoDB | Blog
T
Threat Research - Cisco Blogs
D
Docker
爱范儿
爱范儿
T
Tenable Blog
C
Check Point Blog
B
Blog
C
Cisco Blogs
Vercel News
Vercel News
The Cloudflare Blog
T
Threatpost
NISL@THU
NISL@THU
T
Tor Project blog
V2EX - 技术
V2EX - 技术
P
Palo Alto Networks Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tailwind CSS Blog
G
GRAHAM CLULEY
P
Privacy & Cybersecurity Law Blog
SecWiki News
SecWiki News
博客园 - 司徒正美
S
Security @ Cisco Blogs
GbyAI
GbyAI
S
Secure Thoughts
Microsoft Security Blog
Microsoft Security Blog
The Register - Security
The Register - Security
Recorded Future
Recorded Future
Cloudbric
Cloudbric
Webroot Blog
Webroot Blog
N
News and Events Feed by Topic
Y
Y Combinator Blog
博客园_首页
T
Troy Hunt's Blog
The Hacker News
The Hacker News
雷峰网
雷峰网
Google DeepMind News
Google DeepMind News
U
Unit 42
AWS News Blog
AWS News Blog
PCI Perspectives
PCI Perspectives
V
Visual Studio Blog
博客园 - 聂微东
有赞技术团队
有赞技术团队
酷 壳 – CoolShell
酷 壳 – CoolShell

陈陈菌博客

如何在 podman 容器内使用 systemd 在 podman 运行 GUI 应用的经验总结 管理 podman 容器与镜像的导入或导出 用 xdg-dbus-proxy 解决容器的文件选择器的问题 解决 VSCodium 或 Code-Server 无法安装 GitHub Copilot 问题 不可变发行版 Fedora Silverblue 的体验记录 解决 apt 或 dnf 无法 tab 补全的问题 对 (MPA) 多页面切换实现丝滑动画的探索 让服务在非 Root 权限下使用 80 或 443 端口 AppImage 应用打包快速上手 Flatpak 使用和应用打包快速上手 如何用 webSocket 实现一个实时聊天应用 Linux 内核编译 | 快速上手 | deb 使用 debootstrap 构建 Debian 系根文件系统 高度可定制的 KDE 窗口装饰器 klassy 有关 docker、podman、nspawn、bwrap 的 Linux 容器图形性能测试 创建任意 Linux 容器并支持 GUI 程序 (Docker) 创建任意 Linux 容器并支持 GUI 程序 (nspawn) 文件同步工具 rsync 的使用 玲珑 (Linyaps) 应用打包 RPM 打包指南 | 精简版 PBR 材质的各种贴图类型入门 APU | Stable-Diffusion 使用记录 如何在 Electron 上启用 WebGPU Grub 主题安装与配置 Fcitx5 主题安装与自定义 不使用 Nginx 的网站开发指南 SQL 常用 CRUD 语法记录 Debian 打包入门,与需要注意的问题 在 fedora 建立一个 Arch 容器 (bwrap) 在 fedora 上建立一个 debian 容器 (bwrap) i3wm | 自制一个极简的屏幕亮度调节器 在 Linux 下启用内存压缩技术 zram 搭建一个本地轻量级聊天 Ai 我又又又重新上线了博客 如何通过命令行管理 wine 容器 (包括 proton) Btrfs 文件系统创建 swap 文件的方法 如何创建适用于 Linux 桌面的程序启动文件 在 Linux 控制 intel CPU 调度的方法 Arch Linux 的基本安装
如何自制一个简易的资源监控程序
GlumiStudio@ · 2024-12-29 · via 陈陈菌博客

如何自制一个简易的资源监控程序

2024-12-29 00:09:39

分类: Linux 标签: 系统运维,Bun


前段时间有网友留言好奇我这个博客下的资源监控是怎么实现的,今天就和大家讲一下实现思路。

这里博主使用 JS / TS(Bun)作为演示。当然,你也可以选择自己喜欢的编程语言来实现,但本质上是通用的。

首先科普一下 Linux 下的 /proc/ 这个目录,这里存放着很多内核级的接口文件,有些是可读或可写的(或需要管理员权限)。

下面是几种常见的:

文件信息
/proc/cpuinfo这里可以获取到 CPU 型号,频率等信息。
/proc/meminfo可以获取到内存有关的详细信息。
/proc/net/dev可以获取到网卡的上下行流量,数据包。
/proc/stat可以获取到 CPU 工作状态。

内存总量


为了获取内存总量,我们可以直接读取 /proc/meminfo 文件,过滤文本并找到 MemTotal 字段。

cat /proc/meminfo | grep 'MemTotal'

我们会获得这样的信息

MemTotal:        3878108 kB

如果直接获取到阈值,可以借助 awk 来辅助:

cat /proc/meminfo | grep 'MemTotal' | awk '{print $2}'

即可得到

3878108

这里的阈值 / 1024 即可获得 MB 单位。我这里是 4G内存的服务器,实际可用为 3787 MB,如果想获得 GB 或更高单位,直接以倍数计算即可。

内存剩余量


如果想要获取内存余量,和上面的思路一致,但我们只需要获取 MemFree 字段,以及 Cached 字段即可得出。

可能有疑问,为什么还要获取 Cached 呢?这是因为 MemFree 不包含缓存,只计算了未使用的内存空间。

一般情况下,剩余内存 = 空闲内存 + 缓存 + Buffers

(而其中 Buffers 基本很少,也可以忽略不计)

CPU 使用率


为了得到 CPU 使用率,首先读取 /proc/stat 文件,过滤出其中每个核心的 阈值文本。

使用 shell 命令: cat /proc/stat 会得到类似以下信息:

cpu  185061 18 194351 32867149 40905 0 5423 0 0 0
cpu0 93003 10 97432 16434349 20930 0 2432 0 0 0
cpu1 92057 8 96918 16432799 19975 0 2991 0 0 0
.....还有很多无关内容

其中咱们只需要关注和 cpu 有关的字段,其中第一个 cpu 字段代表所有核心的总量

对应的阈值是:

usernicesystemidleiowaitirqsoftirqsteal
cpu18506118194351328671494090505423
cpu0930031097432164343492093002432
cpu192057896918164327991997502991

CPU 后面带数字的是不同的核心,我这里是双核服务器所以是从 cpu0 到 cpu1.

为了确保算出CPU使用率,我们需要一个时间差来计算 CPU 状态,假如设定为 1 秒

计算公式:

总时间:每个核心的 user + nice + system + idle + irq 的和
总空闲时间:每个核心的 idle 的和
总时间差 = 1秒后CPU总时间1秒前CPU总时间
总空闲时间差 = 1秒后总空闲时间差1秒前总空闲时间差
CPU 使用率 = (总时间差 - 总空闲时间差)总时间差 乘 100

这里我为了代码简短,就不读文件过滤文本了,使用 os 模块可直接获取所有 cpu 信息

// 引入 os 模块来获取 cpus 
import os from 'os'

// 这里我们创建一个获取CPU状态的函数
function getCPUinfo(){
    // 初始一个临时模板对象
    let _cpu = { total:0,user:0,nice:0,sys:0,idle:0,irq:0 }
    // 遍历每个 cpu 状态,并递增阈值
    for(const cpu of os.cpus()){
        _cpu.user += cpu.times.user;
        _cpu.nice += cpu.times.nice;
        _cpu.sys += cpu.times.sys;
        _cpu.idle += cpu.times.idle;
        _cpu.irq += cpu.times.irq;
    }
    // 设置总时间
    _cpu.total = _cpu.user + _cpu.nice + _cpu.sys + _cpu.idle + _cpu.irq
    // 最终返回全部 cpu 核心递增的信息
    return {
        total: _cpu.total,
        user: _cpu.user,
        nice: _cpu.nice,
        sys: _cpu.sys,
        idle: _cpu.idle,
        irq: _cpu.irq
    }
}

以上定义了基本获取CPU信息的函数,接下来我们再通过定时任务来计算时间差的结果

// 获取初始 cpu 状态
let old_cpu_info = getCPUinfo()
// 1秒后执行
setTimeout(()=>{
    // 获取1秒后 cpu 状态
    let new_cpu_info = getCPUinfo()
    // 计算这1秒的总时间差
    let total = new_cpu_info.total - old_cpu_info.total
    // 计算这1秒的空闲时间差
    let idle = new_cpu_info.idle - old_cpu_info.idle
    // 计算这1秒的CPU使用率
    let usage = (total - idle) / total * 100
    // 精确单位到 2 位小数
    console.log(`${usage.toFixed(2)} %`)
},1000)
watch bun run ./test.ts

咱们为了让这个脚本一直执行,可以使用 watch 命令来观察(它本身刚好是每1秒执行一次)。

网络上下行速率


读取 /proc/net/dev 文件,咱们会得到类似以下信息

Inter-|   Receive                                                |  Transmit
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
    lo:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0
  eth0: 507213616 1580643    0    0    0     0          0         0 1201777570 1852460    0    0    0     0       0          0

其中我的网卡为 eth0 ,这个数据中 Receive 和 Transmit,代表者上行和下行流量。其中 bytes 代表总共多少字节单位数据。(它们会在重启系统后重新开始计数)而 packets 为数据包。我们重点关注 eth0,整成表格后为以下信息。

网络接口下载字节下载数据包上传字节上传数据包
eth0507213616158064312017775701852460

我们通过以下 shell 命令可以直接获取到 上行和下行的 bytes(其中 eth0 请替换成自己实际的网卡名称)

cat /proc/net/dev | sed -n '/eth0/p' | awk '{print $2, $10}

会得到该数据(左为下行,右为上行)

507213616 1201777570

接下来我们写一个脚本,也是类似上面计算CPU占用率一样,通过时间差来计算网卡速率。

计算公式:

每秒速率 = 1秒后的 bytes1秒前的 bytes
如果要得到 KB/s 单位,可除于 1024,以此类推。

let res = await Bun.$ `cat /proc/net/dev | sed -n '/eth0/p' | awk '{print $2, $10}'`.text()
let contextSplit = res.split(' ')
let downloadBytes = contextSplit[0]
let uploadBytes = contextSplit[1].trim()
setTimeout(async ()=>{
    let _res = await Bun.$ `cat /proc/net/dev | sed -n '/eth0/p' | awk '{print $2, $10}'`.text()
    let _contextSplit = _res.split(' ')
    // 获取1秒后下载的字节数
    let _downloadBytes = _contextSplit[0]
    // 获取1秒后上传的字节数
    let _uploadBytes = _contextSplit[1].trim()
    // 得出的下载的速率
    let download = ((Number(_downloadBytes) - Number(downloadBytes)) / 1024).toFixed(1)
    // 得出的上传的速率
    let upload = ((Number(_uploadBytes) - Number(uploadBytes)) / 1024).toFixed(1)
    console.log(download + ' KB/s')
    console.log(upload + ' KB/s')
},1000)

现在我们得到了(总内存量,CPU使用率,内存剩余量,以及缓存,网络速率)数据的获取和计算方式。接下来如何应用到其他方面,我知道你懂得 ^_^