命令字:!ndx.pglist
用法描述
用于查看 Linux 内核中物理内存页相关的 pglist_data 和 zone 结构体信息,可展示节点(node)、内存域(zone)的内存分布、管理参数等内容
支持的命令行参数:
-v:显示详细信息
开发过程
1. 找到关键全局变量
关键全局变量 | 作用 |
---|---|
contig_page_data | 内核中表示物理内存页管理的关键全局变量,是 pglist_data 类型的结构体实例,用于管理系统的物理内存页,包含内存节点、内存域等信息,是访问物理内存页管理数据的核心入口。 |
2. 相关数据结构
3. 读取变量和相关偏移,输出信息
#include "ndx.h"
#include <windows.h>
#include <minwindef.h>
#include <WDBGEXTS.H>
#include <cstdio>
#include <vector>
#include <string>
#include <string.h>
#include <cctype>
#include <cstdlib>
#include <cerrno>
#include <fstream>
#include <iostream>
#include "pglist.h"
// 构造函数
PglistAnalyzer::PglistAnalyzer() {
memset(&m_offsets, 0, sizeof(m_offsets));
memset(&m_zone_offsets, 0, sizeof(m_zone_offsets));
m_page_data_addr = 0;
m_initialized = FALSE;
m_zone_size = 0;
}
// 析构函数
PglistAnalyzer::~PglistAnalyzer() {
}
// 初始化 - 获取contig_page_data地址和字段偏移
BOOL PglistAnalyzer::Initialize() {
// 获取contig_page_data全局变量地址
if (!GetExpressionEx("lk!contig_page_data", &m_page_data_addr, NULL)) {
dprintf("ERROR: Failed to get address of contig_page_data\n");
return FALSE;
}
// 获取字段偏移
if (!GetFieldOffsets()) {
dprintf("ERROR: Failed to get pglist_data field offsets\n");
return FALSE;
}
// 获取zone结构体字段偏移
if (!GetZoneOffsets()) {
dprintf("ERROR: Failed to get zone field offsets\n");
return FALSE;
}
m_initialized = TRUE;
return TRUE;
}
// 获取pglist_data结构体字段偏移
BOOL PglistAnalyzer::GetFieldOffsets() {
// 初始化结构体
memset(&m_offsets, 0, sizeof(PGLIST_DATA_OFFSETS));
m_offsets.valid = TRUE;
// 获取各字段偏移
if (GetFieldOffset("pglist_data", "node_zones", &m_offsets.node_zones) != 0) {
dprintf("WARNING: Failed to get offset of node_zones\n");
m_offsets.valid = FALSE;
}
if (GetFieldOffset("pglist_data", "nr_zones", &m_offsets.nr_zones) != 0) {
dprintf("WARNING: Failed to get offset of nr_zones\n");
m_offsets.valid = FALSE;
}
if (GetFieldOffset("pglist_data", "node_id", &m_offsets.node_id) != 0) {
dprintf("WARNING: Failed to get offset of node_id\n");
m_offsets.valid = FALSE;
}
if (GetFieldOffset("pglist_data", "node_start_pfn", &m_offsets.node_start_pfn) != 0) {
dprintf("WARNING: Failed to get offset of node_start_pfn\n");
m_offsets.valid = FALSE;
}
if (GetFieldOffset("pglist_data", "node_present_pages", &m_offsets.node_present_pages) != 0) {
dprintf("WARNING: Failed to get offset of node_present_pages\n");
m_offsets.valid = FALSE;
}
if (GetFieldOffset("pglist_data", "node_spanned_pages", &m_offsets.node_spanned_pages) != 0) {
dprintf("WARNING: Failed to get offset of node_spanned_pages\n");
m_offsets.valid = FALSE;
}
if (GetFieldOffset("pglist_data", "kswapd", &m_offsets.kswapd) != 0) {
dprintf("WARNING: Failed to get offset of kswapd\n");
m_offsets.valid = FALSE;
}
if (GetFieldOffset("pglist_data", "kswapd_order", &m_offsets.kswapd_order) != 0) {
dprintf("WARNING: Failed to get offset of kswapd_order\n");
m_offsets.valid = FALSE;
}
if (GetFieldOffset("pglist_data", "kswapd_highest_zoneidx", &m_offsets.kswapd_highest_zoneidx) != 0) {
dprintf("WARNING: Failed to get offset of kswapd_highest_zoneidx\n");
m_offsets.valid = FALSE;
}
if (GetFieldOffset("pglist_data", "kswapd_failures", &m_offsets.kswapd_failures) != 0) {
dprintf("WARNING: Failed to get offset of kswapd_failures\n");
m_offsets.valid = FALSE;
}
if (GetFieldOffset("pglist_data", "totalreserve_pages", &m_offsets.totalreserve_pages) != 0) {
dprintf("WARNING: Failed to get offset of totalreserve_pages\n");
m_offsets.valid = FALSE;
}
if (GetFieldOffset("pglist_data", "flags", &m_offsets.flags) != 0) {
dprintf("WARNING: Failed to get offset of flags\n");
m_offsets.valid = FALSE;
}
return m_offsets.valid;
}
// 获取zone结构体字段偏移
BOOL PglistAnalyzer::GetZoneOffsets() {
// 初始化结构体
memset(&m_zone_offsets, 0, sizeof(ZONE_OFFSETS));
m_zone_offsets.valid = TRUE;
// 获取zone结构体大小
m_zone_size = GetTypeSize("zone");
if (m_zone_size == 0) {
dprintf("ERROR: Failed to get size of zone structure\n");
m_zone_offsets.valid = FALSE;
return FALSE;
}
// 获取free_area结构体大小
m_free_area_size = GetTypeSize("free_area");
if (m_free_area_size == 0) {
dprintf("ERROR: Failed to get size of free_area structure\n");
m_zone_offsets.valid = FALSE;
return FALSE;
}
// 获取watermark数组偏移
if (GetFieldOffset("zone", "_watermark", &m_zone_offsets.watermark[0]) != 0) {
dprintf("WARNING: Failed to get offset of watermark\n");
m_zone_offsets.valid = FALSE;
}
// 获取managed_pages字段偏移
if (GetFieldOffset("zone", "managed_pages", &m_zone_offsets.managed_pages) != 0) {
dprintf("WARNING: Failed to get offset of managed_pages\n");
m_zone_offsets.valid = FALSE;
}
// 获取spanned_pages字段偏移
if (GetFieldOffset("zone", "spanned_pages", &m_zone_offsets.spanned_pages) != 0) {
dprintf("WARNING: Failed to get offset of spanned_pages\n");
m_zone_offsets.valid = FALSE;
}
// 获取present_pages字段偏移
if (GetFieldOffset("zone", "present_pages", &m_zone_offsets.present_pages) != 0) {
dprintf("WARNING: Failed to get offset of present_pages\n");
m_zone_offsets.valid = FALSE;
}
// 获取flags字段偏移
if (GetFieldOffset("zone", "flags", &m_zone_offsets.flags) != 0) {
dprintf("WARNING: Failed to get offset of zone flags\n");
m_zone_offsets.valid = FALSE;
}
// 获取zone_start_pfn字段偏移
if (GetFieldOffset("zone", "zone_start_pfn", &m_zone_offsets.zone_start_pfn) != 0) {
dprintf("WARNING: Failed to get offset of zone_start_pfn\n");
m_zone_offsets.valid = FALSE;
}
// 获取watermark_boost字段偏移
if (GetFieldOffset("zone", "watermark_boost", &m_zone_offsets.watermark_boost) != 0) {
dprintf("WARNING: Failed to get offset of watermark_boost\n");
m_zone_offsets.valid = FALSE;
}
// 获取nr_reserved_highatomic字段偏移
if (GetFieldOffset("zone", "nr_reserved_highatomic", &m_zone_offsets.nr_reserved_highatomic) != 0) {
dprintf("WARNING: Failed to get offset of nr_reserved_highatomic\n");
m_zone_offsets.valid = FALSE;
}
// 获取lowmem_reserve数组偏移
if (GetFieldOffset("zone", "lowmem_reserve", &m_zone_offsets.lowmem_reserve[0]) != 0) {
dprintf("WARNING: Failed to get offset of lowmem_reserve\n");
m_zone_offsets.valid = FALSE;
}
// 获取cma_pages字段偏移
if (GetFieldOffset("zone", "cma_pages", &m_zone_offsets.cma_pages) != 0) {
dprintf("WARNING: Failed to get offset of cma_pages\n");
m_zone_offsets.valid = FALSE;
}
// 获取pageset_high字段偏移
if (GetFieldOffset("zone", "pageset_high", &m_zone_offsets.pageset_high) != 0) {
dprintf("WARNING: Failed to get offset of pageset_high\n");
m_zone_offsets.valid = FALSE;
}
// 获取pageset_batch字段偏移
if (GetFieldOffset("zone", "pageset_batch", &m_zone_offsets.pageset_batch) != 0) {
dprintf("WARNING: Failed to get offset of pageset_batch\n");
m_zone_offsets.valid = FALSE;
}
// 获取name字段偏移
if (GetFieldOffset("zone", "name", &m_zone_offsets.name) != 0) {
dprintf("WARNING: Failed to get offset of zone name\n");
m_zone_offsets.valid = FALSE;
}
// 获取free_area字段偏移
if (GetFieldOffset("zone", "free_area", &m_zone_offsets.free_area) != 0) {
dprintf("WARNING: Failed to get offset of free_area\n");
m_zone_offsets.valid = FALSE;
}
return m_zone_offsets.valid;
}
// 读取并打印pglist_data结构体字段值
void PglistAnalyzer::ReadAndPrintFields(BOOL detailed_mode) {
if (!m_offsets.valid) {
dprintf("ERROR: Invalid offsets structure\n");
return;
}
// === 基本信息 ===
// 读取node_id
ULONG64 node_id_addr = m_page_data_addr + m_offsets.node_id;
int node_id = 0;
if (!ExtReadMemory(node_id_addr, &node_id, sizeof(node_id), NULL)) {
dprintf(" node_id : ERROR - Failed to read\n");
}
// 读取node_start_pfn
ULONG64 node_start_pfn_addr = m_page_data_addr + m_offsets.node_start_pfn;
unsigned long node_start_pfn = 0;
if (!ExtReadMemory(node_start_pfn_addr, &node_start_pfn, sizeof(node_start_pfn), NULL)) {
dprintf(" node_start_pfn : ERROR - Failed to read\n");
}
// 读取node_present_pages
ULONG64 node_present_pages_addr = m_page_data_addr + m_offsets.node_present_pages;
unsigned long node_present_pages = 0;
if (!ExtReadMemory(node_present_pages_addr, &node_present_pages, sizeof(node_present_pages), NULL)) {
dprintf(" node_present_pages: ERROR - Failed to read\n");
}
// 读取node_spanned_pages
ULONG64 node_spanned_pages_addr = m_page_data_addr + m_offsets.node_spanned_pages;
unsigned long node_spanned_pages = 0;
if (!ExtReadMemory(node_spanned_pages_addr, &node_spanned_pages, sizeof(node_spanned_pages), NULL)) {
dprintf(" node_spanned_pages: ERROR - Failed to read\n");
}
// 打印节点信息
dprintf("Node[%d] start_pfn:%08lx present_pages:%8lu spanned_pages:%8lu\n",
node_id, node_start_pfn, node_present_pages, node_spanned_pages);
// 读取nr_zones(总是需要读取,用于zone数组遍历)
ULONG64 nr_zones_addr = m_page_data_addr + m_offsets.nr_zones;
int nr_zones = 0;
if (!ExtReadMemory(nr_zones_addr, &nr_zones, sizeof(nr_zones), NULL)) {
dprintf("ERROR: Failed to read nr_zones\n");
return;
}
// 详细模式下显示额外信息
if (detailed_mode) {
dprintf(" nr_zones : %d\n", nr_zones);
// 读取并打印totalreserve_pages
ULONG64 totalreserve_pages_addr = m_page_data_addr + m_offsets.totalreserve_pages;
unsigned long totalreserve_pages = 0;
if (ExtReadMemory(totalreserve_pages_addr, &totalreserve_pages, sizeof(totalreserve_pages), NULL)) {
dprintf(" totalreserve_pages: %lu\n", totalreserve_pages);
}
else {
dprintf(" totalreserve_pages: ERROR - Failed to read\n");
}
// === kswapd相关字段和flags ===
// 读取并打印kswapd_order
ULONG64 kswapd_order_addr = m_page_data_addr + m_offsets.kswapd_order;
int kswapd_order = 0;
if (ExtReadMemory(kswapd_order_addr, &kswapd_order, sizeof(kswapd_order), NULL)) {
dprintf(" kswapd_order : %d\n", kswapd_order);
}
else {
dprintf(" kswapd_order : ERROR - Failed to read\n");
}
// 读取并打印kswapd_highest_zoneidx
ULONG64 kswapd_highest_zoneidx_addr = m_page_data_addr + m_offsets.kswapd_highest_zoneidx;
int kswapd_highest_zoneidx = 0;
if (ExtReadMemory(kswapd_highest_zoneidx_addr, &kswapd_highest_zoneidx, sizeof(kswapd_highest_zoneidx), NULL)) {
dprintf(" kswapd_highest_zoneidx: %d\n", kswapd_highest_zoneidx);
}
else {
dprintf(" kswapd_highest_zoneidx: ERROR - Failed to read\n");
}
// 读取并打印kswapd_failures
ULONG64 kswapd_failures_addr = m_page_data_addr + m_offsets.kswapd_failures;
int kswapd_failures = 0;
if (ExtReadMemory(kswapd_failures_addr, &kswapd_failures, sizeof(kswapd_failures), NULL)) {
dprintf(" kswapd_failures : %d\n", kswapd_failures);
}
else {
dprintf(" kswapd_failures : ERROR - Failed to read\n");
}
// 读取并打印flags
ULONG64 flags_addr = m_page_data_addr + m_offsets.flags;
unsigned long flags = 0;
if (ExtReadMemory(flags_addr, &flags, sizeof(flags), NULL)) {
dprintf(" flags : 0x%lx\n", flags);
}
else {
dprintf(" flags : ERROR - Failed to read\n");
}
}
// 打印zone数组信息
ReadAndPrintZones(nr_zones, detailed_mode);
}
// 获取zone类型名称
const char* PglistAnalyzer::GetZoneTypeName(ULONG64 zone_addr) {
static char zone_name_buffer[64] = { 0 }; // 静态缓冲区存储zone名称
if (!m_zone_offsets.valid) {
return "ZONE_UNKNOWN";
}
// 读取name字段(这是一个指向字符串的指针)
ULONG64 name_ptr_addr = zone_addr + m_zone_offsets.name;
ULONG64 name_ptr = 0;
if (!ExtReadMemory(name_ptr_addr, &name_ptr, sizeof(name_ptr), NULL)) {
return "ZONE_READ_ERROR";
}
if (name_ptr == 0) {
return "ZONE_NULL_NAME";
}
// 读取实际的字符串内容
memset(zone_name_buffer, 0, sizeof(zone_name_buffer));
if (!ExtReadMemory(name_ptr, zone_name_buffer, sizeof(zone_name_buffer) - 1, NULL)) {
return "ZONE_STRING_ERROR";
}
// 确保字符串以null结尾
zone_name_buffer[sizeof(zone_name_buffer) - 1] = '\0';
return zone_name_buffer;
}
// 读取并打印zone数组信息
void PglistAnalyzer::ReadAndPrintZones(int nr_zones, BOOL detailed_mode) {
if (!m_zone_offsets.valid) {
dprintf("ERROR: Invalid zone offsets structure\n");
return;
}
// 计算zone数组的起始地址
ULONG64 zones_array_addr = m_page_data_addr + m_offsets.node_zones;
for (int i = 0; i < nr_zones; i++) {
// 计算当前zone的地址
ULONG64 current_zone_addr = zones_array_addr + (i * m_zone_size);
// 读取zone_start_pfn
ULONG64 zone_start_pfn_addr = current_zone_addr + m_zone_offsets.zone_start_pfn;
ULONG64 zone_start_pfn = 0;
ExtReadMemory(zone_start_pfn_addr, &zone_start_pfn, 8, NULL);
// 读取present_pages
ULONG64 present_pages_addr = current_zone_addr + m_zone_offsets.present_pages;
ULONG64 present_pages = 0;
ExtReadMemory(present_pages_addr, &present_pages, 8, NULL);
// 读取spanned_pages
ULONG64 spanned_pages_addr = current_zone_addr + m_zone_offsets.spanned_pages;
ULONG64 spanned_pages = 0;
ExtReadMemory(spanned_pages_addr, &spanned_pages, 8, NULL);
dprintf(" Zone[%-6s] start_pfn:%08I64x present_pages:%8I64u spanned_pages:%8I64u\n",
GetZoneTypeName(current_zone_addr), zone_start_pfn, present_pages, spanned_pages);
// 详细模式下显示Zone详细信息
if (detailed_mode) {
// === 基本信息 ===
// 读取flags
ULONG64 zone_flags_addr = current_zone_addr + m_zone_offsets.flags;
ULONG64 zone_flags = 0;
if (ExtReadMemory(zone_flags_addr, &zone_flags, 8, NULL)) {
dprintf(" flags : 0x%I64x\n", zone_flags);
}
else {
dprintf(" flags : ERROR - Failed to read\n");
}
// 读取managed_pages
ULONG64 managed_pages_addr = current_zone_addr + m_zone_offsets.managed_pages;
ULONG64 managed_pages = 0;
if (ExtReadMemory(managed_pages_addr, &managed_pages, 8, NULL)) {
dprintf(" managed_pages : %I64u\n", managed_pages);
}
else {
dprintf(" managed_pages : ERROR - Failed to read\n");
}
// 读取cma_pages
ULONG64 cma_pages_addr = current_zone_addr + m_zone_offsets.cma_pages;
ULONG64 cma_pages = 0;
if (ExtReadMemory(cma_pages_addr, &cma_pages, 8, NULL)) {
dprintf(" cma_pages : %I64u\n", cma_pages);
}
else {
dprintf(" cma_pages : ERROR - Failed to read\n");
}
// === 水位线和保留信息 ===
// 注意:Linux内核中unsigned long是8字节,而Windows环境中是4字节
ULONG64 watermarks[3] = { 0 };
BOOL watermark_success = TRUE;
for (int j = 0; j < 3; j++) {
ULONG64 watermark_addr = current_zone_addr + m_zone_offsets.watermark[0] + (j * 8); // 使用8字节偏移
if (!ExtReadMemory(watermark_addr, &watermarks[j], 8, NULL)) { // 读取8字节
watermark_success = FALSE;
break;
}
}
if (watermark_success) {
dprintf(" watermark[MIN/LOW/HIGH]: %I64u/%I64u/%I64u\n",
watermarks[0], watermarks[1], watermarks[2]);
}
else {
dprintf(" watermark[MIN/LOW/HIGH]: ERROR - Failed to read\n");
}
// 读取watermark_boost
ULONG64 watermark_boost_addr = current_zone_addr + m_zone_offsets.watermark_boost;
ULONG64 watermark_boost = 0;
if (ExtReadMemory(watermark_boost_addr, &watermark_boost, 8, NULL)) {
dprintf(" watermark_boost: %I64u\n", watermark_boost);
}
else {
dprintf(" watermark_boost: ERROR - Failed to read\n");
}
// 读取nr_reserved_highatomic
ULONG64 nr_reserved_highatomic_addr = current_zone_addr + m_zone_offsets.nr_reserved_highatomic;
ULONG64 nr_reserved_highatomic = 0;
if (ExtReadMemory(nr_reserved_highatomic_addr, &nr_reserved_highatomic, 8, NULL)) {
dprintf(" nr_reserved_highatomic: %I64u\n", nr_reserved_highatomic);
}
else {
dprintf(" nr_reserved_highatomic: ERROR - Failed to read\n");
}
// 读取lowmem_reserve数组
ULONG64 lowmem_reserve[4] = { 0 };
BOOL lowmem_reserve_success = TRUE;
for (int j = 0; j < 4; j++) {
ULONG64 lowmem_reserve_addr = current_zone_addr + m_zone_offsets.lowmem_reserve[0] + (j * 8);
if (!ExtReadMemory(lowmem_reserve_addr, &lowmem_reserve[j], 8, NULL)) {
lowmem_reserve_success = FALSE;
break;
}
}
if (lowmem_reserve_success) {
dprintf(" lowmem_reserve : [%I64u, %I64u, %I64u, %I64u]\n",
lowmem_reserve[0], lowmem_reserve[1], lowmem_reserve[2], lowmem_reserve[3]);
}
else {
dprintf(" lowmem_reserve : ERROR - Failed to read\n");
}
// === 配置参数 ===
// 读取pageset_high
ULONG64 pageset_high_addr = current_zone_addr + m_zone_offsets.pageset_high;
ULONG pageset_high = 0;
if (ExtReadMemory(pageset_high_addr, &pageset_high, 4, NULL)) {
dprintf(" pageset_high : %u\n", pageset_high);
}
else {
dprintf(" pageset_high : ERROR - Failed to read\n");
}
// 读取pageset_batch
ULONG64 pageset_batch_addr = current_zone_addr + m_zone_offsets.pageset_batch;
ULONG pageset_batch = 0;
if (ExtReadMemory(pageset_batch_addr, &pageset_batch, 4, NULL)) {
dprintf(" pageset_batch : %u\n", pageset_batch);
}
else {
dprintf(" pageset_batch : ERROR - Failed to read\n");
}
// === free_area数组信息 ===
ULONG64 free_area_base_addr = current_zone_addr + m_zone_offsets.free_area;
dprintf(" free_area (free page frames count):\n");
for (int order = 0; order < 11; order++) { // MAX_ORDER通常为11
// 计算当前order的free_area结构体地址
ULONG64 current_free_area_addr = free_area_base_addr + (order * m_free_area_size);
// 获取nr_free字段偏移(通常在free_list数组之后)
ULONG nr_free_offset = 0;
if (GetFieldOffset("free_area", "nr_free", &nr_free_offset) == 0) {
ULONG64 nr_free_addr = current_free_area_addr + nr_free_offset;
unsigned long nr_free = 0;
if (ExtReadMemory(nr_free_addr, &nr_free, sizeof(nr_free), NULL)) {
dprintf(" order[%2d] : %lu free pages\n", order, nr_free);
}
else {
dprintf(" order[%2d] : ERROR - Failed to read\n", order);
}
}
else {
dprintf(" order[%2d] : ERROR - Failed to get nr_free offset\n", order);
}
}
}
}
}
DECLARE_API(pglist) {
// 解析命令行参数
BOOL detailed_mode = FALSE;
// 检查是否有-v参数或帮助命令
if (args && strlen(args) > 0) {
// 检查帮助命令
if (strcmp(args, "-h") == 0 || strcmp(args, "--help") == 0 || strcmp(args, "?") == 0) {
dprintf("Linux Kernel Page List Commands:\n");
dprintf(" !ndx.pglist - Display basic page list information\n");
dprintf(" !ndx.pglist -v - Display detailed page list information\n");
dprintf(" !ndx.pglist -h, --help, ? - Show this help information\n");
return;
}
// 简单的参数解析,查找-v
if (strstr(args, "-v") != NULL) {
detailed_mode = TRUE;
}
}
// 创建PglistAnalyzer对象
PglistAnalyzer analyzer;
// 初始化分析器
if (!analyzer.Initialize()) {
dprintf("ERROR: Failed to initialize PglistAnalyzer\n");
return;
}
analyzer.ReadAndPrintFields(detailed_mode);
}
4. 实例测试
!ndx.pglist
!ndx.pglist -v
作者:郭建程 创建时间:2025-09-22 13:48
最后编辑:郭建程 更新时间:2025-09-22 21:32
最后编辑:郭建程 更新时间:2025-09-22 21:32