__attribute__()
属性: interrupt(“”)
我们知道,当发生中断的时候,系统会通过中断向量表跳转到相应的函数中并执行。等中断服务函数执行完后,又继续回到程序中。因此这个过程中涉及到一个现场的保护和恢复。那是否需要在中断处理函数里去写保存/恢复中断现场的代码?答案是不需要,只需要在中断处理函数前使用“__attribute__((interrupt))”
修饰
而这也就是__attribute__(interrupt(""))
的作用:编译器将C语言写的中断处理函数翻译成汇编代码时,会自动插入保存/恢复现场的汇编代码。也就是把中断处理函数中会用到的寄存器都先存入栈中,在中断返回前,再从栈里面读出来恢复到寄存器中
因此当我们需要将一个函数设定为中断服务函数的时候,我们就可以在这个函数前面添加上这个.例如:
SET(interrupt("")) void timer_isr(void) {// 中断服务程序的代码
}
属性: noinline
在 C 语言中,__attribute__((noinline))
是一个非标准的属性声明,用于告诉编译器不要将指定的函数进行内联展开。
那什么是内联展开呢?
函数调用通常会带来一定的开销,因为需要保存现场、跳转到被调用函数执行,然后再恢复现场继续执行当前函数。对于一些小而频繁调用的函数,这种开销可能不划算。内联函数的目的是减少函数调用的开销。当我们使用 inline
关键字声明一个函数时,编译器会尝试在调用处直接展开这个函数,避免函数调用的开销。内联通常可以提高代码的执行效率,但同时也会增加目标代码的大小。
当我们使用__attribute__((noinline))
修饰一个函数时,它就明确告诉编译器不要对该函数进行内联优化,而是保留标准的函数调用方式。
这在以下情况下很有用:
- 调试目的:有时为了方便调试,我们希望保留函数调用的堆栈信息,而内联会破坏堆栈跟踪。
- 性能分析:如果一个函数被内联,它的执行时间就会被合并到调用点,无法单独测量和分析该函数的性能开销。
- 函数指针:如果一个函数作为参数传递给函数指针,那么它就不能被内联,否则无法获取该函数的入口地址。
- 大函数:对于较大的函数,内联可能会导致代码膨胀,反而降低性能。使用
noinline
可以防止这种情况发生。 - 中断处理函数:中断处理函数通常需要快速响应,内联可能会增加代码路径长度,影响响应时间。
需要注意的是,虽然__attribute__((noinline))
可以阻止编译器内联优化,但它并不能阻止其他优化,比如常量折叠、死码消除等。此外,滥用noinline
可能会导致性能下降,因此应当谨慎使用。