命令字:!ndx.loadavg
用法描述:
显示系统平均负载信息。相当于cat /proc/loadavg
开发过程:
1.找到关键全局变量
1.1avenrun
static int loadavg_proc_show(…):这是一个 proc 文件系统的回调函数,当用户读取/proc/loadavg时被调用
get_avenrun():从内核获取系统平均负载数据
get_avenrun()的定义中我们可以知道:函数内部访问了全局数组avenrun,取其中的数据做相应的计算,然后存入传入的数组loads[]
1.2 基地址:runqueues 偏移数组:_percpu_offset
这里通过函数nr_running()获取处于运行状态的进程总数
遍历所有在线(online)的 CPU,对于每个 CPU,通过cpu_rq(i)获取其对应runqueue结构,再获取runqueue->nr_running字段
这里的DECLARE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues)为系统中的每个cpu声明了一个变量,类型为结构体 rq。
而&per_cpu(runqueues, (cpu))用于获取每个cpu中的变量runqueues的地址。
通过基地址:runqueues 和偏移数组:_percpu_offset计算获取准确的地址。
1.3 线程数量:nr_threads
2.相关数据结构和思路:
avenrun数组,遍历取出数据仿照内核做相关计算即可。
runqueues: 结构体变量,类型为rq。
通过地址runqueues和偏移数组_percpu_offset中存放的偏移量。获取每个cpu中的这个结构变量runqueues的地址。
计算rq中的nr_running字段的偏移量。
然后计算具体字段的地址,获取具体字段的数据。
3.读取变量和相关偏移,输出信息
DECLARE_API(loadavg)
{
ULONG64 avenrun_addr = 0;
ULONG read;
GetExpressionEx("lk!avenrun", &avenrun_addr, NULL);
if (avenrun_addr == 0) {
dprintf("Failed to get avenrun address. Please fix symbol.\n");
return;
}
unsigned long long avenrun_buf[3];
unsigned long long loads[3];
ExtReadMemory(avenrun_addr, avenrun_buf, sizeof(avenrun_buf), &read);
if (read != sizeof(avenrun_buf)) {
dprintf("Failed to read avenrun from memory.\n");
return;
}
// 仿照 get_avenrun 处理
for (int i = 0; i < 3; i++) {
loads[i] = (avenrun_buf[i] + FIXED_1 / 200); // shift=0
}
// 获取 nr_running 信息
unsigned long nr_running = 0;
ULONG64 runqueues_addr = 0;
ULONG64 per_cpu_offset_addr = 0;
GetExpressionEx("lk!runqueues", &runqueues_addr, NULL);
GetExpressionEx("lk!__per_cpu_offset", &per_cpu_offset_addr, NULL);
if (runqueues_addr != 0 && per_cpu_offset_addr != 0) {
// 获取CPU数量
int nr_cpus = get_nr_cpus();
// 获取 rq 结构体中 nr_running 字段的偏移
ULONG nr_running_offset = 0;
GetFieldOffset("rq", "nr_running", &nr_running_offset);
// 遍历所有CPU的runqueues并累加nr_running
for (int cpu = 0; cpu < nr_cpus; cpu++) {
// 读取当前CPU的per_cpu_offset
ULONG64 cpu_offset = 0;
ExtReadMemory(per_cpu_offset_addr + (cpu * sizeof(ULONG64)), &cpu_offset, sizeof(cpu_offset), &read);
if (read == sizeof(cpu_offset)) {
// 计算当前CPU的runqueue地址: per_cpu_ptr(runqueues, cpu) = runqueues + __per_cpu_offset[cpu]
ULONG64 cpu_rq_addr = runqueues_addr + cpu_offset;
// 读取当前CPU的nr_running值
unsigned int cpu_nr_running = 0;
ExtReadMemory(cpu_rq_addr + nr_running_offset, &cpu_nr_running, sizeof(cpu_nr_running), &read);
if (read == sizeof(cpu_nr_running)) {
nr_running += cpu_nr_running;
}
}
}
}
// 获取 nr_threads 信息
ULONG64 nr_threads_addr = 0;
int nr_threads = 0;
GetExpressionEx("lk!nr_threads", &nr_threads_addr, NULL);
if (nr_threads_addr != 0) {
ExtReadMemory(nr_threads_addr, &nr_threads, sizeof(nr_threads), &read);
}
dprintf("%lu.%02lu %lu.%02lu %lu.%02lu %u/%d %d\n",
LOAD_INT(loads[0]), LOAD_FRAC(loads[0]),
LOAD_INT(loads[1]), LOAD_FRAC(loads[1]),
LOAD_INT(loads[2]), LOAD_FRAC(loads[2]),
nr_running, nr_threads);
}
3.实例测试
!ndx.loadavg
cat /proc/loadavg
作者:郭建程 创建时间:2025-09-08 09:43
最后编辑:郭建程 更新时间:2025-09-11 16:05
最后编辑:郭建程 更新时间:2025-09-11 16:05