在线客服
Linux环境编程:从应用到内核图书
人气:27

Linux环境编程:从应用到内核

Linux环境编程的进阶指导,帮助应用开发人员快速深入内核,解析Linux接口的工作原理,清楚掌握Linux系统运行机制

内容简介

《UNIX环境高级编程》(简称APUE)几乎是Linux领域程序员人手必备的一本书。但在掌握和理解APUE的内容后,又该如何继续提高自己的技能, 如何更深入地理解Linux环境编程及其背后的工作机制呢?本书将从一个全新的角度带领读者重新进入Linux环境编程,从应用出发,深入内核源码,研究 Linux各接口的工作机制和原理,让读者不仅知其然,还知其所以然。作为Linux开发工程师,如果不仅掌握Linux的应用层开发,同时还熟悉 Linux的内核源码,那么其在Linux环境下设计开发任何产品都将游刃有余,稳定且高效。

本书是Linux技术专家高峰和李彬的合力之 作,是两个人多年开发经验的总结和分享,也是市场上一本将Linux应用态与内核态相结合的技术图书,选择这种写作方式是为了向APUE的作者致敬。 本书涵盖了APUE中大部分章节的内容,并针对Linux环境,以作者多年经验,详细解析了Linux常用接口的使用方法和陷阱。为了让读者更清楚地理解 接口的工作原理,对于绝大部分接口,作者都会深入C库或内核源码进行分析。希望本书可以帮助读者打通Linux环境的应用和内核两条脉络,使两条线融 会贯通,进一步提高开发水平。

作者简介

高峰,北京理工大学通信与信息系统专业硕士学位。毕业后在A10 Networks公司工作六年多,任职Staff Software Engineer,目前在创业公司全讯汇聚(爱快路由)担任技术总监。多年来一直专注于网络领域,熟悉Linux内核、应用及服务端的设计、开发和架构, 对TCP/IP网络协议有深刻的认识和理解。编码功力深厚,知识领域广博,擅长产品的性能改进和调优。撰写过大量技术文章,并为多个知名开源项目贡献过代 码。

李彬,东南大学信号与信息处理专业硕士。毕业后先后任职中兴通讯、趋势科技,目前在存储公司Bigtera担任SEG部门技术负 责人。一直专注于Linux平台下的开发,多年分布式存储开发经验,熟悉Linux内核,编程基本功扎实,对性能优化、bug定位有异乎寻常的爱好,属于 “死磕派”研发工程师。喜欢技术分享和交流,在社区和公司内部分享过大量技术文章。

目录

前 言

第0章 基础知识1

0.1 一个Linux程序的诞生记1

0.2 程序的构成2

0.3 程序是如何“跑”的4

0.4 背景概念介绍5

0.4.1 系统调用5

0.4.2 C库函数6

0.4.3 线程安全7

0.4.4 原子性9

0.4.5 可重入函数9

0.4.6 阻塞与非阻塞11

0.4.7 同步与非同步11

第1章 文件I/O12

1.1 Linux中的文件12

1.1.1 文件、文件描述符和文件表12

1.1.2 内核文件表的实现13

1.2 打开文件14

1.2.1 open介绍14

1.2.2 更多选项15

1.2.3 open源码跟踪16

1.2.4 如何选择文件描述符17

1.2.5 文件描述符fd与文件管理结构file18

1.3 creat简介19

1.4 关闭文件19

1.4.1 close介绍19

1.4.2 close源码跟踪19

1.4.3 自定义files_operations21

1.4.4 遗忘close造成的问题22

1.4.5 如何查找文件资源泄漏25

1.5 文件偏移26

1.5.1 lseek简介26

1.5.2 小心lseek的返回值26

1.5.3 lseek源码分析27

1.6 读取文件29

1.6.1 read源码跟踪29

1.6.2 部分读取30

1.7 写入文件31

1.7.1 write源码跟踪31

1.7.2 追加写的实现33

1.8 文件的原子读写33

1.9 文件描述符的复制34

1.10 文件数据的同步38

1.11 文件的元数据41

1.11.1 获取文件的元数据41

1.11.2 内核如何维护文件的元数据42

1.11.3 权限位解析43

1.12 文件截断45

1.12.1 truncate与ftruncate的简单介绍45

1.12.2 文件截断的内核实现45

1.12.3 为什么需要文件截断48

第2章 标准I/O库50

2.1 stdin、stdout和stderr50

2.2 I/O缓存引出的趣题51

2.3 fopen和open标志位对比52

2.4 fdopen与fileno55

2.5 同时读写的痛苦56

2.6 ferror的返回值57

2.7 clearerr的用途57

2.8 小心fgetc和getc60

2.9 注意fread和fwrite的返回值60

2.10 创建临时文件61

第3章 进程环境66

3.1 main是C程序的开始吗66

3.2 “活雷锋”exit70

3.3 atexit介绍75

3.3.1 使用atexit75

3.3.2 atexit的局限性76

3.3.3 atexit的实现机制77

3.4 小心使用环境变量78

3.5 使用动态库80

3.5.1 动态库与静态库80

3.5.2 编译生成和使用动态库80

3.5.3 程序的“平滑无缝”升级82

3.6 避免内存问题84

3.6.1 尴尬的realloc84

3.6.2 如何防止内存越界85

3.6.3 如何定位内存问题86

3.7 “长跳转”longjmp90

3.7.1 setjmp与longjmp的使用90

3.7.2 “长跳转”的实现机制91

3.7.3 “长跳转”的陷阱93

第4章 进程控制:进程的一生96

4.1 进程ID96

4.2 进程的层次98

4.2.1 进程组99

4.2.2 会话102

4.3 进程的创建之fork()103

4.3.1 fork之后父子进程的内存关系104

4.3.2 fork之后父子进程与文件的关系107

4.3.3 文件描述符复制的内核实现110

4.4 进程的创建之vfork()115

4.5 daemon进程的创建117

4.6 进程的终止119

4.6.1 _exit函数119

4.6.2 exit函数120

4.6.3 return退出122

4.7 等待子进程122

4.7.1 僵尸进程122

4.7.2 等待子进程之wait()124

4.7.3 等待子进程之waitpid()126

4.7.4 等待子进程之等待状态值129

4.7.5 等待子进程之waitid()131

4.7.6 进程退出和等待的内核实现133

4.8 exec家族141

4.8.1 execve函数141

4.8.2 exec家族142

4.8.3 execve系统调用的内核实现144

4.8.4 exec与信号151

4.8.5 执行exec之后进程继承的属性152

4.9 system函数152

4.9.1 system函数接口153

4.9.2 system函数与信号156

4.10 总结157

第5章 进程控制:状态、调度和优先级158

5.1 进程的状态158

5.1.1 进程状态概述159

5.1.2 观察进程状态171

5.2 进程调度概述173

5.3 普通进程的优先级181

5.4 公平调度的实现186

5.4.1 时间片和虚拟运行时间186

5.4.2 周期性调度任务190

5.4.3 新进程的加入192

5.4.4 睡眠进程醒来198

5.4.5 唤醒抢占202

5.5 普通进程的组调度204

5.6 实时进程207

5.6.1 实时调度策略和优先级207

5.6.2 实时调度相关API211

5.6.3 限制实时进程运行时间213

5.7 CPU的亲和力214

第6章 信号219

6.1 信号的完整生命周期219

6.2 信号的产生220

6.2.1 硬件异常220

6.2.2 终端相关的信号221

6.2.3 软件事件相关的信号223

6.3 信号的默认处理函数224

6.4 信号的分类227

6.5 传统信号的特点228

6.5.1 信号的ONESHOT特性230

6.5.2 信号执行时屏蔽自身的特性232

6.5.3 信号中断系统调用的重启特性233

6.6 信号的性236

6.6.1 信号的性实验236

6.6.2 信号性差异的根源240

6.7 信号的安装243

6.8 信号的发送246

6.8.1 kill、tkill和tgkill246

6.8.2 raise函数247

6.8.3 sigqueue函数247

6.9 信号与线程的关系253

6.9.1 线程之间共享信号处理函数254

6.9.2 线程有独立的阻塞信号掩码255

6.9.3 私有挂起信号和共享挂起信号257

6.9.4 致命信号下,进程组全体退出260

6.10 等待信号260

6.10.1 pause函数261

6.10.2 sigsuspend函数262

6.10.3 sigwait函数和sigwaitinfo函数263

6.11 通过文件描述符来获取信号265

6.12 信号递送的顺序267

6.13 异步信号安全272

6.14 总结275

第7章 理解Linux线程(1)276

7.1 线程与进程276

7.2 进程ID和线程ID281

7.3 pthread库接口介绍284

7.4 线程的创建和标识285

7.4.1 pthread_create函数285

7.4.2 线程ID及进程地址空间布局286

7.4.3 线程创建的默认属性291

7.5 线程的退出292

7.6 线程的连接与分离293

7.6.1 线程的连接293

7.6.2 为什么要连接退出的线程295

7.6.3 线程的分离299

7.7 互斥量300

7.7.1 为什么需要互斥量300

7.7.2 互斥量的接口304

7.7.3 临界区的大小305

7.7.4 互斥量的性能306

7.7.5 互斥锁的公平性310

7.7.6 互斥锁的类型311

7.7.7 死锁和活锁314

7.8 读写锁316

7.8.1 读写锁的接口317

7.8.2 读写锁的竞争策略318

7.8.3 读写锁总结323

7.9 性能杀手:伪共享323

7.10 条件等待328

7.10.1 条件变量的创建和销毁328

7.10.2 条件变量的使用329

第8章 理解Linux线程(2)333

8.1 线程取消333

8.1.1 函数取消接口333

8.1.2 线程清理函数335

8.2 线程局部存储339

8.2.1 使用NPTL库函数实现线程局部存储340

8.2.2 使用__thread关键字实现线程局部存储342

8.3 线程与信号343

8.3.1 设置线程的信号掩码344

8.3.2 向线程发送信号344

8.3.3 多线程程序对信号的处理345

8.4 多线程与fork()345

第9章 进程间通信:管道349

9.1 管道351

9.1.1 管道概述351

9.1.2 管道接口352

9.1.3 关闭未使用的管道文件描述符356

9.1.4 管道对应的内存区大小361

9.1.5 shell管道的实现361

9.1.6 与shell命令进行通信(popen)362

9.2 命名管道FIFO365

9.2.1 创建FIFO文件365

9.2.2 打开FIFO文件366

9.3 读写管道文件367

9.4 使用管道通信的示例372

第10章 进程间通信:System V IPC375

10.1 System V IPC概述375

10.1.1 标识符与IPC Key376

10.1.2 IPC的公共数据结构379

10.2 System V消息队列383

10.2.1 创建或打开一个消息队列383

10.2.2 发送消息385

10.2.3 接收消息388

10.2.4 控制消息队列390

10.3 System V信号量391

10.3.1 信号量概述391

10.3.2 创建或打开信号量393

10.3.3 操作信号量395

10.3.4 信号量撤销值399

10.3.5 控制信号量400

10.4 System V共享内存402

10.4.1 共享内存概述402

10.4.2 创建或打开共享内存403

10.4.3 使用共享内存405

10.4.4 分离共享内存407

10.4.5 控制共享内存408

第11章 进程间通信:POSIX IPC410

11.1 POSIX IPC概述411

11.1.1 IPC对象的名字411

11.1.2 创建或打开IPC对象413

11.1.3 关闭和删除IPC对象414

11.1.4 其他414

11.2 POSIX消息队列415

11.2.1 消息队列的创建、打开、关闭及删除415

11.2.2 消息队列的属性418

11.2.3 消息的发送和接收422

11.2.4 消息的通知423

11.2.5 I/O多路复用监控消息队列427

11.3 POSIX信号量428

11.3.1 创建、打开、关闭和删除有名信号量430

11.3.2 信号量的使用431

11.3.3 无名信号量的创建和销毁432

11.3.4 信号量与futex433

11.4 内存映射mmap436

11.4.1 内存映射概述436

11.4.2 内存映射的相关接口438

11.4.3 共享文件映射439

11.4.4 私有文件映射455

11.4.5 共享匿名映射455

11.4.6 私有匿名映射456

11.5 POSIX共享内存456

11.5.1 共享内存的创建、使用和删除457

11.5.2 共享内存与tmpfs458

第12章 网络通信:连接的建立462

12.1 socket文件描述符462

12.2 绑定IP地址463

12.2.1 bind的使用464

12.2.2 bind的源码分析465

12.3 客户端连接过程468

12.3.1 connect的使用468

12.3.2 connect的源码分析469

12.4 服务器端连接过程477

12.4.1 listen的使用477

12.4.2 listen的源码分析478

12.4.3 accept的使用480

12.4.4 accept的源码分析480

12.5 TCP三次握手的实现分析483

12.5.1 SYN包的发送483

12.5.2 接收SYN包,发送SYN ACK包485

12.5.3 接收SYN ACK数据包494

12.5.4 接收ACK数据包,完成三次握手499

第13章 网络通信:数据报文的发送505

13.1 发送相关接口505

13.2 数据包从用户空间到内核空间的流程506

13.3 UDP数据包的发送流程510

13.4 TCP数据包的发送流程517

13.5 IP数据包的发送流程527

13.5.1 ip_send_skb源码分析528

13.5.2 ip_queue_xmit源码分析531

13.6 底层模块数据包的发送流程532

第14章 网络通信:数据报文的接收536

14.1 系统调用接口536

14.2 数据包从内核空间到用户空间的流程537

14.3 UDP数据包的接收流程540

14.4 TCP数据包的接收流程544

14.5 TCP套接字的三个接收队列553

14.6 从网卡到套接字556

14.6.1 从硬中断到软中断556

14.6.2 软中断处理557

14.6.3 传递给协议栈流程559

14.6.4 IP协议处理流程564

14.6.5 大师的错误?原始套接字的接收568

14.6.6 注册传输层协议571

14.6.7 确定UDP套接字571

14.6.8 确定TCP套接字576

第15章 编写安全无错代码582

15.1 不要用memcmp比较结构体582

15.2 有符号数和无符号数的移位区别583

15.3 数组和指针584

15.4 再论数组首地址587

15.5 “神奇”的整数类型转换588

15.6 小心volatile的原子性误解589

15.7 有趣的问题:“x == x”何时为假?591

15.8 小心浮点陷阱593

15.8.1 浮点数的精度限制593

15.8.2 两个特殊的浮点值593

15.9 Intel移位指令陷阱595

媒体评论

李彬是一个内敛且内秀的人,平日不多的话语并不影响他隽秀的文笔和缜密的思路。他通过各种隐喻的方式,将很多复杂甚至庞杂的问题,抽丝剥茧,层层分解,将 其内在的部分展现在你的面前。文中有大道至简的锦章佳句,也不乏诙谐幽默的流行俗语,雅俗并济但不显突兀和生硬,所有的一切都是为了让读者更好地理 解书中的内容。区别于其他编程图书中大量代码示例的堆叠和罗列、各种长篇大论的代码走读分析,本书从环境入手,对环境进行剖析,一切娓娓道来,由浅入深; 通过精致的小工具或者小程序,让读者快速探索环境,了解环境,熟悉环境,从而进一步利用环境,改造环境。

——李铜舒(Bruce Lee) Bigtera研发副总裁

高峰根据自己多年的编程经验, 深入浅出地介绍了Linux下C编程常用的方方面面。这是一本很棒的学习用书,特别是他还总结了自己编程中遇到的有趣问题,了解这些问题对提高程序员的编程技能是很有帮助的。后一章值得精读。

——李海涛 A10 Networks Senior Manager

本书可以说是继承了W. Richard Stevens的《UNIX环境高级编程》的传统并有所超,它重点介绍并剖析了Linux内核所提供的API以及API在内核中的实现,同时描述了 glibc对Linux API的封装,以及使用某些glibc函数时遇到的问题。不管是内容的广度还是深度,本书都达到了《UNIX环境高级编程》的高度。而且由于Linux的 开源特性,让我们有机会更深入地了解API实现的细节,这对我们写出更高效、更健壮的程序很有帮助。“知其然,知其所以然”应该是每个程序员的追求,如果 你对Linux环境下的编程感兴趣,并希望了解更多的话,本书不容错过。

——朱小平 高级技术专家

本书非常地介绍了Linux环境下编程所需的技术和知识,分析深入浅出,理论与实践相结合,且都是作者经验之谈,即使如我这般在Linux下使用C编程超过10年的程序员也是受益良多。因此,无论你是初学还是提升,本书都值得一读。

——夏舰波 华为博学研发工程师

网友评论(不代表本站观点)

免责声明

更多相关图书