Linux内核设计与实现读书笔记---(二)从内核出发
(1)获取内核源码
可以从Linux内核官网The Linux Kernel Archives获取Linux源代码。
(2)内核源码树
| 目录 | 说明 |
|---|---|
| arch | 特定体系结构的代码 |
| block | 块设备I/O层 |
| crypo | 加密API |
| Documentation | 内核源码文档 |
| drivers | 设备驱动程序 |
| firmware | 使用某些驱动程序而需要的设备固件 |
| fs | VFS和各种文件系统 |
| include | 内核头文件 |
| init | 内核引导和初始化 |
| ipc | 进程间通信代码 |
| kernel | 像调度程序这样的核心子系统 |
| lib | 同样内核函数 |
| mm | 内存管理子系统和VM |
| net | 网络子系统 |
| samples | 示例,示范代码 |
| scripts | 编译内核所用的脚本 |
| security | Linux 安全模块 |
| sound | 语音子系统 |
| usr | 早期用户空间代码(所谓的initramfs) |
| tools | 在Linux开发中有用的工具 |
| virt | 虚拟化基础结构 |
(3)编译内核
①配置内核
源码虽多,但可定制。配置项通常以 CONFIG_FEATURE 形式存在,分为二选一(yes/no)或三选一(yes/no/module)。module编译成模块(动态加载的独立代码段,驱动程序常用此选项),内核提供了各种配置工具来简化配置。
- 配置工具:
- make config:基于字符界面的命令行工具,需逐项确认,效率低 。
- make menuconfig:推荐,基于 ncurses 的文本菜单工具 。
- make gconfig:基于 gtk+ 的图形界面工具 。
- make defconfig:基于默认配置创建 。
- 配置文件:配置结果保存在源码根目录下的 .config文件中 。
②编译内核
使用 make 命令。可使用 make -jn(n 为作业数)开启多线程并发编译以提高速度 。
(4)内核开发的特点
①内核编程时不能访问C库也不能访问标准的C头文件
内核不能链接 libc 库,也不能包含标准 C 头文件,必须使用内核源码树中的 include/目录下的头文件,但内核实现了大部分常用 C 库函数(如字符串操作),位于 lib/ 目录下。例如不能使用 printf(),必须使用 printk()。
②内核编程时必须使用GNU C
啥是GNU C呢,简单来说GNU C = 标准 C (如 C99) + GCC 独有的扩展特性。
Linux 内核使用 C 语言编写,但依赖 GCC 提供的扩展特性,这些拓展包括:
- 内联函数 (Inline Functions):使用 static inline定义。用于消除函数调用开销,通常用于对时间要求高且短小的函数 。
- 内联汇编 (Inline Assembly):使用 asm() 嵌入汇编代码,用于偏底层或对时间严格要求的代码 。
- 分支预测优化: 使用 likely()和 unlikely() 宏。
- likely():标记条件通常为真。
- unlikely():标记条件绝少发生。
- 作用:指导编译器优化指令序列,若判断错误会导致性能下降 。
③没有内存保护机制
用户程序非法访问内存会被系统终止(SIGSEGV),但内核若非法访问内存(如引用空指针),会导致 oops 错误,甚至导致系统崩溃 。
内核内存不分页,使用一个字节物理内存就消耗一个字节 。
④内核编程时难以执行浮点运算
⑤内核给每个进程只有一个很小的定长堆栈
用户空间栈可以动态增长且很大;内核栈大小固定且很小 。
⑥由于内核支持异步中断、抢占和 SMP,因此必须时刻注意同步和并发
内核很容易产生竞争条件。和单线程的用户空间程序不同,内核的许多特性都要求能够并发地访问共享数据,这就要求有同步机制以保证不出现竞争条件,常用自旋锁和信号量来解决竞争 。
⑦要考虑可移植性的重要性