legend传奇百度云:《apue2e》(Unix环境高级编程——第二版)学习心得

来源:百度文库 编辑:偶看新闻 时间:2024/05/07 08:18:57

《apue2e》(Unix环境高级编程——第二版)学习心得  

2010-12-14 20:46:14|  分类: Unix编程 |  标签: |字号大中小 订阅

总结:这是一本经典的书,对于Unix程序员如此,对于其它程序员也是相当有价值的。

先给两张可以概括本书所讨论内容的两张图片:(第二部分再给出说明)



第一部分:对本书总体结构的解析

本书共分21章。

个人认为核心章节为1~13章,
其中1~2章为本书的入门简介与本书的特色所在,为何称作“特色所在”呢?
因为本书始终都是在遵从那3个标准来进行编写的,即

ISO C

IEEE POSIX

The Single UNIX Specification

这三个标准,每下一行是上一行的超集,对于每一个Unix的c程序员来说都是最重要的编程标准。它决定了你代码的“高度”与“可用性”。
第二章的一节还提到了“limits”即“限制”这个概念,这对于可移植性的代码很重要。

3~5章,是关于“文件系统”和相关“I/O函数”的讨论,最重要的概念是“文件描述符”。
这里面dup 和dup2 函数很值得”嚼味“,有兴趣可以看看我的另一篇文章”dup2(fd,0)和dup2(0,fd)一样吗?“

6章,讨论了一些“系统文件”。

7~9章,讨论了“进程”。
个人认为下面这张图可以说是对程序的内存布局的典型情况作了很好图示。(见7.6节)



10章,讨论了“信号”,它是一种软中断。同时,它为“异步通信”的实现提供了可能。

11~12章,是相对于《apue》第一版新增的内容之一。对线程作了讨论。
你可以认为“线程”就是一种特殊的“函数”,只不过它能够共享调用进程的资源,能够独立于调用进程(或其它线程)并行执行。当然线程也可以同步,也可以异步执行。还有很多其它有趣的线程特性,可以详细地阅读该书。

13章,讨论了“守护进程”的概念与编写方法。

至此,本书的核心内容基本讨论完毕。

后面14~19章讨论了高级I/O,高级进程间通信,及终端和伪终端的概念。

14章的高级I/O,主要留意I/O的“阻塞”和“非阻塞”两种不同形式及各自的应用场合。要特别注意,所谓的阻塞或非阻塞是由“文件”的O_NONBLOCK 标志(注意文件描述符起的作用)决定的,而不是I/O 函数决定的。另外,这个章节中给出了“锁”的概念,这是进程或线程同步的重要技术。

15章讨论了进程间通信的种种方法,个人推荐“管道”和“FIFO”两种,至于“XSI IPC”则不建议使用,理由在书中也说得很清楚。

16章讨论了“网络套接字”编程。本想自己画个图来说明,没有时间就以后再说吧。

17章讨论了基于15章中所讨论的“流机制”和16章讨论的“套接字”的两种高级IPC,具体是哪两个,有兴趣自己去看书吧。前者没有什么可移植性,后者倒是很不错的概念。(后者很好体现了制定“套接字”的初衷:既可用于网络的通信,也可用于本机进程的通信)

18章讨论了终端I/O。(晕!看过才知道,终端的属性也太多了吧?)

19章讨论了“伪终端”,即“PTY”的概念和使用它的初衷。个人认为书中已经说得很透彻了。
如果你明白了“主设备”和“从设备”的关系,及伪终端运行在从设备上的原理和用处,那么你的知识体系又扩大很多了。
如果你愿意,也可以参考我的另一篇文章“linux中,tty、pty、pts等终端或伪终端的区别”

20章讨论了如何在Unix中构建一个支持 并发访问的数据库。

21章通过构建一个打印机的CS模型(客户-服务器)来整合前面大部分章节的知识,做一个应用示例。


第二部分:本书重点内容讨论

1.“标准”的重要性。一句话,没有第一部分所提到的3个标准,就不存在Unix编程了。(个人对“标准”的理解与重视)

2.“内核”是什么?能为我们提供什么功能呢?
再回过头来,看看一开始我给出的两张图片。
第一张是内核在整个系统中所处的位置,我想说的是:内核是对系统所有硬件资源的“管理和组织者”,你可以说它是“管家”。它负责为上层的软件运行提供所需的硬件资源的分配和时间调度的分配等等。
可以认为内核包含两大功能:一、驱动程序集合;二、调度系统。
第二张是内核所包含的功能模块,也可以说是本书所讨论内容的概括。21个章节中后19个的内容都是对这5个模块的详细展开。

3.文件系统的核心概念:“文件描述符”。
内核对文件系统的管理都是基于“文件描述符”的。不管是创建文件,移动或复制文件,修改文件,删除文件都使用“文件描述符”来对某个想操作的文件进行引用和表示。

4.区别一下信号,进程间通信,和进程间同步,及共享等概念的不同。
信号,是一种软中断;它是异步通信的基础。所以它(异步通信)是进程间通信的一种方式(我们主要使用的signal调用,kill调用等等)。而进程间通信还有别的形式,如Pipes 即管道(典型的是传统的无名单工管道),FIFOs(又叫有名管道,典型的是双工管道),XSI IPC(包括三种:消息队列,信号量(不是信号的概念哈!),共享内存)。后面这些方式主要是同步通信,它们主要用于进程间同步。而共享,主要是指内存共享,一般会用到14章提到的“锁机制”来保证一致性,像前面的共享内存就是它的一种应用。但你要知道共享的概念主要有两方面,一是进程间共享,如“内存共享”,文件共享(dup文件描述符)等;二是线程共享,共享的概念对于线程似乎是“天经地义”的,但主要指的是内存中变量等的“小范围”共享,这点要与进程共享区别。

5.层次组件
Unix是一个典型的层次型系统。由第一张图片可以看出设计者的这种思路。这样做可以简化系统的设计,提高系统的性能。
另一方面,Unix也是以“组件”(也可叫做“构件”)来将系统的所需功能以模块的形式很好地组织起来。如图二。
其实,Unix或Linux的内核也是这种思路。比如你可以定制自己所需的高效内核,删除其中一些不需要的驱动代码模块。如果不是这种“组件”的思想,那么为达到定制的目的,你就不得不重写整个内核了。

6.本书的特色:尽可能给出所有用到的函数原型,以及它的实现思想。
作者力求读者不止能够全面了解到 Unix 中有哪些你可以用的 庞大的 c 语言库,更希望能明白我们为何需要某个函数,以及怎样实现这个函数。Unix中c 语言也是分门别类的,而不是没有组织的。这种“规范化”的“标准”,给了我很深的印象。(诚如第1条中我强调的那样)

7.这本书有着所有优秀计算机类书籍的良好组织编排,特别是给出了很详实的“附录”和“索引”。
这些细节是需要很多时间和功夫才能做到尽善尽美的。这也是国内许多同类书籍缺少的。
现在,我可以将它作为“字典”来随时查阅了。


第三部分:系统地看待编程

该书是一本结合底层的系统级编程参考书。看完之后,对编程有了更加系统级的认识。
下面说一下自己的收获。

早期计算机诞生的目的就是为了解决庞大的计算问题。那时的计算机主要是以“硬件驱动”。
这样说主要是我认为:用户把一个计算问题(可以认为是他想运行的程序),通过某种方式,比如打孔纸条输入给计算机,计算机读取并分析该程序代码,做计算,并输出,比如可以输出到显示器上。这种模式,简单也单调。它不能允许多任务,不能做到更高的硬件资源利用效率。
为了解决效率利用问题,人们希望做一个比较底层的“系统程序”,然后在这个“系统程序”的环境中来组织,管理和调度其他“用户程序”,从而实现多任务与时间上对硬件资源的更高效利用,即“软件驱动”硬件资源。从而“操作系统”这个底层软件诞生了,Unix就是其中一个,而Unix操作系统的核心部分就是“内核”。因为要调度其它多个用户进程,所以内核要有“调度模块”,因为这些程序都是要利用计算机的硬件资源来做事情,所以内核要给所有的上层程序提供“驱动模块”以便它们使用计算机的硬件资源。(见本文第二部分2)

我始终认为软件和硬件的发展总是紧密关联的。
现在我们有了“操作系统”,那么现在我们看看这种“软件驱动”到底给我们带来了什么好处,与早期的“硬件驱动”比较。

从电脑加电开始,一步步启动并初始化操作系统(即内核),然后用户登录,最后在其中运行自己的计算任务。(见我的另一篇文章:Linux启动过程,用户登录过程,及内核进程调度schedule简单介绍)schedule(pid=0)就是内核的调度模块,init(pid=1)就是初始化模块。在schedule的调度下程序交替利用cpu的时间片完成自己的任务,并受到schedule的调度,如开始,暂停,休眠,终止等等。因为Unix中是“进程树”的进程组织形式,所以每个程序总有它的父进程,最顶端的父进程就是schedule,所以,内核总是能够管理和控制其它进程。

用户将自己的计算任务(可以是某段程序代码)存储在内存中供内核调度运行。但是由于内存容量有限,并且易失,所以后来演变为,将程序存放在外存中(比如硬盘),要用时再读取到内存中供内核调度。怎么有组织的存储我们的程序还有其他数据文件呢?于是我们需要有一个“文件系统”来管理这些程序和数据文件。而我们想要使用这些文件时,需要一套完善的I/O函数来进行操作,如读取,修改,写入等。这些都是本书3~5章和14章所讨论内容。

好吧,现在程序已经读取到了内存中,内核也在某个时间片选中该程序,调度并开始运行它,并赋予它某个pid号和它所需要的种种运行时环境和相关的硬件资源(如内存空间,寄存器,一些文件描述符等等),现在假如它是进程A,它可能由于计算需要还会产生子进程,如进程B来,而系统中可能还有很多其它的进程C,进程D之类的与它可能发生关系。由于Unix是多任务系统。所以在下个时间片,该进程就会被其它进程(比如进程C)替换出cpu,转而进入暂停或休眠状态。又由于Unix内核有抢占机制,所以可能当前时间片未完,它也可能被高优先级的其它进程(比如进程D)中断,被强制替换出cpu。关于进程这一块就涉及到进程环境的初始化,进程的控制(内核对用户进程的控制,父进程对子进程的控制等等),由于是多进程环境,所以各个进程之间存在联系(如父子进程,进程组,会话等概念)。由于多进程之间存在联系,那么必然需要内核提供一些它们之间相互通信的机制,包括同步通信和异步通信(见本文第二部分4的讨论)。这些关于进程的内容都是本书7~9章讨论的。而“信号”的机制在10章作了讨论,它是异步通信的基础。15~17章讨论了多种Unix下的通信机制。

有人觉得多进程都还不能充分利用计算机的硬件资源,于是就提出了多线程。多线程解决I/O操作对多进程造成阻塞导致效率低的问题。当然多线程还有一些别的特性,一些别的内核线程映射技巧来提高程序的运行效率。这部分在书中11~12章进行了讨论。

最后Unix提供了一个其它类别的操作系统少有的东东,即“终端”和“伪终端”的概念和设计。这些“组件”丰富了Unix编程的内容,为程序提供了独特的终端I/O函数,设计出有用的终端或伪终端程序。本书中18~19章作了深入讨论。Linux图形用户界面的终端窗口就是一个“伪终端”的前端。

说到图形界面,这里就再多说几句,本书中没有讨论这一部分,因为那不是内核所关注的模块或组件,而是“额外”的组件。如果我们要编写具有交互功能的程序,那么系统提供图形界面的“函数库”就是必须的了。那部分的编程内容你就得另外学习。

一开头我就说了,这本书不局限于面向Unix程序员,对于其它程序员也会有帮助的。
这里说明一下,比如你是一位Java程序员,那么你能从中学到什么呢。
首先你要知道Java提供了一个“虚拟机”(jvm)的概念,你可以认为它就是一个操作系统,或者内核。
jvm如同内核一样,提供了自己的库函数(就像Unix下丰富的底层函数调用一样)。这些函数库也有文件I/O函数,进程控制函数,线程控制函数,网络套接字部分,当然还少不了“信号机制”等等。
知道这些后,你就明白了,在jvm中编程如同在Unix下编程一样,需要面对的问题也一样了。
首先你要知道jvm的调度模块,它提供了那些底层库函数(驱动模块),那么你就可以开始编写代码了吧。


*************************************************************************************************************************

就写这么多了。上面只是一些概括性的总结和体会。
很多东西都是以“读书札记”的形式写在书上的。所以不可能全部分享。
本书中大部分代码,我都在自己的 debian 5 系统上进行了验证,有些地方的结果与书中结果有出入。
这可能是”具体实现“带来的差异性。不过绝大部分都得到了预期的结果。

这本书给我的最大感受就是:纠正了我以前许多错误认识,提高了我的编程的视角高度,更能全面地从系统出发,从全局来考虑以后的编程工作了。一开始看前几章节的时候,差不多,看一页,就要查阅相关的资料一个小时。因为书中值得引申的内容太多了,有些地方也确实值得反复“咀嚼”,就导致了“滚雪球”的现象。当然这并不是坏事,因为我学到更多相关的知识。记得哪一章节只是简单提到了”PAM“机制的概念,我就查阅了一个晚上,我博客还转载了一篇相关的文章 ” 深入Linux PAM 体系结构(转)“。

由于时间有限,如果以后有空,希望能够写更多的内容。
最后给一个在线学习apue的地址:http://book.chinaunix.net/special/ebook/addisonWesley/APUE2/
欢迎交流。