Linux 内核编程学习
常常有人问:我想学习内核,需要什么基础吗?Linus Torvalds 本人是这样回答的:你必须使用过 Linux 。 这个 …… 还是有点太泛了吧,我想下面几个基础可能还是需要的,尽管不一定必需:
1, 关于操作系统理论的最初级的知识。不需要通读并理解《操作系统概念》《现代操作系统》等巨著,但总要知道分时( time-shared )和实时( real-time )的区别是什么,进程是个什么东西, CPU 和系统总线、内存的关系(很粗略即可),等等。
2, 关于 C 语言。不需要已经很精通 C 语言,只要能熟练编写 C 程序,能看懂链表、散列表等数据结构的 C 实现,用过 gcc 编译器,就可以了。当然,如果已经精通 C 语言显然是大占便宜的。
3, 关于 CPU 的知识。这块儿可以在学习内核过程中补,但这样的话你就需要看讲解很详细的书,比方后面将会提到的《情景分析》。你是否熟悉 Intel 80386 CPU ?尝试着回答这几个问题来判断一下: 1 )说出 80386 的中断门和陷阱门的区别; 2 )说出保护模式与实模式的区别; 3 )多处理器机器上,普通的读 - 改 - 写回一块内存这样的动作,为什么需要特殊的手段来保护。等等。讲解基于其它 CPU 的 Linux 内核的书,目前好象只有一本 《IA-64 Linux 内核设计与实现》 ─ ─也还是 Intel 的,其它都是讲解基于 IA32 的。
以上算是知识方面吧,如果还要再补充一条,我想就是:动手编译过内核。
好了,我们接下来走。好多人装上Linux 之后,第一件事找到内核源码所在的路径,打开一个 C 程序文件,开始哗哗哗翻页,看看大名鼎鼎的 Linux 内核代 码到底长啥模样 ── 然后关闭。这是可理解的,但却不是学习的方法。刚开始,必须从读书入手。至少要对内核有一个 Overview 之后,才有可能带着问题去 试图阅读源代码本身。下面就讲一下我读过的几本书:
1, 《 Linux 内核设计与实现》,英文名 Linux Kernel Development (所以有人叫它 LKD ),机械工业出版社,¥ 35, 美国 Robert Love 著,陈莉君译者。 评说:
此书是当今首屈一指的入门最佳图书。作者是为2.6 内核加入了抢占的人,对调度部分非常精通,而调度是整个系统的核心,因此本书是很权威的。这本书讲解浅 显易懂,全书没有列举一条汇编语句,但是给出了整个 Linux 操作系统 2.6 内核的概观,使你能通过阅读迅速获得一个 overview 。而且对内核中较为 混乱的部分(如下半部),它的讲解是最透彻的。对没怎么深入内核的人来说,这是强烈推荐的一本书。
翻译:翻译水平、负责任程度都不错,但是印刷存在一些错误。买了此书的朋友可以参考我在Linux 高级应用版的《 Linux 内核设计与实现中文版勘误》:
http://bbs.chinaunix.net/forum/viewtopic.php?t=541234
另外,此书2005 年有了第二版,目前尚无中译本面世。我就是对照着 2nd-en 勘误 1st-cn 的。
2, 《 Linux 内核源代码情景分析》上、下。毛德操、胡希明著,浙江大学出版社,上册¥ 80, 下册¥ 70. 评说:
本书是基于2.4.0 内核的,比较早,也没听说会出第二版。上册讲解内存管理、中断、异常与系统调用、进程控制、文件系统与传统 Unix IPC ;下册讲解 socket 、设备驱动、 SMP 和引导。关于这套书的评价褒贬不一,我个人认为其深度是同类著作中最优秀的。本书基于 Intel IA32 体系,由于厚度大,很多体系上的知识都捎带讲解了,所以如果你想深入了解内核的工作机制而又不非常熟悉 Intel CPU 的体系构造,本书是最合适的。缺点是:版本较老,没有 TCP/IP 协议栈部分(它讲的 socket 只是 Unix 域协议的),图表太少,不适合初学者 入门。还有就是对学生朋友来说,可能书价偏高,这样的话可以考虑先买上册,因为上册是核心部分,下册一大部分都在讲具体 PCI/ISA/USB 设备的驱 动。
翻译:没什么翻译,作者是国人,而且行文流畅。本人书桌上诸多计算机经典图书当中,这套是唯一又经典又无阅读障碍的。
www.linuxforum.net内核版好多朋友已经把这书读到六七遍了,我很惭愧,上册差不多读熟了,下册就 SMP 部分还看过 ── 但这就花费了整整 1 年的时间,还有好多弄不懂的。这里顺便说明另外一个研究内核常见的误区:目标太庞大。要知道 Linux 内核(最新的 2.6.13 ) bzip2 压缩之后 37M ,解压缩之后 244M ,根本不是哪个人能够吃透的。即使是内核的核心开发团队中,恐怕也只 Linus Torvalds、 Alan Cox 、 David Miller 、 Ingo Molnar 寥寥数人会有比较全面的了解,其它人都是做自己专门的部分。 我自己来说,目前已经决定放弃内存管理的全部(slab 层、 LRU 、 rbtree 等)、文件系统部分、外设驱动部分,暂时也没打算弄 IA32 以外的其它体 系的部分。
3, 《深入理解 Linux 内核》第二版。中国电力出版社。也是陈莉君译。此书是 Linux 内核黑客在推荐图书时的首选。 评说:
此书C 版的 converse 兄送了我一本第一版,因此就没买第二版,比较后悔。因此只就第一版说一说,第一版基于 2.2, 第二版 2.4 。我见 O'Reilly 官方主页上说第三版的英文版将于 2005 年 11 月出版,也不知咱们何时才能见到。此书图表很多,形象地给出了关键数据结构的定义, 与《情景分析》相比,本书内容紧凑,不会一个问题讲解动辄上百页,有提纲挈领的功用,但是深度上要逊于《情景分析》。
4, 其它的几本书。市面上能见到的其它的 Linux 内核的图书,象《 Linux 设备驱动程序》、《 Linux 内核源代码完全注释》以及新出的《 Linux 内核分析及编程》等。
《Linux 设备驱动程序》第二版是基于 2.4 的,中文翻译不错,中国电力出版。这书强调动手实践,但它是讲解 “ 设备驱动 ” 的,不是最核心的东西,而且有 些东西没硬件的话无法实践,可能更适合驱动开发的程序员吧,不太适合那些 For fun and profit 的人。此书有第三版英文版,东南大学出版社影印,讲解 2.6 的,行文流畅,讲解的面也比第二版更广泛,我读过其中关于同步与互斥、内存分配的 部分,感觉很不错。
《Linux 内核源代码完全注释》(机械工业出版社)是同济大学的博士生赵炯的著作,讲解 0.1Linux 内核,我没买也没看,有看过的朋友说一说。
《Linux 内核分析及编程》(电子工业出版社)是刚刚出版的,国人写的,讲解 2.6.11 。很多人说好,但有人说不够系统,我没买,不敢评说。
还有一本清华出的《Linux 内核编程指南(第三版)》,原书应该是好书,但是翻译、排版十分糟烂,脱字跳行,根本没法看,我买了一本又扔掉了。
5, 其它资源。 TLDP ( The Linux Documentation Project )有大量文档,其中不少是关于内核的,有些是在国外出版过的,象《 Linux Kernel Interls 》《 The Linux Kernel 》《 Linux Kernel Module Programming Guide 》等,作者都是亲身参加开发的人,著作较为可信。
Http://www.linuxforum.net中国 Linux 论坛的内核版。该版是研究内核的中文 Linux 社区中水平最高的,有很多专家级别的牛人,强烈推荐去学习一下(但建议不要问太过分简单的问 题,人家脾气再好也会烦的 ^_^ ),它的置顶贴简直是一个包罗万象的 FAQ ,精华区也有很多资料。只可惜太过曲高和寡,人气不是很旺。
6, 一本不是讲解 Linux 的书:《现代体系结构上的 Unix 系统:内核程序员的 SMP 和 Caching 技术》,人民邮电出版社 2003 版,定价¥ 39. 本书虽然不是讲解 Linux ,但是对所有 Unix 内核都是适用的,适合对 SMP 和 CPU 的 Cache 这些组成原理知识不是很熟的朋友,而且是很多国外牛人 推荐的书。中文版翻译非常负责。
还有个很重要的问题:怎样浏览内核源代码。有的朋友喜欢在Windows 上工作,用 Source Insight ;有的在 Linux ,用 Source Navigator ;还有专门浏览源代码的软件,象 lxr ( Linux Cross Reference );还有用 ctags/ectags/cscope 等,这些都是很优秀的软件。我个人用 Vim ctags 浏览(参考了 www.linuxforum.net 内核版 wheelz 大侠的文档,)。
此外,前边已经提到的一个重要的问题是:你研究内核的目的是什么, 开发? 乐趣?如果是开发,而且是国内做开发,把kernel API 熟悉一下就差不太多了(你也知道国内的水平有多差),比方说 copy_from_user() 、 kmalloc() 函数等, kernel API 在 Internet 上找得到,编译内核时也可以用 DocBook 生成(具体请参考内核源代码包下的 README 文件);如果是研究,那就差别很大 了,需要下很大的苦功:会用 kmalloc() 绝不说明你懂得 Linux 内核的虚存管理子系统,正如同会讲汉语不说明你懂中国文化一样。
说完了,发现前面讲的太罗嗦了,简化一下:
1. 动手编译内核
2. 精读《Linux 内核设计与实现》
3. 上www.linuxforum.net 内核版看置顶贴与精华区
此外就凭自己兴趣选择吧。
下面是一篇没写完的《Linux 内核模块编程入门》,不补写了,将就着看吧。
Linux内核模块编程范例
看到昨天有好几个问linux 内核编程问题的帖子,不少是卡在了入门问题上,就整理一下入门的初步流程。针对 2.6 内核的 Linux 系统,需要你的机器上已经安装了 kernel-devel 这个包,也就是编译模块所必须的东西:内核的头文件和一些 Makefile 。
一、Hello World 程序
/*file: hello.c*/
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include
#include
#include
static int hello_init(void)
{
printk(KERN_ALERT "Hello, The fucking crazy world/n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Bye, The fucking crazy world!/n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("albcamus ");
2.6内核的 kbuild 子系统跟 2.4 相比有本质的改变。我们下面尝试两种方式编译这个程序:
1, 你可以在本目录下这样写一个 Makefile
obj-m := hell.o
clean:
rm -rf *.o .*.cmd *.ko *.mod.c .tmp_versions
然后用这样的命令行编译:
make -C /lib/modules/`uname -r`/build M=`pwd` modules
这时ls 一下,就能看到生成了很多文件,其中 hello.ko 就是我们需要的内核模块。
2, 专业点儿, Makefile 这样写:
obj-m := hello.o
KERNELBUILD := /lib/modules/`uname -r`/build
default:
make -C $(KERNELBUILD) M=$(shell pwd) modules
clean:
rm -rf *.o .*.cmd *.ko *.mod.c .tmp_versions
然后只要make 一下就可以了。
插入模块用insmod 命令:
insmod ./hello.ko
这时候大家可能会问:为什么我的屏幕上没有见到输出?这个是console 的日志记录级别和你 printk 消息时指定的级别(本例中指定为 KERN_ALERT ,为次高,仅次于 KERN_EMERG )决定的。无论如何,你可以 tail 或者 cat 看看系统日志的最后几行,系统日志一般为 /var/log/messages ,或者直接用 dmesg 命令,肯定能看到输出了。
二 、 头文件问题
C程序员都知道,要使用某个外部的函数,应当 #include 某个头文件,这个头文件包含了那个函数的原型 (prototype) 。内核的头文件在 include/ 下,其中 include/asm 是个符号链接,指向你所用内核的具体的体系结构目录,比方说我的系统是 i386 的,那么 include /asm 就指向 include/asm-i386 。
内核编程中我们不能链接libc 库,不能使用 libc 库中的函数,所以很有些麻烦。一些重要的函数,象 strcpy/strcmp/snprintf 等, kernel 也为我们实现并导出( export )了,而我们需要 #include 相关的头文件,在 include/linux 和 include /asm 中,你需要自己寻找你所要使用的函数在哪个头文件中声明,并将其 #include 进来。