1. Linux内核代码的编写和优化技巧
1.1. 代码编写
- 使用
__same_type
和typecheck
编译时检查类型匹配。 __printf
和__scanf
编译时检查代码。- 对宏进行undef后重新定义,生成不同的代码,如
__SYSCALL
配合unistd.h
生成系统调用,TRACE_EVENT
生成ftrace相关的数据结构和各个函数。 - 使用模板宏,减少重复代码也降低出错率,如
STANDARD_PARAM_DEF
生成module_param
用到的set和get函数。 - 使用内联汇编提升性能。
- 使用位图优化内存占用。
- 无锁队列kfifo。
1.2. 控制编译时行为
O2
或O3
,编译时优化汇编代码。__builtin_constant_p
优化常量分支。- 使用
likely
和unlikely
,编译时对代码进程重排序。 __noreturn
优化不需要返回的函数。- 宏、
inline
和__always_inline
,内联函数,减少入栈出栈操作。 - 使用
__randomize_layout
,结构体布局随机化,打乱成员排序,提升入侵难度。 - 使用
__cacheline_aligned
对结构体进行cache line对齐,提升访问性能,减少cache颠簸 - 细化程序分段,如
init
、initdata
等,释放无用内存。 - 区分冷热段,热段如
sched
、read_mostly
,冷段如tracepoint
段,热段更容易常驻cache。 - 重要部分使用联合减少结构大小,如
struct page
,减少内存占用。
1.3. 控制运行时行为
- 使用percpu变量减少核见竞争。
- 访问一个变量时,需同时获取两把锁,将锁放到不同的cache line。如
struct zone
。 - 使用动态代码修改,如
jump_label
、alternative_if
,减少分支判断。 - 栈随机化和地址空间布局随机化(KASLR),提升攻击难度。
- 零拷贝技术、写时复制
2. Linux内核中的面向对象编程
- 使用函数回调,抽象通用接口
- 抽象通用数据结构,使用container获取自定义数据结构
2.1. 使用 __noreturn会有哪些编译优化?
使用 __noreturn
属性可以允许编译器进行一些特定的优化,因为编译器知道带这个属性的函数不会返回。这些优化可能包括:
- 省略返回值:如果一个函数被标记为
__noreturn
,并且它的返回类型不是void
,编译器可能会省略对返回值的处理,因为它知道这个函数不会返回给调用者。 - 省略后续代码的生成:在函数调用的后面如果有代码,编译器可能会省略这些代码的生成,因为它们永远不会被执行。
- 优化调用栈:编译器可能会优化函数调用的栈处理,因为不需要为非
void
返回类型的函数保留返回地址。 - 分支预测:编译器可能会对调用
__noreturn
函数的代码进行分支预测优化,因为它知道执行流程将不会回到正常的控制流。 - 省略函数结束时的清理代码:通常,函数在返回前需要执行一些清理工作,比如释放局部变量的存储空间。对于标记为
__noreturn
的函数,这些清理工作可能被省略。 - 优化错误处理代码:如果一个函数在错误处理分支中调用
__noreturn
函数,编译器可能会优化这些错误处理路径,因为它们不会导致函数的正常返回。 - 消除不必要的检查:例如,如果一个
__noreturn
函数的返回值被用来做条件判断,编译器可能会省略这些检查,因为函数不会返回。 需要注意的是,虽然__noreturn
可以带来一些优化,但它也可能导致一些问题,如果使用不当。例如,如果一个函数被错误地标记为__noreturn
但实际上它可以返回,那么编译器可能会省略一些重要的代码路径,导致未定义的行为。因此,使用__noreturn
时必须非常小心,确保函数确实不会返回。