本书作者在整理自己多年研发笔记的基础上,以精心挑选的典型开发实例向读者详细地讲述了内核源代码的各部分结构、原理及组成框架,主要分析了Linux最新版本(2.6.11)的内核源代码,帮助读者深入理解Linux 内核,精通Linux内核编程。全书分为20章,内容包括进程管理、进程间通信、内存管理、文件系统、I/O接口及资源管理、内核的编译及调试原理、网络通信、内核安全、USB驱动程序等。
对于想了解Linux开发,以及从事Linux内核编程的开发人员来说,本书是一本集大成之作,它既有讲解透彻的原理,也有详细实用的示例,更有作者多年从事实际开发工作的心得。本书主要针对从事Linux内核编程的中高级读者及软件工程师,也很合适作为大学教材和参考书。
第1章 数据类型及链表 1
1.1 数据类型所占空间 1
1.2 有关移植性的其他问题 3
1.2.1 时间间隔 3
1.2.2 页面大小 3
1.2.3 字节存储顺序 3
1.2.4 数据对齐 4
1.3 内核通用链表 4
1.3.1 hlist哈希链表 7
1.3.2 RCU操作保护的链表 8
1.4 AT&T的汇编格式 9
1.5 内核中的时间延迟 11
第2章 进程及进程调度 13
2.1 进程结构 13
2.2 进程创建 24
2.2.1 对象缓存的分配 24
2.2.2 系统调用sys_fork 25
2.3 内核线程 26
2.4 工作队列 27
2.4.1 工作队列的结构及宏定义 28
2.4.2 工作队列的建立 29
2.5 进程调度 33
2.5.1 runqueue结构 34
2.5.2 进程调度初始化 36
2.5.3 负载平衡的启动 38
2.5.4 负载平衡的方法 42
2.5.5 函数schedule分析 46
2.5.6 调度器的实时性能 51
2.6 Linux内核抢占 51
第3章 内核同步机制 55
3.1 内核中的互斥机制 55
3.1.1 自旋锁 55
3.1.2 原子操作 59
3.1.3 信号量 60
3.2 RCU 64
3.2.1 RCU原理介绍 64
3.2.2 RCU应用实例 66
3.2.3 RCU相关数据结构 67
3.2.4 内核RCU机制的建立 68
3.2.5 RCU回调处理 73
3.3 内核与用户空间的通信机制 74
3.3.1 热插拔操作 74
3.3.2 内核发消息到用户空间通信机制 75
3.3.3 内核空间调用用户空间程序 78
第4章 内存管理 81
4.1 内存地址类型和内存保护 82
4.1.1 地址类型 82
4.1.2 内存保护 83
4.2 80386的段页式管理机制 84
4.2.1 描述符及分段 84
4.2.2 物理内存分页机制 85
4.3 IA-64 Linux地址空间划分 86
4.4 进程的内存组织 88
4.4.1 内存管理的数据结构 88
4.4.2 VMA在/proc文件系统中的显示 90
4.5 虚拟内存管理 91
4.5.1 大容量对象缓存 91
4.5.2 内存映射 94
4.5.3 物理内存的反向映射 110
4.5.4 虚拟内存的加锁和保护 113
4.6 物理内存管理 114
4.6.1 物理内存的结构 114
4.6.2 物理页位图 116
4.6.3 物理内存的初始化过程 117
4.6.4 物理页面的分配和回收 121
4.6.5 缓存及slab 125
4.6.6 缓存分配的应用 129
4.6.7 分配缓存函数的分析 129
4.6.8 交换空间 135
4.6.9 请页机制 137
4.6.10 守护进程kswapd 139
4.6.11 内存管理相关的高速缓存 144
4.6.12 内存缓冲池 144
4.6.13 大块内存页 147
第5章 虚拟文件系统 149
5.1 VFS的超级块、dentry和节点结构 150
5.2 与进程联系的文件系统相关结构 153
5.3 系统有关操作函数集的结构 155
5.3.1 super_operations 155
5.3.2 inode_operations 156
5.3.3 file_operations 156
5.3.4 dquot_operations 157
5.4 文件系统的建立过程 157
5.5 文件系统的注册、安装与卸载 159
5.5.1 文件系统的注册 159
5.5.2 文件系统的安装与卸载 160
5.6 文件系统的系统调用过程 160
5.6.1 系统调用open 161
5.6.2 read系统调用 170
5.7 文件系统的各种缓存 172
5.7.1 块缓存buffer 172
5.7.2 inode缓存 182
5.7.3 目录条目dentry缓存 185
5.8 缓存同步操作——sys_sync系统调用 189
5.8.1 多个节点同步回写操作函数
5.8.1 sync_inodes 189
5.8.2 单个节点同步回写操作函数sync_inodes_sb 190
5.8.3 节点地址空间数据回写操作函数 194
5.8.4 块设备节点映射的数据同步回写
5.8.4 函数sync_blockdev 200
5.9 pdflush线程池 203
5.9.1 pdflush线程池的实现 203
5.9.2 pdflush线程使用实例
5.9.2 ——wakeup_bdflush 206
5.10 限额机制 207
第6章 EXT2文件系统 208
6.1 EXT2文件系统的几个数据结构 210
6.1.1 EXT2超级块 210
6.1.2 EXT2超级块信息结构 211
6.1.3 超级块的操作函数结构 212
6.1.4 EXT2的索引节点inode 212
6.1.5 EXT2文件系统的节点信息结构 214
6.1.6 节点操作函数结构 215
6.1.7 文件操作函数结构 215
6.1.8 EXT2文件系统的组描述符 215
6.2 EXT2文件系统建立过程 215
6.3 ext2_read_inode函数分析 220
6.4 ext2_write_inode函数分析 221
6.5 文件的读写 223
6.6 文件扩展时的数据块分配策略 228
6.7 EXT2的目录项及文件的定位 234
6.8 链接文件 237
第7章 其他文件系统 238
7.1 ramfs内存文件系统 238
7.1.1 ramfs文件系统模块初始化 238
7.1.2 ramfs文件系统操作函数集 240
7.1.3 文件读写操作 240
7.1.4 目录及节点操作函数集 241
7.2 /proc文件系统 242
7.2.1 /proc文件系统在调试中的作用 243
7.2.2 /proc文件系统实现分析 245
7.2.3 在/proc中读写设备信息示例 250
7.3 VFAT文件系统 255
7.3.1 FAT文件系统的组成 255
7.3.2 引导记录区DBR及定义 256
7.3.3 FAT文件系统结构定义 260
7.3.4 VFAT文件系统的注册超级块 261
7.3.5 超级块操作函数集的实现 264
7.3.6 目录操作函数集 265
7.4 Devfs文件系统 270
7.5 sysfs文件系统 275
7.5.1 内核对象相关结构 276
7.5.2 sysfs文件系统的建立过程 277
7.5.3 sysfs提供给对象模型的调用函数 278
7.5.4 sysfs建立bus子系统 280
7.5.5 bus子系统的接口函数 282
7.5.6 在sysfs中建立pci目录示例 283
第8章 I/O端口资源管理 288
8.1 I/O资源的描述 288
8.1.1 内存屏障 289
8.1.2 资源管理函数 290
8.2 中断处理 295
8.2.1 硬件提供的中断机制 295
8.2.2 Linux的中断处理 297
8.2.3 中断向量的设置和相关数据的
8.2.3 初始化 298
8.2.4 中断处理全过程 299
8.2.5 tasklet机制 303
8.2.6 中断处理在/proc文件系统中的报告 311
8.2.7 并口中断处理程序示例 311
8.3 DMA 315
8.3.1 DMA控制器硬件结构 315
8.3.2 DMA通道使用的地址 316
8.3.3 DMA操作函数 317
8.3.4 DMA映射 318
8.3.5 DMA池 321
8.3.6 一个简单的使用DMA例子 324
8.4 电源管理 325
8.4.1 ACPI规范介绍 326
8.4.2 ACPI的一些基本概念 328
8.4.3 ACPI的运行 329
8.4.4 ACPI驱动程序分析 332
8.4.5 pci的ACPI电源管理的实现 337
8.4.6 APM电源管理模式 341
第9章 模块机制 348
9.1 简单模块示例 348
9.2 内核空间和用户空间 349
9.2.1 处理器保护级 349
9.2.2 用户空间和内核空间权限 350
9.2.3 用户空间和内核空间范围及函数
9.2.3 参数传递 350
9.2.4 内核态和用户态之间数据传递 352
9.3 模块的使用过程 353
9.4 实现机制 354
9.4.1 模块在/proc文件系统中的显示 354
9.4.2 模块结构 354
9.4.3 模块数据宏操作 356
9.4.4 实现函数的分析 359
9.5 modutils介绍 369
第10章 设备驱动程序 371
10.1 设备文件及设备访问方式 372
10.1.1 轮询与中断 372
10.1.2 直接内存访问(DMA) 372
10.1.3 设备驱动使用内存 372
10.1.4 设备文件及接口 372
10.2 设备驱动程序模型 374
10.2.1 驱动模型中的描述结构 374
10.2.2 驱动程序向新的模型上迁移 383
10.2.3 即插即用 386
10.2.4 文件系统中与设备驱动相关
10.2.4 的结构 389
10.3 字符设备操作过程 390
10.4 块设备伪文件系统 393
10.4.1 块设备文件系统初始化 393
10.4.2 文件操作函数集 394
10.5 通用硬盘GENHD 398
10.6 通用块层 403
10.6.1 bio相关结构 404
10.6.2 bio_vec池 405
10.6.3 碎片链表 406
10.6.4 请求及请求队列结构 407
10.6.5 通用的命令标志请求 410
10.6.6 I/O调度器 411
10.7 块设备的读写请求队列及提交过程 415
10.7.1 初始化块设备的请求队列 415
10.7.2 块设备读写请求的传递过程 417
10.8 IOCTL设备控制操作 423
10.9 编写设备驱动程序的基本步骤 425
10.9.1 如何添加一个字符设备 425
10.9.2 如何添加一个块设备 425
第11章 FLASH闪存及SD/MMC卡设备
第11章 驱动程序 427
11.1 MTD内存技术设备 427
11.1.1 MTD内存技术设备层次结构 428
11.1.2 设备层和原始设备层的函数
11.1.2 调用关系 430
11.1.3 MTD相关结构 430
11.1.4 MTD块设备初始化 432
11.1.5 MTD块设备的读写操作 439
11.1.6 MTD核心初始化 442
11.1.7 MTD字符设备 443
11.1.8 具体flash芯片的探测及映射 444
11.1.9 驱动程序实例分析 447
11.2 SD/MMC卡块设备驱动程序 449
11.2.1 MMC抽象设备层相关结构 449
11.2.2 MC抽象设备层MMC块设备
11.2.2 驱动程序 453
11.2.3 具体MMC控制器驱动程序示例 462
第12章 Linux系统初始化 468
12.1 Boot Loader 468
12.1.1 PC的Boot Loader 468
12.1.2 嵌入式系统Boot Loader 473
12.2 Linux内核启动过程 478
第13章 系统调用 481
13.1 设定0x80号中断 481
13.2 系统调用现场保护 482
13.3 Linux系统调用的流程 484
13.3.1 系统调用过程 484
13.3.2 中断INT 0x80入口处理 484
第14章 Linux网络系统分层结构 488
14.1 Linux网络系统分层结构 488
14.2 数据包结构 489
14.2.1 msghdr结构 489
14.2.2 socket结构 490
14.2.3 sk_buff结构及管理 490
14.2.4 sock结构 495
14.3 sockfs文件系统 497
14.4 利用socket通信 499
14.4.1 socket层 500
14.4.2 IP层收发数据包函数 506
14.4.3 网络核心层 513
14.5 网卡驱动程序 525
14.5.1 NAPI 525
14.5.2 8139CP网卡驱动程序 526
14.6 netlink 533
14.6.1 内核netlink调用函数 535
14.6.2 示例 536
第15章 执行文件的运行过程 544
15.1 动态链接与静态链接 544
15.2 位置无关代码(PIC)的汇编
15.2 语言编程 548
15.3 可执行文件格式 550
15.3.1 a.out文件格式分析 550
15.3.2 COFF文件格式分析 551
15.3.3 ELF文件格式分析 552
15.3.4 符号的重定位 557
15.3.5 ELF文件加载过程 558
15.4 可执行文件加载代码分析 559
第16章 进程间通信 567
16.1 管道 567
16.2 消息队列 575
16.2.1 消息队列结构 575
16.2.2 消息队列文件系统 576
16.2.3 消息队列系统调用函数 579
16.3 共享内存 585
16.3.1 共享内存相关结构 586
16.3.2 tmpfs文件系统 587
16.3.3 共享内存系统调用 593
16.4 信号 599
16.4.1 信号相关的结构 600
16.4.2 设置信号响应 601
16.4.3 信号分发 603
16.4.4 信号响应 607
16.5 用户空间信号量操作 610
16.5.1 信号量相关结构 610
16.5.2 系统调用函数的实现 611
第17章 Linux的安全策略 618
17.1 Linux常用安全技术 618
17.1.1 PAM机制 618
17.1.2 入侵检测系统 618
17.1.3 加密文件系统 619
17.1.4 安全审计 620
17.1.5 基于ACL的自主访问控制 620
17.1.6 强制访问控制 621
17.1.7 防火墙 621
17.2 Linux能力机制 621
17.3 Flask安全体系结构概述 622
17.4 SE Linux安全策略配置语言 624
17.4.1 基本概念 625
17.4.2 Linux与SE Linux在安全管理
17.4.2 上的区别 626
17.4.3 安全模型 626
17.4.4 策略语言及配置样例 626
17.5 SELinux的内部结构 634
17.6 SELinux的实现 636
17.6.1 任务的安全管理 637
17.6.2 AVC分析 640
17.6.3 security_compute_av函数 644
17.7 策略库的结构 647
17.7.1 sidtab结构 648
17.7.2 symtab结构 649
17.7.3 avtab结构 649
17.7.4 class_datum结构 649
17.7.5 role_datum结构 650
17.7.6 user_datum结构 651
17.7.7 role_tran结构 651
17.7.8 cond_node结构 652
17.8 安全审计的管理 653
17.9 sel_fs文件系统 654
17.10 防火墙 660
17.10.1 Netfilter框架 661
17.10.2 iptables管理工具 662
17.10.3 Netfilter例子 663
第18章 内核配置与编译 664
18.1 配置文件的生成 664
18.2 配置语言 665
18.3 主Makefile分析 667
18.3.1 主Makefile中的分析 667
18.3.2 嵌入式内核的交叉编译 671
18.4 Rule.make及子目录编译 673
18.4.1 编译选项变化引起增量编译 673
18.4.2 子目录的编译 673
18.4.3 Rule.make分析 674
18.4.4 驱动程序配置示例 680
第19章 Linux内核调试 683
19.1 strace命令 683
19.2 oops消息分析 683
19.3 调试工具 684
19.4 printk打印调试 688
19.4.1 printk 688
19.4.2 如何记录消息 689
19.4.3 sys_syslog系统调用 690
19.4.4 printk函数分析 692
19.4.5 控制台 694
19.4.6 tty代码分析 695
19.4.7 tty_register_ldisc函数 701
19.5 ptrace调试跟踪 702
19.5.1 调试寄存器 702
19.5.2 TSS中的调度陷阱 704
19.5.3 INT3 704
19.5.4 程序的单步执行 705
19.5.5 ptrace系统调用 705
19.5.6 系统调用跟踪 710
19.5.7 调试陷阱处理 711
19.5.8 调试器运行方法 712
第20章 USB总线驱动程序 715
20.1 USB的拓朴结构 715
20.2 USB 2.0协议 717
20.2.1 包标志符及传输控制概述 717
20.2.2 总线枚举 718
20.2.3 USB设备请求 719
20.2.4 描述符 719
20.2.5 OTG规范 720
20.3 USB总线驱动程序结构 722
20.3.1 USB主机驱动程序的体系 722
20.3.2 USB驱动程序的编写 723
20.3.3 设备结构间的关系 725
20.4 USB驱动程序初始化 727
20.5 usbfs文件系统 729
20.5.1 usbfs文件系统初始化 729
20.5.2 usbfs文件操作 731
20.6 USB请求块(URB) 732
20.6.1 URB结构 732
20.6.2 URB的操作 733
20.7 同步消息处理 735
20.7.1 同步请求完成模型 736
20.7.2 控制与查询 737
20.8 用主机控制器驱动层(HCD层) 737
20.8.1 USB总线的注册与注销 738
20.8.2 HCD操作函数 739
20.8.3 注册根集线器 741
20.9 集线器Hub 741
20.9.1 Hub初始化 742
20.9.2 Hub设备的各种事件处理 744
20.9.3 ehci-hcd控制器 752
20.10 USB大存储设备 758
20.10.1 Bulk-Only传输协议 759
20.10.2 SCSI体系结构模型及命令描述块 761
20.10.3 大存储类主机驱动程序 765
20.11 USB从设备驱动程序(Gadget) 779
20.11.1 Gadget相关结构 781
20.11.2 Gadget API 783
20.11.3 pxa2xx控制器 786
20.11.4 gadgetfs文件系统 794
20.11.5 大存储设备驱动程序 804
附录A Linux系统调用 819
主要参考文献 823
Linux是开放的源代码,它具备了UNIX的全部特征,它还同POSIX标准兼容。Linux操作系统,如Red hat Linux 9,被广泛地应用于PC、服务器,还广泛地用于手机、PDA等高端嵌入设备。由于Linux综合了UNIX主要派生系统(包括SysV、BSD)的先进技术,因而,Linux操作系统上能运行原UNIX系统的各种应用程序,同时,还存在大量的应用程序开放源代码供开发者使用。而且,许多著名公司将自己的Linux程序源代码进行发布。这些因素导致了Linux在嵌入系统中的大量应用。
为什么写作本书
如今,Linux内核代码几乎是每个软件工程师必读的,但是内核代码复杂难懂。作为一名Linux编程者,我一向颇为留意内核编程方面的图书,我理想中的这本书应该是一本实践性很强的书,是真正从事Linux内核开发的人士写作的。由于我多年以来一直记有从事Linux内核开发的笔记,后来,我便想到,如果我从一名研发者的角度来写作这样一本书,把自己的笔记加以整理,那么,对读者的实践应该会有不小的帮助。在整理自己的开发笔记的基础上,我还查阅了大量相关资料,加强研究,力求融会贯通,费时两年,写成这本书。
Linux 2.6版内核改写了以前版本内核的绝大部分,本书主要针对目前Linux的最新版本2.6.11版,对以后的新版本也具有普适性。本书的主要目的是帮助软件工程师读懂linux2.6.11版本内核,并能开发各种驱动程序、编写内核模块。在这本书里,我将平常编程中遇到的重点、难点进行分析,并给予充分的论述,相信其中许多问题是其他内核编程者也会遇到的。对于一名软件工程师来说,本书有助于他们少走弯路,更快地掌握Linux 2.6.11内核源代码及编程技巧。
关于本书作者
笔者从在清华大学电子系读研究生起,就开始从事Linux内核编程,后又一直在外国著名公司从事Linux内核编程工作。先后从事过Linux内核的移植、USB驱动程序编写、内核安全程序的编写等。目前,Linux被广泛地应用于手机、PDA等高端嵌入设备,笔者在这方面有丰富的开发经验。
本书主要内容
本书共包括20章,每章的主要内容如下:
第1章“数据类型及链表”介绍了数据类型占用的空间及用户空间输出的数据类型,说明了内核通用链表的原理,还介绍了行内汇编语言的语法。
第2章“进程及进程调度”分析了进程结构及进程调度算法。
第3章“内核同步机制”介绍了内核的互斥机制:自旋锁、原子操作和信号量。还说明了RCU读写机制,及内核与用户空间进行通信的机制。
第4章“内存管理”介绍了虚拟内存及映射,还分析了物理内存的管理(它包括缓存的分配及回收,请页机制,交换空间等),还说明了内存缓冲池和大块内存的管理机制。
第5章“虚拟文件系统”介绍了虚拟文件系统的结构和实现文件操作的机制,还介绍了底层各种缓存的管理。
第6章“EXT2文件系统”介绍了EXT2文件系统逻辑分区的结构、节点和dentry的管理,说明了读写系统调用的具体实现过程。
第7章“其他文件系统”介绍了常用的一些文件系统:ramfs、proc、vfat、devfs和sysfs。
第8章“I/O端口资源管理”介绍了中断处理、DMA及电源管理的实现机制。
第9章“模块机制”介绍了内核模块的实现机制,还分析了内核空间的保护机制。
第10章“设备驱动程序”介绍了字符设备及块设备驱动程序的工作原理,分析了通用硬盘及块层的机制,还说明了如何编写字符设备与块设备驱动程序。
第11章“Flash闪存及SD/MMC卡”分析了MTD设备驱动程序和MMC/SD驱动程序,它们分别驱动Flash闪存和存储卡,是嵌入设备的主要存储设备。
第12章“Linux系统初始化”阐述了在i386机器上的BootLoader和嵌入设备上的Blob,分析了Linux系统的启动过程。
第13章“系统调用”分析了系统调用的实现机制。
第14章“Linux网络系统分层结构”介绍了数据包的传递过程、与应用层的接口、与底层的接口以及网络驱动程序的编写。
第15章“执行文件的运行过程”阐述了动态链接与静态链接的概念,并说明了动态链接中函数定位的原理。然后分析了ELF文件格式,以及ELF文件在内核中是如何加载运行的。
第16章“进程间通信”分析了进程间通信机制在内核中的实现原理。
第17章“Linux的安全策略”介绍了selinux的安全策略配置语言、selinux模块的实现,以及病毒防火墙的原理。
第18章“内核配置与编译”说明了内核的配置、配置语言的语法,还分析了makefile是如何进行内核编译的。
第19章“Linux内核调试”分析了内核调试的方法,控制台驱动程序以及如何将打印信息显示在控制台上,阐述了日志系统是如何工作的,还说明了ptrace调试跟踪的原理。
第20章“USB总线驱动程序”分析了USB总线接口驱动程序(包括USB总线驱动程序的结构、编写方法和USB接口的U盘设备驱动程序)。
附录A列表说明了系统调用的功能,供读者快速查询。
如何阅读本书
这是一本大厚书,读者应该怎样利用这本书呢?
在阅读此书前,读者应当学过操作系统原理、数据结构、计算机结构及组成、汇编语言及C语言等课程或具备这方面的知识。这本书章节的安排是依据读者对内核学习循序渐进的顺序设立的,建议初学者从前至后阅读。由于Linux内核复杂难懂,我建议读者分几遍阅读本书。
第一遍先将书通读,主要弄清楚概念,程序代码部分可以只是浏览一下。当对概念有初步认识时,再尝试编译安装内核,安装一个驱动程序模块。
第二遍再对照源代码详细看一个驱动程序,如:USB驱动程序,理解驱动程序模块是如果调用内核函数、注册驱动程序等,再反过来,仔细看这些内核函数的实现,就明白了内核为什么要写这些函数了。
第三遍再根据需要对照源程序看相关章节,例如:对照源程序看“内存管理”一章中vmolloc函数是如何实现的。这种阅读法使读者能从本书中获得最大收效,是学习内核的好方法。当然,如果你是一名内核精通者,也可以根据需要直接跳读到相关章节,查阅你需要的内容。
阅读内核是一个反复又枯燥的过程,读者只有在反复的研读中,才能逐渐使自己的内核知识条理化,在此基础上,你还需要去应用这些知识,比如,你可以尝试写一个驱动程序、系统调用或文件系统,在实践的过程中再反复查阅参考书及源代码,这样才能达到掌握内核知识的目的。
致谢
我首先要特别感谢我的妻子邓咏秋,我记录开发笔记的初衷只是总结自己的经验和收获,供自己使用,或与朋友分享,是她促成了这本书的出版,使这本书能与千千万万读者分享。她在撰写博士学位论文的同时,热情而高效率地充当着我的“经纪人”,帮我承担了与出版社联系的大部分工作,使我能够全身心投入到书稿写作中去。
我还要真诚地感谢三家优秀的计算机图书出版社:电子工业出版社、清华大学出版社和人民邮电出版社,他们都愿意出版我的这本书。在此,我非常感谢他们对这本书的认可和兴趣。
电子工业出版社计算机图书事业部主任郭立女士的热情推动最终促成了我们与电子工业出版社的合作。她有敏锐的市场眼光,以专业的营销数据为我们分析了这本书的市场前景,对于作者的种种顾虑总是愿意真诚的倾听,并且能从作者角度替我们分析问题。在此书写作过程中,她多次邀请专家对此书提出有益意见,对于此书的修改完善起到了重要作用。很感谢郭立女士和电子工业出版社对本书的重视,以及她们为本书出版所做的一切。
由于作者水平有限,书中不足及错误之处在所难免,敬请专家和读者给予批评指正。
谨以此书献给正在从事和将要从事Linux内核编程的人们。
倪继利
2005年7月