从内核出发
内核源码树
内核源码树根目录描述
目录 | 描述 |
---|---|
arch | 特定体系结构的源码 |
block | 块设备IO层 |
crypto | 加密API |
Documentation | 内核源码文档 |
drivers | 设备驱动程序 |
firmware | 使用某些驱动程序而需要的设备固件 |
fs | VFS和各种文件系统 |
include | 内核头文件 |
init | 内核引导和初始化 |
ipc | 进程间通信代码 |
kernel | 像调度程序这样的核心子系统 |
lib | 通用内核函数 |
mm | 内存管理子系统和VM |
net | 网络子系统 |
samples | 实例,示范代码 |
scripts | 编译内核所用的脚本 |
security | Linux安全模块 |
sound | 语音子系统 |
usr | 早期用户空间代码(所谓的initramfs) |
tools | 在Linux开发中有用的工具 |
virt | 虚拟化基础结构 |
在源码根目录还有很多文件值得提及。COPYING文件是内核许可证(GNU GPL v2)。CREDITS是开发了很多内核代码的开发者列表。MAINTAINERS是维护者列表,负责维护内核子系统和驱动程序。Makefile是基本内核的Makefile。
编译内核
配置内核
在编译前可以把自己的需要的特定功能和驱动程序编译进内核,首先必须先配置,
其以CONFIG_FEATURE形式表示。配置选项要么是二选一(yes/no)要么是三选一(yes/no/module)module表示这部分功能的实现代码是以模块的形式生成。驱动程序一般都是三选一选项。
配置选项也可以是字符串或整数。这些选项并不控制编译过程,而只是指定内核源码可以访问的值,一般以预处理宏的形式表示。
$ make config
该工具会遍历所有配置项,但耗时过长
$make menuconfig
$make gconfig
可破之。
以上三种工具将所有配置分门别类的放置。
$make defconfig
创建默认配置。
这些配置项被存放在根目录的.config文件中。在进行修改后,你应当验证和更新配置:
$ make oldconfig
配置选项CONFIG_IKCONFIG_PROC把完整的压缩内核配置文件放在/proc/config.gz下,在编译新内核时可以克隆配置。
$zcat /proc/config.gz > .config
$make oldconfig
衍生多个编译作业
make程序能把编译过程拆分成多个并行的作业。极大加快编译的过程。
$make -jn
安装新内核
%make modules_install
内核开发的特点
- 内核编程时既不能访问c库也不能访问标准c头文件。
- 内核编程时必须使用GNU C
- 内核编程时缺乏像用户空间那样的内存保护机制。
- 内核编程时难以执行浮点运算。
- 内核给每个进程只有一个很小的定长堆栈。
- 由于内核支持异步中断、抢占和SMP,因此必须时刻注意同步和并发。
- 要考虑可移植性的重要性。
无libc库或无标准头文件
因为对内核来说,C库太大太低效了。但是大部分常用的C库函数在内核中都已经得到了实现。
内核头文件位于根目录include目录下。< linux/inotify.h>对应内核include/linux/inotify.h
体系结构相关的头文件位于源码树arch/< architecture>/include/asm目录下。内核代码通过如< asm/youwant.h>包含。
printk(“Hello world’%s’and ‘%d’”,str,i);
例如内核代码无法调用printf,但它提供printk()。其允许通过一个标志来设置优先级。
GNU C
gcc是多种GNU编译器的集合,它包含的C编译器既可以编译内核,也可以编译Linux系统上用C语言写的其他代码。
- 内联函数,用于时间要求高,本身长度短的函数。 定义内联函数时需要static 作为关键字,并且用inline限定它。为了类型安全和易读性,优先使用内联函数而不是复杂的宏。
- 内联汇编,gcc支持C函数嵌入汇编指令,需要知道其体系结构。使用asm()指令嵌入汇编代码。Linux内核混用了C语言和汇编语言。在偏近体系结构底层或对执行时间要求严格的地方,一般使用汇编语言,其他大部分代码是用C语言编写的。
- 分支声明,对于条件选择语句,gcc内建了优化指令,并封装成了宏。likely()和unlikely(),你的判断正确,性能会得到提升,如果判断错误,性能反而会下降。
没有内存保护机制
在内核中不应该去做访问非法的内存地址,引用空指针一类的事,否则可能一声不响的死掉。内核的内存不分页。
不要轻易在内核中使用浮点数
内核并不能完美地支持浮点操作,因为它本身不能陷入。在内核使用浮点数时,除了要人工保存和恢复浮点寄存器,还有一些其他的事情要处理。所以不要在内核中使用浮点操作。
容积小而固定的栈
同步和并发
可移植性的重要性
大部分C代码应该与体系结构无关,在不同体系结构的计算机上都能够编译和执行。如保持字节序、64位对齐、不假定字长和页面长度等一系列准则都有助于移植性。