《Linux Kernel Development》读书笔记PDF下载
by
at 2012-05-12 14:22:53
original http://simple-is-better.com/news/866
这个读书笔记目录系列尝试以PDF文件的形式提供,也是我的LaTeX初学成果。
《Linux Kernel Development》读书笔记1
第1章:Linux内核概览
第2章:内核概览
Linux Kernel Development读书笔记
mdyang 1 2
本文档是《Linux Kernel Development》英文版第三版的读书笔记。
目录:
-
Introduction to the Linux Kernel (Linux内核概览)
-
Getting Started with the Kernel (内核入门)
-
Process Management (进程管理)
-
Process Scheduling (进程调度)
-
System Calls (系统调用)
-
Kernel Data Structures (内核数据结构)
-
Interrupts and Interrupt Handlers (中断与中断处理器)
-
Bottom Halves and Deferring Work (下半部和推后执行的工作)
-
An Introduction to Kernel Synchronization (内核同步概览)
-
Kernel Synchronization Methods (内核同步方法)
-
Timers and Time Management (定时器与时间管理)
-
Memory Management (内存管理)
-
The Virtual Filesystem (虚拟文件系统)
-
The Block I/O Layer (块I/O层)
-
The Process Address Space (进程地址空间)
-
The Page Cache and Page Writeback (页缓存与页写回)
-
Devices and Modules (设备与模块)
-
Debugging (调试)
-
Portability (可移植性)
- Patches, Hacking, and the Community (补丁、技巧与Linux社区)
Chapter 1
第1章:Linux内核概览
1.1 Linux的历史
1.1.1 Unix的历史
Unix由Dennis Ritchie和Ken Thompson在1969年创造。Unix的前身是Multics,一个失败的多用户操作系统。1969年,贝尔实验室启动了一个文件系统项目,最终演化为Unix,Unix最早在PDP-7系统上运行,后来被移植到PDP-11。1973年,Unix被使用C语言重写。
最早广为使用的Unix系统是Unix第六版,简称V6。除贝尔实验室外也有一些其他公司开发了自家的Unix,1977年贝尔实验室将这些分支系统整合后发布了Unix System III。1982年,AT&T发布了System V。
众多Unix开发者中数UC Berkeley的开发者最有影响力,这些发行被统称为BSD(Berkeley Software Distribution)。BSD的第一个发布1BSD发布于1977年,第二年发布了2BSD。这两版都是对贝尔版Unix再打包并添加一些应用软件后发布的。首个完全独立开发的BSD Unix是3BSD,发布于1979年。现在已经有了Darwin、FreeBSD、NetBSD、OpenBSD等多个BSD Unix版本。
Unix的设计初衷是设计一个简单、强大、鲁棒的操作系统,这主要体现在以下几点:
- 与当时的操作系统动辄就有上千个系统调用相比,Unix只有数百个系统调用
- Unix的思想是“一切都是文件”,简化了对数据的操作。无论是读写文件还是对I/O设备进行操作,都可以使用诸如write()、lseek()和close()这样的函数完成
- Unix使用C写成,具有良好的可移植性
- Unix创建进程的性能极好,系统调用fork()也是独一无二的
- Unix具有简单、鲁棒的IPC(Inter-Process Communication,进程间通信)机制
今天的Unix系统已经是一个抢占式的(preemptive)多任务操作系统,支持多线程、虚拟内存,请求调页(demand paging)、动态共享库与TCP/IP网络连接等现代操作系统特性。
1.1.2 Linux的历史
Linux是芬兰的Linus Torvalds在1991年写成的一个基于80386架构的操作系统。那时Linus是赫尔辛基大学的一名学生,由于当时缺乏为个人电脑设计的操作系统,他便自己编写了一个操作系统,称为Linux. 今天,Linux已经能够在Alpha、ARM、PowerPC、SPARC、x86-64以及很多其它架构的硬件上运行,从有房间那么大的巨型机到嵌入式设备都能见到Linux的身影。
Linux是一个类Unix(Unix-like)的系统,这意味着Linux实现了Unix具备的很多特性(例如Linux实现了Unix API),但与其它Unix系统不同,Linux并不是基于Unix代码实现的。
Linux的另一个显著特性是它并不由某个人或某家公司控制,Linux是社区协作的产物。虽然创始人Linus依然被尊为Linux的创造者,并直到现在依然在维护Linux内核代码,但整个Linux内核的开发与更新工作是由整个社区推动的。Linux内核在GPL General Public License (GPL) v2.0许可证下发布。因此,任何人都可以下载Linux的内核源代码,并且进行修改。但前提是经过修改的代码也必须遵守GPL v2.0的条款。
1.1.3 Linux内核概览
Linux操作系统是指是系统运行起来所需的最基本的程序和工具的集合,包括内核、设备驱动、引导加载器(boot loader)、命令行Shell以及其他用户界面、文件系统和系统工具。其中用户界面是最外围的部分,直接与用户交互,而内核则是最核心的部分。
Linux中有用户态(User Mode)和内核态(Kernel Mode)的区分。内核程序是在内核态下执行的,用户程序是在用户态下执行的,在这两个状态之间转换需要进行上下文切换(Context Switch)。用户态下的程序使用系统提供的功能是通过系统调用(System Call)实现的。比如对于库函数printf(),它进行的操作是将输出数据格式化写入缓冲区后,调用系统调用write()输出缓冲区内容到控制台。当应用程序调用系统调用时,我们说内核正在代表应用执行;或是说应用程序正在内核态下执行系统调用,内核在进程上下文中运行。
内核也负责系统硬件的管理。几乎所有的架构中都使用中断这个概念处理外部硬件请求(例如键盘敲击)。Linux内核负责管理中断请求,并调用中断处理程序(handler)处理中断请求。
1.1.4 Linux内核与传统Unix内核的对比
单内核(Monolithic Kernel)与微内核(Microkernel)的比较
单内核是指将内核的所有功能实现为一个整体的二进制可执行镜像(image),在同一个进程空间内执行。
微内核则反其道行之,将不同的功能实现为不同的模块,在不同的进程空间内执行。Windows NT和Mach(即Mac OS X)都是微内核的代表。这种设计使得需要使用IPC机制处理这些进程之间的通信。
Linux是单内核的操作系统,但与Unix不同,Linux从微内核的思想中借鉴一些东西,将不同的系统功能分别编译为不同的共享库,这些库可以在调用请求发生时被动态加载进内核的进程空间。
以下是Linux与Unix相比不同的地方:
- Linux支持内核模块的动态链接
- Linux支持SMP(Symmetrical Multiprocessor)
- Linux内核是抢占式的
- Linux并不区分进程和线程,它们都是进程。同一进程内的多个线程被视作共享某些资源的多个进程
- Linux提供面向对象的设备模型,可管理设备类,支持热插拔,并通过用户空间设备文件系统(user space device filesystem)管理设备
- Linux摒弃了Unix中一些败笔的设计,例如STREAMS
-
Linux是开放、自由、免费的,你可以免费获得Linux源代码,对其进行修改,甚至还可以将这些修改贡献至社区
Linux内核版本号
Linux内核版本号分为四段:2(主版本号).6(次版本号).26(Revision版本号).1(稳定版本号)
主版本号代表大的升级,次版本号代表小的升级。次版本号为偶数代表为稳定版,奇数代表开发版。例如1.3经bug修复后发布了2.0稳定版,2.5经稳定后发布了2.6稳定版。
到2004年有了一些变化,因为2.6的Linux内核已经包括了现代操作系统所需具备的全部特性,并且稳定、高效。因此决定延长2.6版本的生命周期,推迟2.7版的开发。自此Linux内核版本一直稳定在2.6版,只是更新Revision版本号、稳定版本号。Revision版本号更新代表着相对上一个Revision进行了较多修改,并引入了新特性。稳定版本号升级代表进行了重要bug修复。
主要的Linux内核开发社区是Linux Kernel Mailing List(也称为lkml)
Chapter 2
第2章:内核入门
Linux内核源代码使用Git进行版本管理,因此可通过git clone命令获得Linux内核源代码。使用tar解压即可安装源码、并阅读、修改。可以使用patch命令对代码进行增量式修改,例如添加特性、版本切换等。需要注意的是由于/usr/src/linux下的源代码是供GCC等编译器编译代码时使用的头文件所在的地方,因此对内核代码的修改最好不要在该位置进行。可以在自己的用户文件夹下新建一个目录,并将源码安装至此进行修改、编译。
内核编译的配置文件以CONFIG开头,为CONFIG_FEATURE的格式,例如对于SMP,其config文件为CONFIG_SMP。配置通常是二元选择(yes和no)或三元选择(yes、no和module)。对于设定编译为module的模块,该模块会被编译为动态链接库。也有一些选项需要执行数值或是字符串。
内核代码中提供了一些编辑配置文件的工具,例如make config就启动一个最简单的基于文本的命令行工具,对于每个编译选项提出问题,用户需要对于每个问题选择编译选项(yes, no或是module)。还有一些图形化的编辑工具,例如make menuconfig和make gconfig等。也可以直接编辑.config文件或是使用make defconfig应用默认设置。
配置好的.config文件也可以留到以后版本的Linux源代码中使用,使用前需要执行make oldconfig进行验证、升级。
配置项CONFIG_IKCONFIG_PROC将在编译时把当前的配置文件压缩为/proc/config.gz,下次使用时,可以直接使用这个文件:
$ zcat /proc/config.gz > .config$ make oldconfig
使用make命令编译内核,可以通过>将编译过程中的控制台输出导入至文件:
$ make > ../detritus
这将把输入导入至文本文件../detritus。而如果使用/dev/null则可以在事实上关闭控制台输出(/dev/null吞噬任何对它的输入):
$ make > /dev/null
出于编译模块间依赖性的考虑make默认每次只处理一个模块的变异任务,否则可能发生大量编译错误。而Linux内核源代码的依赖性处理得很好,因此可以进行并行编译,使用make -jn同时编译n个模块。例如使用make -j32可同时启动32个模块的编译任务。
安装编译好的内核根据系统使用的boot loader不同而不同,例如在grub下,将arch/i386/boot/bzImage拷贝至/boot,重命名为vmlinuz-version,编辑/boot/grub/grub.conf,为新的内核加入启动项。使用LILO的系统则需要编辑/etc/lilo.conf。
而为内核安装新的模块则很简单,只需要执行make modules_install即可。
2.1 内核代码与普通C代码的不同之处
写内核代码和写普通应用程序的C代码主要有如下几个不同之处
- 内核代码中不能使用标准C程序库,也不能#include标准C库的头文件
- 内核源代码使用GNU C编写,而不是ANSI C
- 不像用户代码,内核源代码在执行时没有内存访问保护
- 内核缺乏对浮点操作的良好支持
- 对于内核代码,每个进程的栈空间大小是固定不可变的
- 同步、并发控制是每时每刻都需要仔细处理的问题:抢占式的异步中断处理、SMP支持、多线程无处不在
- 必须保证可移植性
2.1.1 不能使用标准C程序库
主要处于两个原因:
- 标准C程序库作为为应用程序代码设计的程序库,运行在内核之上,因此在内核代码中使用标准C程序库陷入了“先有鸡还是先有蛋”的悖论
- 内核代码需要短小精悍,加入大量的库将会使模块变得臃肿
但实际上,很多常用的libc库函数都包含在了Linux内核源代码中,例如lib/string.c中实现了常用的字符串处理函数,只需在头文件中包含<linux/string.h>即可使用它们。
Linux源代码中#include语句的基准目录是include/,例如#include <linux/inotify.h>包含的.h文件实际位于include/linux/inotify.h。
2.1.2 GNU C
要编译Linux内核源代码,需要使用gcc 3.2及以上版本,最好是4.4及以上版本。 使用内联函数提高代码执行效率。注意内联函数会增大生成的二进制代码体积。 在if语句中使用gcc分支优化指令likely()和unlikely(),例如对于if (err) {...},由于error并不经常发生,因此可以写作if(unlikely(error)) {...}使得gcc能够对其进行分支优化。
2.1.3 无内存访问保护
用户态下内存访问越界时,内核将捕获这个错误,向进程发送SIGSEGV信号,并杀死进程。但当内核代码发生访问越界时是没有这些保护措施的。因此编写内核代码时需要万分小心。
2.1.4 浮点操作支持不佳
用户态下的整点/浮点数运算操作的转换由内核完成,内核本身并不能很好地处理浮点运算操作,因此除非万不得已,不要使用浮点数,进行浮点操作。
2.1.5 固定大小的栈空间
在x86架构下,对于32位机栈空间为8KB,64位机则为16KB。
2.1.6 同步和并发控制
- Linux是抢占式的多任务操作系统,内核代码负责切换执行进程,因此必须处理同步与并发处理
- Linux支持SMP,因此内核代码可能在多个处理器上同时执行,因此
- 中断是异步发生的,程序可能在任何时候被中断,因此
- Linux内核本身的执行也可能被抢占,因此
2.1.7 可移植性
Linux是跨平台的操作系统,因此其源代码必须具有良好的可移植性。一些原则需要注意,例如对于small-endian和big-endian保持中立,对是32/64位平台都能够良好支持,对不同的内存页尺寸都能良好支持。
Footnotes:
1http://www.cnblogs.com/mdyang
File translated from TEX by TTH, version 4.01.
On 10 May 2012, 00:28.
<hr>
<p>
在微博上关注:
<a href="http://t.sina.com.cn/pythoncn">新浪</a>,
<a href="http://t.sina.com.cn/pythoncn">腾讯</a>
<a href="http://simple-is-better.com/tougao">投稿</a>
</p>
<p><strong>最新招聘</strong></p>
<ul>
<li><a href="http://simple-is-better.com/jobs/detail-330">[北京] Python开发工程师</a> - 创新工场</li>
<li><a href="http://simple-is-better.com/jobs/detail-146">[杭州] Python开发工程师(至少熟悉一个 web 框架)</a> - 杭州美巴科技有限公司</li>
<li><a href="http://simple-is-better.com/jobs/detail-328">[上海] Python开发工程师</a> - ITALKI</li>
<li><a href="http://simple-is-better.com/jobs/detail-313">[上海] Python 程序开发(Django, Scrapy, 待遇 6-15k)</a> - 若邻网</li>
<li><a href="http://simple-is-better.com/jobs/detail-219">[深圳] Python 程序开发(Django, 网络爬虫开发, 数据分析)</a> - 北京创新在线网络技术有限公司</li>
</ul>
<p><a href="http://simple-is-better.com/jobs/">更多>></a></p><img src="http://www1.feedsky.com/t1/638052723/simple-is-better/feedsky/s.gif?r=http://simple-is-better.com/news/866" border="0" height="0" width="0">