Linux内核触发SIGBUS场景解析
Linux内核啥时候出发SIGBUS
在 Linux 系统中,SIGBUS 信号(总线错误)通常由内核在以下场景触发:
1. 内存映射文件被截断(Truncated Memory-Mapped File)
- 当程序通过
mmap
将文件映射到内存后,如果文件被其他进程截断(例如truncate
或ftruncate
),程序访问被截断部分对应的内存区域时,会触发SIGBUS
。 - 示例:
1
2
3
4int fd = open("file.txt", O_RDWR);
void *addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// 另一个进程执行:truncate("file.txt", 2048); // 截断文件
// 访问 addr + 2048 之后的地址会触发 SIGBUS
2. 未对齐的内存访问(Unaligned Memory Access)
- 在某些架构(如 ARM、SPARC、MIPS 等 RISC CPU)中,访问未对齐的内存地址(例如尝试从非 4 字节对齐的地址读取
int
类型数据)会触发SIGBUS
。 - 注意:x86/x86_64 架构对未对齐访问更宽容(通常不会触发
SIGBUS
,但可能有性能损失)。
3. 访问物理无效的内存区域
- 尝试访问物理上不存在的地址(例如通过
/dev/mem
直接访问不存在的硬件寄存器地址)。 - 内存映射的硬件设备被移除(如热插拔设备)。
4. 内存页错误(Page Fault)的特殊情况
- 当内核无法处理某个内存页错误时(例如访问的页在交换空间但已被释放),可能触发
SIGBUS
而非SIGSEGV
。
5. 用户态访问内核保留内存
- 尝试通过
mmap
或其他方式访问内核保留的内存区域(如vmalloc
区域),可能触发SIGBUS
。
6. 硬件错误(罕见)
- 内存硬件故障(如 ECC 校验错误)可能通过
SIGBUS
通知用户态进程。
如何调试 SIGBUS?
- **使用
gdb
**:捕获信号并检查程序崩溃时的上下文。 - 检查内存操作:确保所有指针操作、内存对齐符合架构要求。
- 验证文件映射:确保
mmap
映射的文件未被意外修改。
与 SIGSEGV
的区别
SIGSEGV
(段错误):访问逻辑上无效的地址(如野指针、已释放内存)。SIGBUS
:访问物理上无效的地址或违反硬件对齐规则。
理解具体场景需要结合代码、架构和系统行为分析。
Linux ACPI支持多段地址的配置示例
在 Linux 系统中,ACPI(Advanced Configuration and Power Interface)通过 DSDT(Differentiated System Description Table)或 SSDT(Secondary System Description Table)描述硬件资源。当设备需要配置多个地址段(如多个内存区域或 I/O 端口范围)时,可以通过 ACPI 表中的 ResourceTemplate
定义多段地址。以下是一个示例和详细说明:
示例:ACPI 表中定义多段地址
假设某个硬件设备需要配置 两段内存地址 和 一段 I/O 端口范围,在 ACPI 表中可以按如下方式描述:
1 | Device (DEV0) { |
关键资源描述符说明
**
Memory32Fixed
**:- 描述 32 位固定地址 的内存区域。
- 参数:
(权限, 基地址, 长度)
。 - 示例:
Memory32Fixed (ReadWrite, 0xFE000000, 0x1000)
。
**
QWordMemory
**:- 描述 64 位动态地址 的内存区域,支持可缓存、可预取等属性。
- 参数:
(资源用途, 权限, 地址范围限制, 对齐, 长度, 类型)
。 - 示例中指定了长度为
0x2000
(8KB),地址对齐为0xA0000000
(4KB)。
**
IO
**:- 描述 I/O 端口范围。
- 参数:
(解码方式, 起始端口, 结束端口, 对齐, 长度)
。 - 示例:16 位端口范围
0x1000-0x100F
。
Linux 驱动中解析多段地址
在 Linux 设备驱动中,可以通过 ACPI API 解析这些资源:
1 |
|
注意事项
地址对齐:
- 内存地址需要按硬件要求对齐(如 4KB 对齐)。
- ACPI 中的
Memory32Fixed
和QWordMemory
需指定正确的Alignment
。
资源冲突:
- 确保地址段不与其他设备冲突,可通过
cat /proc/iomem
和cat /proc/ioports
检查。
- 确保地址段不与其他设备冲突,可通过
64 位支持:
- 使用
QWordMemory
描述 64 位地址时,需确保内核和硬件支持。
- 使用
ACPI 表编译:
- 修改 DSDT/SSDT 后需用
iasl
工具编译为 AML 文件,并通过引导加载程序(如 GRUB)加载。
- 修改 DSDT/SSDT 后需用
通过上述示例,可以在 ACPI 中定义多段地址并在 Linux 驱动中正确解析它们。实际应用中需结合硬件手册调整地址范围和属性。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Helloeuler!