1.1 介绍 1
1.2 什么是系统编程 1
1.2.1简单的程序模型 1
第1章 Unix系统编程概述 1
1.2.2系统模型 2
1.2.3操作系统的职责 3
1.2.4为程序提供服务 4
1.3理解系统编程 4
1.3.1 系统资源 4
1.3.2 目标:理解系统编程 5
1.3.3方法:通过三个问题来理解 5
1.4.2登录-运行程序-注销 6
1.4从用户的角度来理解Unix 6
1.4.1 Unix能做些什么 6
1.4.3 目录操作 8
1.4.4文件操作 10
1.5从系统的角度来看Unix 12
1.5.1用户和程序之间的连接方式 12
1.5.2 网络桥牌 12
1.5.3 bc:Unix的计算器 13
1.5.4从bc/dc到Web 16
1.6动手实践 16
1.7.3 Unix的发展历程 23
1.7.2 Unix的概要图 23
1.7.1接下来的工作步骤 23
1.7工作步骤与概要图 23
小结 24
第2章 用户、文件操作与联机帮助:编写who命令 25
2.1介绍 25
2.2关于命令who 26
2.3 问题1:who命令能做些什么 27
2.4问题2:who命令是如何工作的 28
2.5 问题3:如何编写who 32
2.5.1 问题:如何从文件中读取数据结构 33
2.5.2 答案:使用open、read和close 34
2.5.3 编写who1.c 36
2.5.4 显示登录信息 37
2.5.5 编写who2.c 39
2.5.6 回顾与展望 44
2.6 编写cp(读和写) 44
2.6.1 问题1:cp命令能做些什么 44
2.6.2问题2:cp命令是如何创建/重写文件的 44
2.6.3问题3:如何编写cp 45
2.6.4 Unix编程看起来好像很简单 47
2.7提高文件I/O效率的方法:使用缓冲 48
2.7.1缓冲区的大小对性能的影响 48
2.7.2为什么系统调用需要很多时间 48
2.7.3低效率的who2.c 49
2.7.4在who2.c中运用缓冲技术 50
2.8内核缓冲技术 53
2.9文件读写 54
2.9.1注销过程:做了些什么 54
2.9.2注销过程:如何工作的 54
2.9.3改变文件的当前位置 55
2.9.4编写终端注销的代码 57
2.10处理系统调用中的错误 58
小结 59
3.2问题1:ls命令能做什么 63
3.2.1 1s可以列出文件名和文件的属性 63
3.1介绍 63
第3章 目录与文件属性:编写Is 63
3.2.2列出指定目录或文件的信息 64
3.2.3经常用到的命令行选项 65
3.2.4问题1的答案 65
3.3文件树 65
3.4问题2:ls是如何工作的 66
3.4.1什么是目录 66
3.4.2是否可以用open、read和close来操作目录 66
3.4.3如何读目录的内容 67
3.5问题3:如何编写ls 69
3.6编写ls-l 71
3.6.1 问题1:ls-l能做些什么 71
3.6.3用stat得到文件信息 72
3.6.2问题2:ls-l是如何工作的 72
3.6.4 stat提供的其他信息 74
3.6.5如何实现 75
3.6.6将模式字段转换成字符 75
3.6.7将用户/组ID转换成字符串 79
3.6.8 编写ls2.c 81
3.7三个特殊的位 86
3.7.1 set-user-ID位 86
3.7.2 set-group-ID位 87
3.7.3 sticky位 87
3.7.4用ls-1看到的特殊属性 87
3.9.1文件类型 88
3.9设置和修改文件的属性 88
3.8 ls小结 88
3.9.2许可位与特殊属性位 89
3.9.3文件的链接数 90
3.9.4文件所有者与组 90
3.9.5文件大小 91
3.9.6 时间 91
3.9.7文件名 91
小结 92
第4章 文件系统:编写pwd 96
4.1介绍 96
4.2.3文件操作命令 97
4.2.2 目录命令 97
4.2.1 目录和文件 97
4.2从用户的角度看文件系统 97
4.2.4针对目录树的命令 99
4.2.5 目录树的深度几乎没有限制 99
4.2.6 Unix文件系统小结 100
4.3 Unix文件系统的内部结构 100
4.3.1第一层抽象:从磁盘到分区 100
4.3.2第二层抽象:从磁盘到块序列 100
4.3.3第三层抽象:从块序列到三个区域的划分 100
4.3.4文件系统的实现:创建一个文件的过程 101
4.3.5文件系统的实现:目录的工作过程 102
4.3.6文件系统的实现:cat命令的工作原理 104
4.3.7 i-节点和大文件 105
4.3.8 Unix文件系统的改进 106
4.4理解目录 107
4.4.1理解目录结构 107
4.4.2与目录树相关的命令和系统调用 109
4.5编写pwd 113
4.5.1 pwd的工作过程 113
4.5.2 pwd的一种版本 114
4.6多个文件系统的组合:由多棵树构成的树 116
4.6.1装载点 117
4.6.2多重i-节点号和设备交叉链接 118
4.6.3符号链接 119
小结 120
第5章 连接控制:学习stty 124
5.1为设备编程 124
5.2设备就像文件 124
5.2.1设备具有文件名 125
5.2.2设备和系统调用 125
5.2.3例子:终端就像文件 126
5.2.4设备文件的属性 126
5.2.5编写write程序 127
5.2.6设备文件和i-节点 128
5.3设备与文件的不同之处 129
5.4.1属性1:缓冲 130
5.4磁盘连接的属性 130
5.4.2属性2:自动添加模式 132
5.4.3用open控制文件描述符 133
5.4.4磁盘连接小结 134
5.5终端连接的属性 135
5.5.1终端的I/O并不如此简单 135
5.5.2终端驱动程序 137
5.5.3 stty命令 137
5.5.4编写终端驱动程序:关于设置 138
5.5.5编写终端驱动程序:关于函数 139
5.5.6编写终端驱动程序:关于位 140
5.5.7编写终端驱动程序:几个程序例子 142
5.6其他设备编程:ioctl 146
5.5.8终端连接小结 146
5.7文件、设备和流 147
小结 147
第6章 为用户编程:终端控制和信号 153
6.1软件工具与针对特定设备编写的程序 153
6.2终端驱动程序的模式 154
6.2.1规范模式:缓冲和编辑 155
6.2.2非规范处理 156
6.2.3终端模式小结 157
6.3编写一个用户程序:play_again.c 158
6.4.2信号是什么 168
6.4.1 Ctrl-C做什么 168
6.4信号 168
6.4.3进程该如何处理信号 170
6.4.4信号处理的例子 171
6.5为处理信号做准备:play_again4.c 173
6.6进程终止 176
6.7为设备编程 176
小结 176
第7章 事件驱动编程:编写一个视频游戏 180
7.1视频游戏和操作系统 180
7.2任务:单人弹球游戏(Pong) 182
7.3屏幕编程:curses库 182
7.3.1介绍curses 182
7.4 时间编程:sleep 185
7.3.2 curses内部:虚拟和实际屏幕 185
7.5时钟编程1:Alarms 188
7.5.1添加时延:sleep 189
7.5.2 sleep()是如何工作的:使用Unix中的Alarms 189
7.5.3调度将要发生的动作 191
7.6时间编程2:间隔计时器 191
7.6.1添加精度更高的时延:usleep 192
7.6.2三种计时器:真实、进程和实用 192
7.6.3两种间隔:初始和重复 192
7.6.4用间隔计时器编程 193
7.6.5计算机有几个时钟 196
7.6.6计时器小结 197
7.7.2处理多个信号 198
7.7信号处理1:使用signal 198
7.7.1早期的信号处理机制 198
7.7.3测试多个信号 200
7.7.4信号机制其他的弱点 202
7.8信号处理2:sigaction 203
7.8.1处理多个信号:sigaction 203
7.8.2信号小结 206
7.9 防止数据损毁(Data Corruption) 206
7.9.1数据损毁的例子 206
7.9.2临界区(Critical Sections) 206
7.9.3阻塞信号:sigprocmask和sigsetops 207
7.9.4重入代码(Reentrant Code):递归调用的危险 208
7.9.5视频游戏中的临界区 208
7.10 kill:从另一个进程发送的信号 209
7.11.1 bou nceld.c:在一条线上控制动画 210
7.11使用计时器和信号:视频游戏 210
7.11.2 bounce2d.c:两维动画 213
7.11.3完成游戏 218
7.12输入信号:异步I/O 218
7.12.1使用异步I/O 218
7.12.2 方法1:使用O_ASYNC 218
7.12.3方法2:使用aio_read 221
7.12.4弹球程序中需要异步读入吗 224
7.12.5异步输入、视频游戏和操作系统 224
小结 224
8.1进程=运行中的程序 228
第8章 进程和程序:编写命令解释器sh 228
8.2通过命令ps学习进程 229
8.2.1 系统进程 231
8.2.2进程管理和文件管理 232
8.2.3内存和程序 232
8.3 shell:进程控制和程序控制的一个工具 233
8.4 shell是如何运行程序的 234
8.4.1 shell的主循环 234
8.4.2 问题1:一个程序如何运行另一个程序 235
8.4.3问题2:如何建立新的进程 240
8.4.4问题3:父进程如何等待子进程的退出 244
8.4.5小结:shell如何运行程序 249
8.5实现一个shell:psh2.c 250
8.6思考:用进程编程 254
8.7 exit和exec的其他细节 255
8.7.1进程死亡:exit和_exit 255
8.7.2 exec家族 256
小结 257
第9章 可编程的shell、shell变量和环境:编写自己的shell 260
9.1 shell编程 260
9.2什么是以及为什么要使用shell脚本语言 260
9.3 smshl——命令行解析 263
9.4.1 if语句做些什么 270
9.4 shell中的流程控制 270
9.4.2 if是如何工作的 271
9.4.3在smsh中增加if 272
9.4.4 smsh2.c:修改后的代码 273
9.5 shell变量:局部和全局 278
9.5.1使用shell变量 279
9.5.2变量的存储 280
9.5.3 增加变量命令:Built-ins 280
9.5.4效果如何 283
9.6环境:个性化设置 284
9.6.1使用环境 285
9.6.2什么是环境以及它是如何工作的 286
9.6.3在smsh中增加环境处理 288
9.6.4 varlib.c的代码 290
9.7 已实现的shell的功能 295
小结 296
第10章 I/O重定向和管道 299
10.1 shell编程 299
10.2一个shell应用程序:监视系统用户 300
10.3 标准I/O与重定向的若干概念 301
10.3.1概念1:3个标准文件描述符 302
10.3.2默认的连接:tty 302
10.3.3程序都输出到stdout 303
10.3.4重定向I/O的是shell而不是程序 303
10.3.6概念2:“最低可用文件描述符(Lowest-Available-fd)”原则 304
10.3.5理解I/O重定向 304
10.3.7两个概念的结合 305
10.4如何将stdin定向到文件 305
10.4.1方法1:close then open 305
10.4.2方法2:open..close..dup..close 308
10.4.3系统调用dup小结 310
10.4.4方法3:open..dup2..close 310
10.4.5 shell为其他程序重定向stdin 310
10.5为其他程序重定向I/O:who>userlist 310
10.6管道编程 314
10.6.1创建管道 314
10.6.2使用fork来共享管道 317
10.6.3使用pipe、fork以及exec 318
10.6.4技术细节:管道并非文件 320
小结 321
第11章 连接到近端或远端的进程:服务器与Socket(套接字) 325
11.1产品和服务 325
11.2一个简单的比喻:饮料机接口 326
11.3 bc:Unix中使用的计算器 327
11.3.1 编写bc:pipe、fork、dup、exec 328
11.3.2对协同进程的讨论 332
11.3.3 fdopen:让文件描述符像文件一样使用 332
11.4 popen:让进程看似文件 332
11.4.1 popen的功能 332
11.4.2实现popen:使用fdopen命令 334
11.4.3访问数据:文件、应用程序接口(API)和服务器 336
11.5 socket:与远端进程相连 337
11.5.1类比:“电话中传来声音:现在时间是 338
11.5.2 因特网时间、DAP和天气服务器 340
11.5.3服务列表:众所周知的端口 341
11.5.4编写timeserv.c:时间服务器 342
11.5.5测试timeserv.c 347
11.5.6编写timeclnt.c:时间服务客户端 347
11.5.7测试timeclnt.c 350
11.5.8另一种服务器:远程的ls 351
11.6软件精灵 356
小结 356
12.1服务器设计重点 360
12.2三个主要操作 360
第12章 连接和协议:编写Web服务器 360
12.3操作1和操作2:建立连接 361
12.3.1操作1:建立服务器端socket 361
12.3.2操作2:建立到服务器的连接 362
12.3.3 socklib.c 362
12.4操作3:客户/服务器的会话 364
12.4.1使用socklib.c的timeserv/timeclnt 365
12.4.2第2版的服务器:使用fork 366
12.4.3服务器的设计问题:DIY或代理 367
12.5编写Web服务器 369
12.5.1 Web服务器功能 369
12.5.3 Web服务器协议 370
12.5.2设计Web服务器 370
12.5.4编写Web服务器 372
12.5.5运行Web服务器 374
12.5.6 Webserv的源程序 374
12.5.7比较Web服务器 379
小结 379
第13章 基于数据报(Datagram)的编程:编写许可证服务器① 381
13.1软件控制 381
13.2许可证控制简史 382
13.3一个非计算机系统实例:轿车管理系统 383
13.3.1轿车钥匙管理描述 383
13.3.2用客户/服务器方式管理轿车 383
13.4.1许可证服务系统:它做些什么 384
13.4许可证管理 384
13.4.2许可证服务系统:如何工作 385
13.4.3一个通信系统的例子 386
13.5数据报socket 386
13.5.1流与数据报的比较 387
13.5.2数据报编程 388
13.5.3 sendto和recvfrom的小结 393
13.5.4数据报应答 394
13.5.5数据报小结 396
13.6许可证服务器版本1.0 396
13.6.1客户端版本1 397
13.6.2服务器端版本1 401
13.6.3测试版本1 406
13.6.4进一步的工作 407
13.7处理现实的问题 407
13.7.1处理客户端崩溃 407
13.7.2处理服务器崩溃 410
13.7.3测试版本2 412
13.8分布式许可证服务器 414
13.9 Unix 域socket 416
13.9.1文件名作为socket地址 416
13.9.2使用Unix域socket编程 416
13.10小结:socket和服务器 419
小结 419
14.1 同一时刻完成多项任务 423
第14章 线程机制:并发函数的使用 423
14.2函数的执行路线 424
14.2.1一个单线程程序 424
14.2.2一个多线程程序 425
14.2.3相关函数小结 427
14.3线程间的分工合作 428
14.3.1 例1:incrprint.c 428
14.3.2 例2:twordcount.c 430
14.3.3线程内部的分工合作:小结 436
14.4线程与进程 437
14.5线程间互通消息 438
14.5.1通知选举中心 439
14.5.2使用条件变量编写程序 440
14.5.3使用条件变量的函数 443
14.5.4 回到Web服务器的例子 444
14.6多线程的Web服务器 444
14.6.1 Web服务器程序的改进 445
14.6.2多线程版本允许一个新的功能 445
14.6.3 防止僵尸线程(Zombie Threads):独立线程 445
14.6.4 Web服务器代码 445
14.7线程和动画 452
14.7.1使用线程的优点 452
14.7.2多线程版本的bounceld.c 453
14.7.3基于多线程机制的多重动画:tanimate.c 455
14.7.4 tanimate.c中的互斥量 458
14.7.5屏幕控制线程 459
小结 460
第15章 进程间通信(IPC) 464
15.1编程方式的选择 464
15.2 talk命令:从多个数据源读取数据 465
15.2.1 同时从两个文件描述符读取数据 465
15.2.2 select系统调用 466
15.2.3 select与talk 469
15.2.4 select与poll 469
15.3通信的选择 469
15.3.1一个问题的三种解决方案 470
15.3.2通过文件的进程间通信 470
15.3.3命名管道 471
15.3.4共享内存 473
15.3.5各种进程间通信方法的比较 475
15.4进程之间的分工合作 476
15.4.1文件锁 476
15.4.2信号量(Semaphores) 480
15.4.3 socket及FIFO与共享的存储 487
15.5打印池 488
15.5.1多个写者、一个读者 488
15.5.2客户/服务器模型 489
15.6纵观IPC 490
15.7连接与游戏 492
小结 493