1、UNIX 环境高级编程 (第二版) (人民邮电出版社)【美】W.Richard Stevens & Stephen A.Rago 著尤晋元 张亚英 戚正伟 译目录第 1章 :UNIX 基础知识 .1第 2章 :UNIX 标准及其实现 .2第 3章 :文件 I/O.2第 4章 :文件和目录 2第 5章 :标准 I/O库 2第 6章 :系统数据文件和信息 3第 7章 :进程环境 3第 8章 :进程控制 3第 9章 :进程关系 4第 10章 :信号 4第 11章 :线程 4第 12章 :线程控制 4第 13章 :守护进程 5第 14章 :高级 I/O.5第 15章 :进程间通信 5第 16章 :网络
2、 IPC:套接字 .5第 17章 :高级进程间通信 6第 18章 :终端 I/O.6第 19章 :伪终端 6第 20章 :数据库函数库 6第 21章:与网络打印机通信 6第 1章:UNIX 基础知识UNIX 体系结构中,最主要的是内核,它有一些称为系统调用的接口与外界交互。在内核之上有 shell 和库函数,然后是应用软件。常见的 shell有 Bourne shell(sh), Bourne_again shell(bash), C shell(csh), Korn shell(ksh), TENEX C shell(tcsh)。不能出现在文件名中的字符只有斜线(/)和空操作符(null)两
3、个。文件描述符(file descriptor)通常是一个小的非负整数。当一个进程收到一个信号时,有三种选择:忽略该信号;按系统默认方式处理;提供一个函数,信号发生时调用这个函数。第 2章:UNIX 标准及其实现1、本章介绍了三个主要标准:ISO C、POSIX 和 Single UNIX Specification 。POSIX标准中的都只是接口,而不是实现,所以不区分系统调用和库函数,都称为函数。Single UNIX Specification(单一 UNIX规范)是 POSIX.1标准的一个超集,定义了一些附加的接口。2、提高移植性的限制有两类:编译时限制和运行时限制。ISO C定义的
4、限制都是编译时限制,列在头文件中。POSIX.1 定义的限制和常量有 5类:不变的最小值;不变值;运行时可以增加的值;运行时不变的值(可能不确定) ;路径名可变值(可能不确定) 。3、如果在编译一个程序时,希望它只使用 POSIX的定义而不使用任何其他的定义,就需要定义常量_POSIX_C_SOURCE。第 3章:文件 I/O1、UNIX 系统中的大多数文件 I/O只需用到 5个函数:open、read、write、lseek 以及 close。由于新的 open函数提供了 O_CREAT和 O_TRUNC选项,也就可以完全替代 creat函数了。Lseek 函数的作用就是为一个打开的文件设置
5、其当前文件偏移量,定位读写的位置。2、内核使用三种数据结构表示打开的文件:进程表中的一个记录项、文件表、v 节点(在 Linux中由通用型的 i节点来实现) 。3、使用 pread和 pwrite函数可以实现原子读写。Dup 和 dup2函数可以复制一个现存的文件描述符。Sync、fsync 和 fdatasync函数可以使放在缓存中的数据写入磁盘,免得系统崩溃时造成数据丢失。Fcntl 函数可以改变已打开文件的性质。第 4章:文件和目录1、本章讨论的中心是 3个 stat函数以及它们返回的信息。Stat 函数返回文件的信息结构,fstat 函数获取描述符为 filedes的文件的有关信息,l
6、stat 可以返回符号链接的有关信息而不是它所指向的文件的有关信息。2、其他函数:access 函数按实际用户 ID和实际组 ID进行访问权限测试;umask 函数为进程设置文件模式创建屏蔽字;chmod 和 fchmod函数用于更改现有文件的访问权限;chown、fchown 和 lchown函数用于更改文件的用户 ID和组 ID;truncate 和 ftruncate函数把现有文件截短为参数 length字节;link、unlink 创建和删除一个指向现有文件的链接(硬链接) ;symlink 函数创建一个符号链接;utime 函数可以更改一个文件的访问和修改时间;chdir、fchdi
7、r 函数可以更改当前工作目录;getcwd 返回工作目录的绝对路径。第 5章:标准 I/O库1、在 UNIX系统中,标准 I/O库最终都要调用第 3章中说明的 I/O例程。2、当用标准 I/O库打开或创建一个一个文件时,我们已使一个流与一个文件相关联。每个标准 I/O流都有一个与其相关联的文件描述符,可以对一个流调用 fileno函数以获取其描述符。3、对一个进程预定义了 3个流:标准输入、标准输出和标准出错。4、打开标准 I/O流的函数:fopen、freopen、fdopen。用 fclose函数关闭。5、流的读写函数:getc、putc,fgetc、fputc,getchar、putch
8、ar,fgets、fputs,gets、puts(这一对不推荐使用) 。6、格式化输入输出函数:printf、scanf,fprintf、fscanf,sprintf、snprintf、sscanf。7、使用 tmpnam和 tmpfile函数可以创建临时文件。第 6章:系统数据文件和信息1、一般情况下,对于每个数据文件至少有三个函数:get 函数,读出记录;set 函数,打开文件然后反绕它;end 函数,关闭文件。2、与用户 ID相关的几个重要的文件是:口令文件(passwd) ;阴影口令文件(shadow) ;组文件(group) ;utmp 文件,记录当前登录进系统的各个用户;wtmp
9、文件,跟踪各个登录和注销事件。3、关于时间和日期的一个函数。Time 函数返回当前时间:自 1970年 1月 1日零时以来的秒数,而且是国际标准时间。Gettimeofday 函数与 time类似,只是可以提供更高的分辨率(微秒) 。Localtime 和 gmtime函数将日历时间转换成以年、月、日、时、分、秒周日表示的时间,填充 struct tm结构。而 mktime函数则相反,转换成 time_t值,也就是日历时间。Asctime 和 ctime函数产生 26字节的字符串,表示年月日时分秒等信息。Strftime函数提供格式化的时间表示,类似于 printf函数。第 7章:进程环境1、
10、进程的正常终止大多是调用 exit函数,它先调用各终止处理程序(由 atexit函数登记) ,然后按需多次调用 fclose关闭打开的所有流。2、一个 C程序的组成部分:正文段(指令) 、初始化数据段、非初始化数据段(bss) 、栈(函数调用时使用) 、堆(动态存储分配) 。3、存储空间的动态分配函数有 3个:malloc、calloc、realloc。而 free函数释放存储空间。4、系统中的环境变量由各个应用程序解释使用,内核不过问。相关的 3个函数,putenv添加一个环境变量、setenv 更改一个环境变量、unsetenv 删除一个环境变量。5、在 C中,goto 语句是不能跨越函数
11、的,而执行这类跳转功能的是函数 setjmp和longjmp 。6、使用 getrlimit和 setrlimit函数查询和修改系统对一个进程的资源限制。第 8章:进程控制1、每一个进程都有一个非负整型表示的唯一进程 ID。这是最重要的一个进程标识符。2、本章的重点 fork。一个现有进程可以调用 fork函数创建一个新进程。有两种用法:一个父进程希望复制自己,使父、子进程同时执行不同的代码段;一个进程想启动一个完全不一样的进程(例如利用 shell执行命令) 。Fork 调用一次返回两次,在子进程中返回0,在父进程中返回子进程的 ID。3、vfork 用于创建一个新进程,但其目的是 exec
12、一个新程序。就像 fork的第二种用法。但是 vfork保证子进程先运行。而对于 fork,子进程还是父进程先运行是不确定的。4、对于一个进程的任意一种终止情形,我们都希望被终止的进程能够通知其父进程它是如何终止的。终止函数 exit、_exit 和_Exit 把进程的退出状态作为参数传递给函数。5、调用 wait和 waitpid函数可以等待子进程终止的异步信号的通知。但是一个子进程终止之前,wait 使其调用者阻塞,而 waitpid有一个选项,可使调用者不阻塞。6、exec 函数族。调用 exec函数不创建新的进程,前后进程 ID并不改变,它只是用一个全新的程序替换了当前进程的正文、数据
13、、堆和栈。一共有 6个函数:execl、execv、execle、execve、execlp 和 execvp。Exec 函数的一种变体:解释器文件。Shell脚本是其中比较常见的一种。第 9章:进程关系1、本章最重要的是三个概念:进程组、会话和作业控制。进程组是一个或多个进程的集合,拥有一个唯一的 ID,有一个组长进程。进程可以通过调用 setpgid来加入一个现有的组或者创建一个新进程组。而会话是一个或多个进程组的集合,进程调用 setsid函数建立一个新会话。作业控制允许在一个终端上启动多个作业(进程组) ,它控制哪一个作业可以访问终端,哪些作业在后台运行。第 10章:信号1、这一章是相
14、当重要的一章,用了 50多页来讲解。含有大量的实例程序,讲解得比较深入。2、信号是软件中断,用于处理异步事件。每一个信号都有一个名字,而且都以三个字符 SIG开头,对应一个整型编号。信号产生的情况:用户按下某些终端按键;硬件异常;调用 kill函数发送;用户使用 kill命令发送;检测到某种软件条件已经发生。三种信号处理方法:忽略它;捕捉它,然后调用特定的用户函数;执行系统默认动作。3、一些与信号有关的重要的函数。Signal 函数用于设置某信号的处理程序,在很多平台该函数都是由 sigaction函数来实现的。Kill 函数将信号发送给进程或进程组。Alarm函数设置过一段时间后发送信号给自
15、己,而 pause函数使自己挂起直到捕捉到一个信号。Sigprocmask 函数可以检测或更改其信号屏蔽字。Sigpending 函数返回对应进程是阻塞的信号集。Sigsuspend 函数解除一个阻塞信号并且马上使进程休眠,两步是一个原子操作。Abort 函数用于使异常程序终止,进程接收到信号后终止之前可以执行一些必须的清理工作。第 11章:线程1、这一章有三部分的内容,线程的概念、线程的创建和终止、线程的同步。2、多线程设计的好处:使得处理异步事件的代码变得简化。共享方便。提高整个程序的吞吐量。改善交互程序的响应时间。3、线程包含了表示进程内执行环境必需的信息,包括线程 ID(只在它所属的进
16、程环境中有效) 、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno 变量以及线程私有数据。4、线程的创建使用 pthread_create函数。但是创建后不保证哪一个线程先运行。如果进程中的任一线程调用了 exit、_Exit 或者_exit,那么整个进程就会终止。而单个线程有三种方式推出:从启动例程中返回。被同一进程中的其他线程取消。自己调用pthread_exit函数。5、实现线程同步主要有三种方式:使用互斥量、使用读写锁、使用条件变量。第 12章:线程控制1、线程属性存放于数据结构 pthread_attr_r中。可以使用函数 pthread_attr_init函数来初始化这个
17、数据结构,使用 pthread_attr_destroy函数来用无效数值填充,即“反初始化” 。使用函数 pthread_attr_setdetachstate函数修改结构中的 detachstate属性,可以让线程以分离状态启动。线程的栈属性通过 pthread_attr_setstack函数来设置。2、线程的同步属性包括互斥量属性、读写锁属性、条件变量属性。它们都有对应的进出共享属性,存在于不同的数据结构中。3、如果一个函数在同一时刻可以被多个线程安全地调用,就称该函数是线程安全的。如果函数对异步信号处理程序的重入是安全的,那么就说函数是异步-信号安全的。4、另外是线程属性还有可取消状态和
18、可取消类型。这两个属性影响着线程在响应pthread_cancle函数调用时所呈现的行为。第 13章:守护进程1、守护进程也称精灵进程(daemon) 。2、守护进程编程规则:调用 umask将文件模式创建屏蔽字设置为 0。调用 fork,然后使父进程退出。调用 setsid以创建一个新会话。将当前工作目录更改为根目录。关闭不再需要的文件描述符。3、守护进程没有控制终端,它的出错信息通过 syslog设施来记录。对应的函数有openlog、syslog、closelog、setlogmask。第 14章:高级 I/O1、非阻塞 I/O使得当我们使用 open、read、write 这样的操作时
19、,如果不能完成则立即出错返回,不会阻塞。2、记录锁对一个文件区域进行加锁,当一个进程正在对一个文件的某区域进行操作时可以阻止被另外一个进程对其的操作而引起的混乱。注意,这不是对整个文件加锁,只是一个文件的指定区域,所以这个锁也叫字节范围锁。3、当一个进程需要请求多个描述符时,可以使用 I/O多路转接技术来处理。大致是先构造一张有关描述符的表,然后调用一个函数,直到这些描述符中的一个准备好进行 I/O时,该函数才返回。可以选择的函数有三个:poll、pselect 和 select。4、高级 I/O还提供了一些很有用的扩展函数。Readv 和 writev函数实现在一次函数调用中读、写多个非连续
20、区域。Readn 和 writen函数可以指定读、写 N个字节的数据,能够自动处理返回值小于要求值的情况,是多次调用 read和 write函数实现的。第 15章:进程间通信1、最古老的 IPC是管道,它有两方面的局限性:半双工传输(有个别系统实现了全双工) ;只能在有公共祖先的进程间使用。一些相关函数:pipe 函数用于创建一个新管道,popen函数实现创建一个管道然后调用 fork产生一个子进程,然后使用管道与其通信。2、当一个程序产生某个过滤程序的输入,同时又读取该过滤程序的输出时,则该过滤程序就称为协同进程。3、FIFO 有时被称为命名管道。一些相关函数:mkfifo 用于创建,使用
21、open函数打开。4、有三种 IPC统称为 XSI IPC,分别是消息队列、信号量(其实它真正上是一种同步原语) 、共享存储器。每一个 XSI IPC结构都有对应的一个非负整型标识符和一个键。三种的创建函数分别为 msgget、semget、shmget。第 16章:网络 IPC:套接字1、套接字是通信端点的抽象,访问套接字要用套接字描述符。创建一个套接字使用socket函数。使用函数 shutdown可以禁用一个套接字,通过参数决定关闭写端或者读端。2、套接字使用的地址有一个通用的结构:socketaddr 结构。使用函数 bind可以将地址绑定到一个套接字,而调用函数 getsocketn
22、ame查看绑定到一个套接字的地址。3、使用函数 connect建立一个连接。对于服务器,可以调用 listen函数来宣告可以接受连接请求,然后使用函数 accept函数获得连接请求并建立连接。4、六个数据传送相关的函数。Send 和 sendto函数很相似,都是把数据发出去,只是sendto允许在无连接的套接字上指定一个目标地址。而,sendmsg 函数可以指定多重缓冲区传输数据,类似于 writev函数。Recv 函数用来接收数据,recvfrom 函数比 recv多一个功能是可以得到数据发送者的地址。对应于 sendmsg,有 recvmsg函数。第 17章:高级进程间通信1、基于 STR
23、EAMS的管道(简称 STRREAMS管道,STREAMS pipe)是一个双向(全双工)管道。可以用 fattach函数给 STREAMS管道一个文件系统中的名字,使用 fdetach函数撤销它。2、UNIX 域套接字用于在同一台机器上运行的进程之间的通信。提供流和数据报两种接口。使用 socketpair函数可以创建一对非命名的、相互连接的 UNIX域套接字。第 18章:终端 I/O1、终端设备是由位于内核中的终端驱动程序控制的,都有一个输入队列和输出队列。终端设备的所有特性都包含在 termios结构中,该结构有四大标志:c_cflag, c_lflag, c_iflag, c_ofla
24、g。使用函数 tcgetattr和 tcsetattr可以获得和设置 termios结构,而在命令行中可以使用 stty命令。2、终端 I/O有两种不同的工作模式:规范模式输入处理(以行为单位处理)和非规范模式输入处理(不以行为单位处理输入数据) 。第 19章:伪终端1、伪终端这个术语暗示对于一个应用程序而言,它看上去像一个终端,但实际上应用程序被欺骗了。从内核角度看,伪终端看起来像一个双向管道。而事实上 Solaris的伪终端就是用 STREAMS构建的。2、一些相关的调用函数。Posix_openpt 函数用来打开下一个可用的伪终端主设备。用于更改权限的两个函数是 grantpt和 unl
25、ockpt。确定路径名用 ptsname函数。3、当我们用 pty来执行另外一个程序时,该程序在一个它自己的会话中执行,并和一个伪终端连接。具体对伪终端的应用主要有以下几个。Utmp 文件、作业控制交互、检查长时间运行程序的输出、script 程序、运行协同进程、用非交互模式驱动交互式程序。第 20章:数据库函数库1、本章详细介绍了一个数据库函数库的设计与实现。此函数库包括的一些函数有:数据库的打开与关闭函数:db_open, db_close;存储和删除一条记录:db_store, db_delete;从数据库获取一条记录:*db_fetch;访问数据库所有记录的两个函数:db_rewind
26、, *db_nextrec。2、数据库被建立时创建两个文件:索引文件和数据文件。索引文件的数据通常使用散列法或 B+树来组织,从而提高数据的访问速度。对于进程对数据库数据的访问,有集中式和非集中式两种实现方法。集中式是指由一个数据库进程作为数据库管理者,所有的数据库访问工作由此进程完成,其它进程通过 IPC与此中心进程联系。非集中式是指每个库函数独立申请并发控制,然后自己调用 I/O函数。第 21章:与网络打印机通信1、本章实现了两个程序:一个打印假脱机守护进程,用以将作业发送到打印机;一个命令行程序,用以将打印作业提交到假脱机守护进程。与网络打印机通信使用的是网络打印协议(IPP) ,而它建立在 HTTP和 TCP/IP的基础之上。