Linux 内核笔记
七月 18, 2025 [linux] #kernel #memory #driver #oom #debugLinux 内核笔记
物理内存布局
在典型 RISC-V/ARM 主板中,物理地址空间分配如下:
0x0000_0000— 保留0x0200_0000— CLINT(中断控制器)0x1000_0000— Boot ROM0x1009_0000— 以太网控制器0x8000_0000— DRAM 起始地址
高于 0x8000_0000 的地址访问 DRAM,低于的地址访问 MMIO(内存映射 I/O)设备。主板设计者决定物理地址的路由规则。
可通过 cat /proc/iomem 查看物理内存分布,System RAM 项代表系统可用内存。
预留系统内存
方法一:内核启动参数
在 grub 启动参数中添加:
memmap=4m\$0x10DBFFFFF
表示从 0x10DBFFFFF 起预留 4MB。重启后通过 cat /proc/iomem 验证是否标记为 reserved。
方法二:memblock
在 setup_arch() 中调用 memblock_reserve():
memblock_reserve(0x100000000, 0x2000000); // 预留 32MB
预留内存可通过 memremap() 或 phy_to_vir(仅限 normal 区)映射访问。
方法三:限制总内存 mem=
在 grub 中添加 mem=2G 限制可用内存,超出部分即被隐藏。隐藏内存只能通过 memremap() 访问,phy_to_vir 不可用。
方法四:CMA
CMA(Contiguous Memory Allocation)动态分配大块连续物理内存,可用于 DMA 等场景,需修改内核配置或设备树。
访问预留内存
// 申请资源
request_mem_region(0x40000000, 0x20000000, "reserve test");
// 映射内存
void* addr = memremap(0x40000000, 0x20000000, MEMREMAP_WB);
// 使用完后释放
iounmap(addr);
release_mem_region(0x40000000, 0x20000000);
earlyoom
当系统内存不足时,earlyoom 在 OOM Killer 之前主动杀掉高内存消耗进程,避免系统卡顿。
apt install earlyoom
systemctl enable --now earlyoom
默认行为:内存和 swap 同时低于 10% 时杀 oom_score 最高的进程;低于 5% 时发 SIGKILL。
配置 /etc/default/earlyoom:
EARLYOOM_ARGS="-r 60 -m 1 -s 5 --avoid '(^|/)(init|X|sshd)$'"
-r 60:每 60 秒打印一次统计-m 1:物理内存保留 1%-s 5:swap 保留 5%--avoid:永不杀匹配正则的进程--prefer:优先杀匹配正则的进程
查看历史记录:
journalctl -u earlyoom | grep -iE "(sending|killing)"
内核内存泄漏排查
思路:
- 监控用户进程 PSS(
pmap <pid>) - 监控
/proc/meminfo,重点关注 Slab 使用量 - 使用
slabtop查看 slab 分配,关注持续增长的 size 类别 - 使用
trace-cmd跟踪kmalloc/kfree:
trace-cmd record -e kmalloc -f 'bytes_alloc==4096' -e kfree -T
trace-cmd report
对比 kmalloc 和 kfree 的 ptr 地址,匹配不上的即为泄漏。
- 手动触发 vmcore 用 crash 工具分析:
echo c > /proc/sysrq-trigger
crash ./vmcore ./vmlinux.debug
Linux 总线驱动模型
Linux 设备驱动模型以**总线(bus)为核心,管理设备(device)和驱动(driver)**的匹配。
总线注册
static struct bus_type xbus = {
.name = "xbus",
.match = xbus_match, // 设备和驱动匹配回调
};
// 模块加载时注册
bus_register(&xbus);
// 导出属性文件到 /sys
bus_create_file(&xbus, &bus_attr_xbus_test);
设备注册
static struct device xdev = {
.init_name = "xdev",
.bus = &xbus,
.release = xdev_release,
};
device_register(&xdev);
// 导出设备属性
DEVICE_ATTR(xdev_id, S_IRUSR|S_IWUSR, xdev_id_show, xdev_id_store);
device_create_file(&xdev, &dev_attr_xdev_id);
驱动注册
static struct device_driver xdrv = {
.name = "xdev", // 与设备名一致才能匹配
.bus = &xbus,
.probe = xdrv_probe,
.remove = xdrv_remove,
};
driver_register(&xdrv);
匹配成功后自动调用驱动的 probe 函数。
属性文件导出宏
| 宏 | 用途 |
|---|---|
BUS_ATTR | 总线级别属性 |
DEVICE_ATTR | 设备级别属性 |
DRIVER_ATTR_RO / DRIVER_ATTR_WO / DRIVER_ATTR_RW | 驱动级别属性 |
属性文件出现在 /sys/bus/<bus_name>/ 对应目录下,用户可通过 cat/echo 读写。