从内核出发

内核源码树

内核源码树根目录描述

目录 描述
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位对齐、不假定字长和页面长度等一系列准则都有助于移植性。