Mr Dk.'s BlogMr Dk.'s Blog
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🐧 Linux Kernel Development
    • Chapter 1 - Linux 内核简介
    • Chapter 2 - 从内核出发
    • Chapter 3 - 进程管理
    • Chapter 4 - 进程调度
    • Chapter 5 - 系统调用
    • Chapter 7 - 中断和中断处理
    • Chapter 9 - 内核同步介绍
    • Chapter 10 - 内核同步方法
    • Chapter 11 - 定时器和时间管理
    • Chapter 13 - 虚拟文件系统
    • Chapter 14 - 块 I/O 层
    • Chapter 16 - 页高速缓存和页回写

Chapter 2 - 从内核出发

Created by : Mr Dk.

2019 / 10 / 07 22:55

Nanjing, Jiangsu, China


2.1 获取内核源码

略过...

2.2 内核代码树

2.3 编译内核

2.3.1 配置内核

由于内核提供了数不胜数的功能,支持了难以计数的硬件,所以需要配置很多东西。所有可配置的选项都以 CONFIG 开头,要么是二选一,要么是三选一。

  • yes - 编译进主内核映像中
  • no
  • module - 编译时以模块的形式生成 (可以动态安装的独立代码段)
make config

这个工具会逐一遍历所有配置项,要求用于二选一或三选一。这样很麻烦,所以可以这样:

make defconfig

基于默认的配置,为你的体系结构创建一个配置。所有的配置项都被存放在内核代码 root 目录下的 .config 中,该文件可以被直接修改。修改配置文件之后,应当验证和更新配置:

make oldconfig

之后就 make 编译吧。

2.3.2 减少编译的垃圾信息

将 make 重定向到某个文件或 /dev/null 中。

2.3.3 衍生多个编译作业

make -jn

2.3.4 安装新内核

内核编译好后,需要将内核映像拷贝到合适的位置并安装。因体系结构和启动引导工具而异。

2.4 内核开发的特点

内核开发与用户空间内的应用程序开发的差异:

  • 不能访问 C 库,也不能访问标准的 C 头文件
  • 必须使用 GNU C
  • 缺乏像用户空间那样的内存保护机制
  • 难以执行浮点运算
  • 内核给每个进程一个很小的定长堆栈
  • 需要时刻注意同步和并发
  • 要考虑可移植性

2.4.1 无 libc 库异或无标准头文件

因为 C 库中的函数对于内核来说太庞大,性能低下,大部分常用的 C 库函数在内核中都已经得到了实现,比如 string.h 等。最著名的 printf() 也由 printk() 实现。

2.4.2 GNU C

内核并不完全符合 ANSI C 标准,而是使用了 ISO C99 标准和 GNU C 的扩展特性。

内联函数 (inline)

函数在被调用的位置展开:

  • 消除函数调用和返回的开销 (寄存器存储和恢复)
  • 编译器可以把调用函数的代码和函数本身一起进行优化
  • 代码变长,占用更多的内存和指令缓存

通常会把对时间要求高,而自身长度较短的函数定义为内联函数。需要使用 static 关键字来声明。

static inline void wolf(unsigned long tail_size);

在内核中,为了 类型安全 和 易读性,优先使用内联函数,而不是 宏。

内联汇编

gcc 编译器支持在 C 函数中嵌入汇编指令。只有知道对应的体系结构,才能使用这个功能。在偏近体系结构底层,或对时间要求严格的地方,使用汇编。

分支声明

对于条件选择语句,gcc 内建了指令用于优化:

  • 一个条件经常出现
  • 一个条件很少出现

编译器根据这个指令对条件分支选择进行优化,内核把这条指令封装为宏。

if (error) {
    // ...
}
//
if (unlikely(error)) {
    // ...
}
//
if (likely(error)) {
    // ...
}

需要判断是否存在条件,使得大多数情况下都会成立:

  • 如果判断正确,那么这样能提高性能
  • 否则性能反而会下降

2.4.3 没有内存保护机制

内核访问了非法内存,后果难以控制。内核中的内存都不分页,用掉一个字节,物理内存就少一个字节。

2.4.4 不要轻易在内核中使用浮点数

用户空间中的浮点数操作,内核会在其中完成从整数操作到浮点数操作的模式转换。在内核中使用浮点数,需要人工保存和恢复浮点寄存器,还要做一些琐碎的事——尽量 别这么做 !除了极少数情况,不要在内核中使用浮点操作。

2.4.5 容积小而固定的栈

用户空间的栈比较大,并且可以动态增长。内核栈的大小随体系结构而变,而且 大小固定。

2.4.6 同步和并发

内核很容易产生竞争条件:

  • Linux 是抢占多任务 OS - 内核必须和其调度的任务同步
  • Linux 内核支持对称多处理器系统 (SMP) - 同时在两个以上的 CPU 上执行的内核代码很可能会同时访问共享资源
  • 中断是异步到来的 - 中断完全可以在代码访问资源时到来,这样中断处理程序也能访问同一资源
  • Linux 内核可以抢占 - 内核中一段正在执行的代码可能会被另一段代码抢占,导致几段代码同时访问相同的资源

需要用机制解决竞争。

2.4.7 可移植性的重要性

大部分 C 代码应该与体系结构无关,必须把与体系结构相关的代码从内核代码树的特定目录中分离。

Summary

OS 内核之所以难、复杂,就是因为这些吧 🥱

Edit this page on GitHub
Prev
Chapter 1 - Linux 内核简介
Next
Chapter 3 - 进程管理