《Linux内核设计与实现》读书笔记 第二章 从内核出发

一、获取内核源码

1. Git

  • git实际上是一种开源的分布式版本控制工具。

  • Linux作为一个开源的内核,其源代码也可以用git下载和管理

      - 获取最新提交到版本树的一个副本
    - $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
    - 下载代码后,更新自己的分支到最新分支
    - $ git pull

2.安装内核源代码

  • 压缩形式为bzip2:$ tar xvjf linux-x.y.z.tar.bz2

  • 压缩形式为zip:$ tar xvzf linux-x.y.z.tar.gz

      关于参数:
    -x 解开.tar格式的文件
    -v 显示详细信息
    -j 使用bzip2程序
    -z 使用gzip程序
    -f 使用归档文件

3. 使用补丁

从内部源码树开始,运行$ patch -p1 < ../patch-x,y,z

二、内核源码结构

详见LINUX内核分析第三周学习总结:构造一个简单的LINUX系统MENUOS中第一部分:“Linux内核源码简介”。

目 录 描 述
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 虚拟化基础结构
  • COPYIN:内核许可证
  • CREDITS:开发者列表
  • MAINTAINTERS:维护者列表(维护内核子系统和驱动程序)

三、编译内核

1. 配置内核(关于make与config)

(1)相关

  • Makefile:根据配置的情况,构造出需要编译的源文件列表,然后分别编译,并把目标代码链接到一起,最终形成 Linux 内核二进制文件。
  • config.in:内核配置文件,给用户提供配置选择的功能。
  • 配置工具:包括配置命令解释器(make config)和配置用户界面(例如:make menuconfig:基于ncurse库的图形界面工具;make gconfig:基于gtk+的图形工具...)。
  • .config:用户用来存放内核配置后结果的文件。
  • 可以配置的各种选项:用CONFIG_FEATURE形式表示,其前缀为CONFIG。

(2)命令

  • make config:遍历所有配置项,并让用户选择
  • make deconfig:按默认的配置
  • make oldconfig:先将/boot目录下的配置文件写进.config文件中,采用的是注释的形式写进新增加的功能。
  • zcat /proc/config.gz > .config:配置选项CONFIG_IKCONFIG_PROC会把完整的压缩过的内核配置文件存放在/proc/config.gz中,再次编译时可以方便地克隆当前的配置。
  • make:默认的Makefile自动化编译。

2. 其它事项

  • 减少垃圾信息

      $ make > ../detritus
    #将错误报告和警告信息重定向到文件中
    $ make > /dev/null
    #将无用的输出信息重定向到/dev/null中 - /dev/null:空设备,输入的信息直接丢弃
  • 衍生多个编译作业:make程序能把编译过程拆分成多个并行的作业。其中每个作业独立并发地运行,有助于加快多处理器系统上的编译过程,也有利于改善处理器的利用率。默认情况下,make只衍生一个作业。

      $ make -jn
    #以多个作业编译内核 - j:指定同时执行多任务
    - n:要衍生出的作业数

3. 安装新内核

make modules_install
#把所有已编译的模块安装到正确的主目录/lib/modules下
  • System.map文件:编译时在内核代码树的根目录下创建的符号对照表。用来将内核符号与它们的起始地址对应起来。

四、内核开发特点

1. 无libc库/标准头文件

  • 原因:(速度与大小)保证内核高效和简练。

  • 内核源代码文件不能包含外部头文件。

    • 基本头文件:内核源代码*目录下的include
    • 体系结构相关头文件:内核源代码树的arch/<architecture>/include/asm目录下
  • printk()函数:把格式化好的字符串拷贝到内核日志缓冲区上,syslog程序可以通过读取该缓冲区来获取内核信息。

2. 必须使用GNU C

什么是GNU?GNU是一种操作系统,GNU提供的C编译器就是我们之前使用的gcc。

(1)内联函数

static inline void wolf(unsigned long tail_size);
- static:关键字
- inline:用于限定关键字
  • 内联函数:编译时在它被调用的地方展开。

    • 优点:减少了函数调用的开销,性能较好。
    • 缺点:频繁的使用内联函数也会使代码变长,从而在运行时占用更多的内存。
  • 定义内联函数特点:时间要求高,本身长度较短的函数。

  • 使用之前就要定义好内联函数,一般在头文件中定义。

  • 为了类型安全和易读性,优先使用内联函数而不是复杂的宏。

(2)内联汇编

unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
/* low 和 high 分别包含64位时间戳的低32位和高32位 */
- asm:嵌入汇编代码
- volatile:不优化
  • 汇编语言用于偏近底层或对执行时间严格要求的地方。

(3)分支声明

/* 如果error在绝大多数情况下为0(假) */
if (unlikely(error)) {
/* ... */
} /* 如果success在绝大多数情况下不为0(真) */
if (likely(success)) {
/* ... */
}
  • 对于条件选择语句,在一个条件经常/很少出现时,编译器可通过gcc内建的一条指令对条件分支选择进行优化。
  • 内核把这条指令封装成了宏。

3. 没有内存保护机制

  • 内核自己非法访问内存的风险
  • 内核中的内存都不分页:每用掉一个字节,物理内存都减少一个

4. 难以执行浮点运算

  • 使用浮点数时,需要人工保存和恢复浮点寄存器及其他一些繁琐的操作。
  • 不建议使用

5. 每个进程只有一个很小的定长堆栈

  • 内核栈的大小是编译内核时决定的,对于不用的体系结构,内核栈的大小不一样,但都是固定的。(不像用户空间的栈可以动态增长)

6. 必须时刻注意同步和并发

  • 原因:

    • Linux是抢占多任务操作系统
    • 内核支持对称多处理器系统(SMP)
    • 中断异步到来
    • 内核可以抢占
  • 常用解决方法:自旋锁和信号量

7. 考虑可移植性的重要性

  • 需要保持的特点:大部分C语言代码与体系结构无关。

五、总结:关于Linux内核的结构与特点

1. 版本控制

  • 我最早接触git是刚开始使用实验楼的时候,实验楼中的代码保存需要用到其中“我的代码库”功能,实际上就是最简单的git。
  • Linux内核这种开源的代码以及很多项目使用git进行版本控制与协作都是挺方便的。
  • 网上有很多教程可以参考,感觉这个比较全面:Git教程,可以在一些网站上创建自己的代码库,比如:git.oschina,操作过就会发现还是比较简单的。

2. 依据结构和特点的开发

  • 通读本章之后感觉Linux内核的很多要求与一般的项目其实是差不多的,它的这些基本结构、开发的特点,对于理解它各个部分的工作过程是很有帮助的。

参考资料1:《Linux内核设计与实现》(原书第三版)

参考资料2:make config 解惑

上一篇:多文件上传


下一篇:RMQ训练题 codevs 1291 火车线路 已经搞定