内核源码树结构
目录 | 描述 |
---|---|
arch | 特定体系结构的源码 |
block | 块设备I/O层 |
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 | 虚拟化基础结构 |
配置、编译及安装内核
配置内核
内核提供了各种不同的工具来简化内核配置。
一. 通过一个字符界面下的命令行工具
$ make config
这个工具会逐一遍历所有配置项,要求用户选择yes
,no
或module
(如果是三选一的话),比较耗费时间
二. 通过基于ncurse
库编制的图形界面工具(需要预先安装ncurse
)
$ make menuconfig
三. 通过基于gtk+
的图形工具
$ make gconfig
四. 基于默认配置为你的体系结构创建一个配置
$ make defconfig
这些配置项会被存放在内核源码树根目录下的.config
文件中,你可以找到它,并可以随意修改它。在你修改过配置文件之后,或者在用已有的配置文件配置新的代码树的时候,你应该验证和更新配置:
$ make oldconfig
编译内核
一旦内核配置好了,就可以使用一个简单的命令来编译它了
$ make
或者你也可以衍生多个作业来加快编译计数
$ make -jn
这里的n
是要衍生的作业数
安装内核
一. 安装内核模块
$ make modules_install
二. 安装内核
$ make install
Linux所谓的安装即是拷贝文件,修改配置文件,内核安装实际上也是如此
- 复制内核映像到
/boot
中,编译成功后生成的内核映像文件bzImage
放在arch/<architecture>/boot/
中,该文件复制到/boot
后重命名为vmlinuxz-<kernel-version>
- 生成
initrd-<kernel-version>
.img文件 - 配置引导程序(
GRUB
方式编辑/etc/grub/grub.conf
,LILO
方式编辑/etc/lilo.conf
) -
reboot
进入新内核
内核开发的特点
- 无
libc
库抑或无标准头文件
为了保证内核的小而且高效,内核开发不能使用C标准库,所以哪怕是最简单的printf
函数也无法使用,不过有替代的printk
- 使用
GNU C
,推荐使用GNC 4.4或之后的版本
因为使用了GNC
,所以Linux内核常有一些GNC
的一些扩展 - 内联(
inline
)函数
内核开发者通常把那些对时间要求比较高,而本身长度又比较短的函数定义成内联函数。
定义一个内联函数的时候,需要使用static
作为关键字,并使用inline
限定它,比如
```c
static inline void wolf(unsigned long tail_size)
```
- 内联汇编
gcc
编译器支持在C函数中嵌入汇编指令,在内核编程的时候,知道对应的体系结构,可以使用这个功能。通常使用asm()
指令嵌入汇编代码,例如下面这条内核指令用于执行x86处理器的rdtsc
指令,返回时间戳(tsc)寄存器的值:
```c
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
/* low 和 high 分别包含64位时间戳的低32位和高32位 */
```
- 分支声明
如果事先知道一个条件经常为假,或者经常为真,我们可以使用unlikely()
和likely()
对条件分支选择进行优化。
```c
/* 我们认为error绝大多数时间都会为0 */
if (unlikely(error)) {
/* ... */
}
```
```c
/* 我们认为success通常都不会为0 */
if (likely(success)) {
/* ... */
}
```
- 缺乏像用户空间那样的内存保护机制
- 难以执行浮点运算
- 内核给每个进程只有一个很小的定长堆栈
内核栈的大小由编译内核时决定的,对于不用的体系结构,内核栈的大小虽然不一样,但都是固定的。
查看内核栈大小的方法:
$ ulimit -a | grep "stack size"
- 由于内核支持异步中断、抢占和SMP,因此必须时刻注意同步和并发
- 要考虑可移植性的重要性