《linux内核设计与实现》笔记:第二章从内核出发

文章目录

  • 1、关于内核源码树
  • 2、关于内核开发的特点
  • 3、关于标准C函数库
  • 4、关于GNU C
  • 5、关于内存保护机制
  • 6、关于浮点数
  • 7、关于内核栈
  • 8、关于同步和并发

1、关于内核源码树

目录 描述
arch 特定体系结构的源码
block 块设备I/O层
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 虚拟化基础结构

2、关于内核开发的特点

1)内核编程时既不用访问C库,也不能访问标准的C文件;
2)内核编程时必须使用GNU C;
3)内核编程时缺乏像用户空间那样的内核保护机制;
4)内核编程时难以执行浮点运算;
5)内核给每个进程只有一个很小的定长堆栈;
6)由于内核支持异步中断、抢占和SMP,因此必须时刻注意同步和并发。

3、关于标准C函数库

  • 与用户空间的应用程序不同,内核不能链接使用标准C函数库。最主要的原因时速度和大小。对内核来说,完整的C库——哪怕是它的一个子集,都太大且太低效了。
  • 不过不用担心,大部分常用的C库函数在内核中都已经得到了实现。
  • 内核代码虽然无法调用printf(),但它提供了printfk()函数,几乎与printf()相同。

4、关于GNU C

  • gcc是多种GNU编译器的集合,它包含的C编译器既可以编译内核,也可以编译Linux系统上用C语言写的其它代码。
  • 内核开发者使用的C语言涵盖了ISO C99标准和GNU C扩张特性。Linux内核使用gcc的各种特性。
  • 当我定义一个内函数时,需要使用static作为关键字,并且用inline限定它。比如:static inline void wolf(unsigned long tail_size)。由于使用了static作为关键字进行限制,所以编译时不会为内联函数单独建立一个函数体。在内核中,为了类型安全和易读性,优先选用内联函数,而不是复杂的宏。
  • 对于内联汇编,我们通常使用asm()指令嵌入汇编代码。在偏近体系结构的底层或对执行时间要求严格的地方,一般使用的是汇编语言。而内核其它部分的大部分代码是用C语言编写的。
  • 对于条件选择语句,gcc内建了一条指令用于优化,在一条条件经常出现,或者该条件很少出现的时候,编译器可以根据这条指令对条件分支选择进行优化。内核把这条指令封装成了宏,比如likely()unlikely()。还有,必须明确的一点是,如果你判断正确,确实是这条占压倒性的地位,那么性能会得到提升;如何搞错,性能反而下降。

5、关于内存保护机制

  • 如果一个用户程序试图进行一次非法的内存访问,内核就会发现这个错误,发送SIGSEGV信号,并结束整个进程。
  • 对于内核,自己非法访问了内存,则后果很难控制(用户空间的非法访问,可以由内核来处理,而内核的访问,由谁来管呢?)。内核发生的内存错误会导致oops,这是内核中出现的最常见的一类型错误。
  • 在内核中,不应该做访问非法的内存地址,引用空指针之类的事情,否则它可能会死掉却根本不告诉你一声——在内核里,风险常常比外面更大一些。

6、关于浮点数

  • 与用户空间进程不同,内核并不能完美地支持浮点操作,因为它本身不能陷入。陷入指触发中断。
  • 在内核中使用浮点数是,除了要人工保存和恢复浮点寄存器,还有其它一些琐碎的事情要做。
  • 在内核中,不要轻易使用浮点数。

7、关于内核栈

  • 内核栈的准确大小随体系结构而变。在X86上,栈的大小在编译时配置,可以是4KB,也可以是8KB。
  • 从历史上说,内核栈的大小是两页,这意味着,32位机的内核栈是8KB,而64位机是16KB,这是固定不变的。每个处理器都有自己的栈。

8、关于同步和并发

  • 内核很容易产生竞争条件。和单线程的用户空间程序不同,内核的许多特性都要能够并发地访问共享数据,这就要求有同步机制以保证不出现竞争条件。
  • 常用的解决竞争的办法是自旋锁和信号量。

你可能感兴趣的:(《linux内核设计与实现》笔记:第二章从内核出发)