Alignment trap 解决方法

本文介绍了在ARM9平台上编译并运行crtmpserver时遇到的AlignmentTrap问题及其解决方案。通过对编译参数的调整及内存对齐设置的更改,最终定位到问题代码并修复。文章强调了在不同架构间移植代码时应注意内存访问方式。
前几天交叉编译crtmpserver到arm9下。编译通过,但是运行的时候,总是提示Alignment trap,但是并不影响程序的运行。这依然很令人不爽,因为不知道是什么原因引起的,这就像一颗定时炸弹一样,一定要解决。

    修改makefile,加入-ggdb,去掉编译优化,重新编译。编译完毕,在gdb下运行,依然提示Alignment trap,并且gdb没有任何反应。按照设想,操作系统应该能捕获到这个错误,然后通过信号的方式传递给gdb,gdb再中断停下来。但是事实上并没有按照我的设想运行,为什么呢?通过查找资料,发现cpu在处理内存对齐的时候,有几种方式可以设置。

cat /proc/cpu/alignment
User:           1
System:         0
Skipped:        0
Half:           0
Word:           1
DWord:          0
Multi:          0
User faults:    3 (fixup+warn)

    我的嵌入式linux系统下的默认处理方式是第3级处理方式:修复+警告。

0 - ignore
1 - warn
2 - fixup
3 - fixup+warn
4 - signal
5 - signal+warn (需要这个)

    于是修改为:echo 5 > /proc/cpu/alignment,这样就会给内核一个信号。再在gdb下面重新运行

./rtmpserver ./rtmpserver.lua,果然gdb捕获到该信息,然后bt,查看出现问题的代码:
_currentFPVersion = ntohl(*((uint32_t *) (GETIBPOINTER(buffer) + 4))); //----MARKED-LONG---
原来是在强制类型转换读取内存的时候出现了错误,于是修改为:
 
uint32_t uTemp = 0; 
  memcpy(&uTemp,GETIBPOINTER(buffer) + 4,sizeof(uint32_t)); 
_currentFPVersion = ntohl(uTemp);
 
    再重新编译,运行,果然烦人的Alignment trap消失了。这也提醒我们,平时在写代码的时候,在内存访问上,尽量使用memcmp,memcpy,memset等函数,而不要为了方便,直接对指针内容进行访问。这样的代码在x86上可能没问题,但是运行到arm上,就可能会出问题。关于为什么在arm上会出现Alignment trap,可以参考http://hi.baidu.com/simplejoy/blog/item/cf456c8b1549e617c8fc7ad6.html
在Linux内核中,`trap_init`函数用于初始化异常处理机制,它负责设置处理器在遇到异常(如除法错误、页错误、调试异常等)时的处理流程。该函数通常在内核启动的早期阶段被调用,以确保在系统运行过程中能够正确地处理各种异常情况。 ### `trap_init` 的主要功能 1. **设置异常向量表** `trap_init`会初始化异常向量表(Interrupt Descriptor Table, IDT),该表包含了每个异常或中断的处理程序地址。在x86架构中,IDT是一个包含最多256个描述符的数组,每个描述符对应一个中断或异常。`trap_init`会调用`set_trap_gate`、`set_intr_gate`等函数来填充这些描述符,确保每个异常都能正确跳转到对应的处理程序[^1]。 2. **注册核心异常处理程序** 内核定义了一系列与异常相关的C语言处理函数,如`do_divide_error`、`do_debug`、`do_nmi`等。`trap_init`会将这些函数注册到IDT中,作为异常发生时的回调函数。例如: ```c set_trap_gate(X86_TRAP_DE, &divide_error); set_trap_gate(X86_TRAP_DB, &debug); set_intr_gate(X86_TRAP_NMI, &nmi); ``` 3. **初始化调试支持** 如果启用了调试选项(如`CONFIG_X86_DEBUG_IDT`),`trap_init`还会设置额外的调试陷阱,用于监控系统行为并协助调试。 4. **初始化本地APIC定时器中断(可选)** 在SMP系统中,`trap_init`可能还会设置本地APIC定时器中断的处理程序,以便支持高精度定时器功能。 ### `trap_init` 的实现示例 以下是一个简化的`trap_init`函数实现片段,展示了其基本结构和调用方式: ```c void __init trap_init(void) { int i; /* 初始化IDT描述符 */ for (i = 0; i < ARRAY_SIZE(idt_table); i++) { idt_table[i] = default_idt_handler; } /* 设置特定异常的处理函数 */ set_trap_gate(X86_TRAP_DE, &divide_error); set_trap_gate(X86_TRAP_DB, &debug); set_trap_gate(X86_TRAP_NMI, &nmi); set_trap_gate(X86_TRAP_BP, &int3); set_trap_gate(X86_TRAP_OF, &overflow); set_trap_gate(X86_TRAP_BR, &bounds); set_trap_gate(X86_TRAP_UD, &invalid_op); set_trap_gate(X86_TRAP_NM, &device_not_available); set_trap_gate(X86_TRAP_DF, &double_fault); set_trap_gate(X86_TRAP_OLD_MF, &coprocessor_segment_overrun); set_trap_gate(X86_TRAP_TS, &invalid_tss); set_trap_gate(X86_TRAP_NP, &segment_not_present); set_trap_gate(X86_TRAP_SS, &stack_segment); set_trap_gate(X86_TRAP_GP, &general_protection); set_trap_gate(X86_TRAP_SPURIOUS, &spurious_interrupt_bug); set_trap_gate(X86_TRAP_MF, &coprocessor_error); set_trap_gate(X86_TRAP_AC, &alignment_check); set_trap_gate(X86_TRAP_XF, &simd_coprocessor_error); /* 初始化本地APIC定时器中断(如果启用) */ if (cpu_has_apic) setup_local_APIC_timer(); /* 其他架构相关的初始化 */ setup_irq_stack(); } ``` ### 调试 `trap_init` 的方法 1. **使用内核调试器(如KGDB)** 可以在`trap_init`函数中设置断点,观察其执行流程,并检查IDT表是否正确初始化。例如,在GDB中可以使用以下命令: ``` break trap_init run ``` 2. **打印调试信息** 在`trap_init`函数中插入`printk`语句,输出关键寄存器或数据结构的状态,例如: ```c printk(KERN_INFO "trap_init: IDT initialized at %p\n", idt_table); ``` 3. **检查IDT内容** 在内核启动后,可以通过`cat /proc/interrupts`或使用`crash`工具查看IDT的内容,确认异常处理函数是否正确注册。 4. **模拟异常** 可以编写模块或用户空间程序来触发特定异常(如除零错误),观察内核是否能正确处理并跳转到对应的处理函数。 ### 总结 `trap_init`是Linux内核启动过程中非常关键的一个函数,它负责初始化异常处理机制,确保系统能够正确响应各种异常事件。通过理解其调用流程和实现细节,可以更好地调试内核行为并优化系统稳定性。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值