《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使用某些驱动程序而需要的设备固件
fsVFS和各种文件系统
include内核头文件
init内核引导和初始化
ipc进程间通信代码
kernel像调度程序这样的核心子系统
lib同样内核函数
mm内存管理子系统和VM
net网络子系统
samples示例,示范代码
scripts编译内核所用的脚本
securityLinux 安全模块
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 解惑

原文地址:https://www.cnblogs.com/hyq20135317/p/5277142.html