前段时间有网友留言好奇我这个博客下的资源监控是怎么实现的,今天就和大家讲一下实现思路。
这里博主使用 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 使用率,首先读取 /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 字段代表所有核心的总量
对应的阈值是:
user | nice | system | idle | iowait | irq | softirq | steal |
---|---|---|---|---|---|---|---|
cpu | 185061 | 18 | 194351 | 32867149 | 40905 | 0 | 5423 |
cpu0 | 93003 | 10 | 97432 | 16434349 | 20930 | 0 | 2432 |
cpu1 | 92057 | 8 | 96918 | 16432799 | 19975 | 0 | 2991 |
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,整成表格后为以下信息。
网络接口 | 下载字节 | 下载数据包 | 上传字节 | 上传数据包 |
---|---|---|---|---|
eth0 | 507213616 | 1580643 | 1201777570 | 1852460 |
我们通过以下 shell 命令可以直接获取到 上行和下行的 bytes(其中 eth0 请替换成自己实际的网卡名称)
cat /proc/net/dev | sed -n '/eth0/p' | awk '{print $2, $10}
会得到该数据(左为下行,右为上行)
507213616 1201777570
接下来我们写一个脚本,也是类似上面计算CPU占用率一样,通过时间差来计算网卡速率。
每秒速率 = 1秒后的 bytes
减 1秒前的 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使用率,内存剩余量,以及缓存,网络速率)数据的获取和计算方式。接下来如何应用到其他方面,我知道你懂得 ^_^