struct resource是 Linux 内核中用于描述和统一管理各种系统资源(如I/O端口、内存区域、IRQ号、DMA通道等)的核心数据结构。
它的定义位于include/linux/ioport.h文件中,其结构体定义如下:
struct resource { resource_size_t start; /* 资源的起始地址 */ resource_size_t end; /* 资源的结束地址 */ const char *name; /* 资源的名称 */ unsigned long flags; /* 资源的类型和属性标志 */ struct resource *parent, *sibling, *child; /* 指向资源树中的父、兄弟、子节点 */ };字段含义详解
| 字段 | 类型 | 描述 |
|---|---|---|
start | resource_size_t | 资源的起始地址(对于I/O端口、内存等地址空间资源)或编号(对于IRQ、DMA等)。 |
end | resource_size_t | 资源的结束地址。start和end共同定义了资源的地址范围。 |
name | const char * | 资源的描述性名称,通常会被导出到用户空间,例如你在/proc/iomem或/proc/ioports中看到的名称。 |
flags | unsigned long | 用于标识资源的类型(如IORESOURCE_MEM、IORESOURCE_IO)和属性(如IORESOURCE_BUSY、IORESOURCE_READONLY)的位掩码。这是资源管理中最关键的字段之一。 |
parent | struct resource * | 指向资源树中该节点的父节点。资源组织成一棵树形结构。 |
sibling | struct resource * | 指向同一父节点下的兄弟资源节点的链表指针。 |
child | struct resource * | 指向该资源节点的第一个子节点。 |
资源树结构
内核将所有的同类型资源(如所有I/O内存)组织成一棵资源树。其中,parent、sibling和child这三个字段正是为了构建这棵树:
每个资源都有一个父节点(
parent),根节点通常是全局的,如iomem_resource。子节点之间通过
sibling指针链接成一个链表。每个父节点通过
child指针指向它的第一个子节点。
这个树形结构有效地管理了资源的层次关系和冲突检测。
flags常用标志说明
flags字段定义了资源的类型和属性,常见的标志位包括:
| 标志 | 宏定义 | 含义 |
|---|---|---|
IORESOURCE_IO | 0x00000100 | I/O端口空间。 |
IORESOURCE_MEM | 0x00000200 | 内存地址空间。 |
IORESOURCE_IRQ | 0x00000400 | 中断号(IRQ)。 |
IORESOURCE_DMA | 0x00000800 | DMA通道。 |
IORESOURCE_BUSY | 0x80000000 | 该资源已被驱动程序占用。 |
IORESOURCE_READONLY | 0x00004000 | 资源为只读。 |
在code_resource、data_resource和bss_resource正是通过struct resource来精确描述内核自身各段在物理内存中的地址范围,并通过flags标识类型为IORESOURCE_MEM,然后被添加到全局的iomem_resource资源树中,最终通过/proc/iomem文件呈现给用户。
code_resource.start = __pa_symbol(_text);
code_resource.end = __pa_symbol(_etext)-1;
data_resource.start = __pa_symbol(_etext);
data_resource.end = __pa_symbol(_edata)-1;
bss_resource.start = __pa_symbol(__bss_start);
bss_resource.end = __pa_symbol(__bss_stop)-1;
这段代码的作用是为内核的代码段(.text)、数据段(.data)和BSS段(.bss)分别建立物理地址范围的资源描述符。它通过__pa_symbol宏将内核各段符号的虚拟地址转换为物理地址,并填充到对应的struct resource结构体中。
这段代码位于arch/x86/kernel/setup.c的setup_arch函数中,执行时机非常早,是内核初始化自身内存布局的关键一步。
代码解析
code_resource:记录内核代码段的物理地址范围。start = __pa_symbol(_text):起始地址是内核代码段的开始。end = __pa_symbol(_etext)-1:结束地址是代码段结束符号的前一个字节。
data_resource:记录内核数据段的物理地址范围。start = __pa_symbol(_etext):起始地址紧跟在代码段之后。end = __pa_symbol(_edata)-1:结束地址是数据段结束符号的前一个字节。
bss_resource:记录内核BSS段(未初始化全局变量)的物理地址范围。start = __pa_symbol(__bss_start):起始地址是BSS段的开始。end = __pa_symbol(__bss_stop)-1:结束地址是BSS段结束符号的前一个字节。
代码中的-1是因为end字段表示的是最后一个有效字节的地址,而_etext、_edata等符号通常指向其段结束位置之后的一个字节。
内核各段布局与链接脚本
这些_text、_etext等符号由内核链接脚本vmlinux.lds.S定义,它们共同描绘了内核在内存中的完整布局:
| 符号 | 段 | 含义 |
|---|---|---|
_text | .text | 代码段的起始地址。 |
_etext | .text | 代码段的结束地址(之后是只读数据段)。 |
_edata | .data | 数据段的结束地址(之后是BSS段)。 |
__bss_start | .bss | BSS段的起始地址。 |
__bss_stop | .bss | BSS段的结束地址。 |
核心作用:建立内核资源表
这些struct resource对象(如code_resource、data_resource、bss_resource)是内核资源管理子系统的一部分。通过将它们注册到资源树中,内核能够:
提供信息查询接口:用户和系统程序可以通过
/proc/iomem文件查看这些段在物理内存中的确切位置和大小。防止资源冲突:明确告知内核的内存管理模块,这些物理地址范围已被内核自身占用,不应被其他设备驱动或内存分配器重复分配。
为内存管理和调试提供支撑:当内核需要映射物理地址或进行内存转储时,这些资源描述符提供了可靠的内核自身定位信息。
因此,这段看似简单的赋值代码,实际上是内核将自己“烙印”到物理内存地图上的一个过程,确保了后续所有内存管理操作的基础是清晰和正确的。