第2章 从内核出发
内核源码树
编译内核
配置内核
配置选项可以用来决定哪些文件编译进内核,也可以通过预处理命令处理代码。这些选项要么是二选一,要么是三选一。其中三选一是加多了module选项,编译的时候这部分功能被编译成以模块的形式生成,驱动程序一般都用三选一的配置选项。
配置选项可以是字符串或者整数。
配置工具
命令行工具:
make config
图形界面工具:
make menuconfig #ncurse库
make gconfig #gtk+
配置文件—.config
.config被放置在内核源码树中根目录下。修改了配置文件之后,或者更新新源码树的时候,应该验证与更新配置:
make oldconfig
编译
减少编译的垃圾信息
make > ../detritus 或
make > /dev/null #永无放回值的黑洞
内核开发的特点
1. 无libc库抑或无标准头文件
原因:(速度与大小)保证内核高效和简练。
头文件
基本头文件:内核源代码顶级目录下的include中
体系结构相关头文件:内核源代码树的arch/architecture/include/asm目录下
printk()函数:把格式化好的字符串拷贝到内核日志缓冲区上,syslog程序可以通过读取该缓冲区来获取内核信息。
printk()和printf()的最主要的区别:printk()允许通过设置优先级让syslog决定是否显示这条系统消息。
2. 必须使用GNU C
(1)内联函数
static inline void wolf(unsigned long tail_size);
- static:关键字
- inline:用于限定关键字
内联函数:编译时在它被调用的地方展开。
优点:减少了函数调用的开销,性能较好。
缺点:频繁的使用内联函数也会使代码变长,从而在运行时占用更多的内存。
定义内联函数特点:时间要求高,本身长度较短的函数,一般在头文件中定义。
在内核中,为了类型安全和易读性,优先使用内联函数而不是复杂的宏。
(2)内联汇编
Linux的内核混合使用了C语言和汇编语言。汇编语言用于偏近底层或对执行时间严格要求的地方。
(3)分支声明
对于条件选择语句,在一个条件经常/很少出现时,编译器可通过gcc内建的一条指令对条件分支选择进行优化。比如likely()(条件为真的频率高)和unlikely()(条件为真的频率很低)。内核把这条指令封装成了宏。
3. 没有内存保护机制
4. 难以执行浮点运算
5. 容积小而固定的栈
内核栈的大小是编译内核时决定的,对于不用的体系结构,内核栈的大小不一样,但都是固定的。每个处理器都有自己的栈。(不像用户空间的栈可以动态增长)
6. 同步和并发
原因:
- Linux是抢占多任务操作系统
- 内核支持对称多处理器系统(SMP)
- 中断是异步到来的
- 内核可以抢占
常用解决方法:自旋锁和信号量
7. 可移植性的重要性
大部分C语言代码与体系结构无关。必须把与体系结构相关的代码从内核代码树的特定目录中适当地分离出来。