




已阅读5页,还剩121页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
计算机科学丛书UNIX环境高级编程15章尤晋元 译机械工业出版社 前言引言本书说明Unix系统的程序设计界面系统调用界面和标准C提供的很多函数。这些对编写在Unix系统中运行的程序是非常有帮助的。与大多数操作系统一样,Unix对程序运行提供了大量的服务打开文件、读文件、启动一道新程序、分配存储区以及获得当前时间等。这些被称之为系统调用界面(system call interface)。另外,标准C库提供了大量C程序广泛使用的函数(格式化输入变量的值、比较两个字符串等)。系统调用和库函数系统上由Unix程序员手册中的第,部分说明。本书不是这些内容的重复。该手册没有给出实例,也不说明这些界面和函数设计的合理性,而这些则正是本书所要弥补的。Unix标准在八十年代出现的大量Unix版本的基础上,八十年代后期开始制定了几个国际标准,包括:程序设计语言的ANSI标准、IEEE POSIX标准族(还在继续制定)、X/open可移植性指南。本书也说明这些标准,但是并不只是说明这些标准本身,而是着重说明它们与广泛受到重视的一些实现之间的关系,这些实现主要是系统版本,以及将发布的.BSD。这样也就提供了一种对现实世界的说明,而这正是标准本身及描述符的文献所缺少的。本书的组织本书分成个部分:1. 对Unix程序设计概念和术语的概要描述(第一章),以及对各种Unix标准化工作和Unix实现的讨论(第二章)。2. 不带缓存的(第三章)、文件和目录(第四章、标准I/O库(第五章)以及系统数据文件(第六章)。3. 进程nix进程的环境(第七章)、进程控制(第八章)、进程之间的关系(第九章)以及信号(第十章)。4. 终端I/O(第十一章)、高级I/O(第十二章)以及精灵进程(第十三章)5. IPC进程间通信(第十四、十五章)。6. 实例一个数据库的函数库(第十六章)、与Postscrip打印机的通信(第十七章)、调制解调器拨号程序(第十八章)以及使用伪终端(第十九章)。如果对语言较熟悉并具有某些应用Unix的经验,那么对阅读本书是非常有益的,但是并不要求读者具有Unix的程序设计经验。本书面向的主要读者是:熟悉Unix的程序员,熟悉某个其它操作系统的程序员,他们希望了解大多数Unix系统提供的各种服务的详细情况。本书中的实例本书包含大量实例大约10000行源代码。所有实例都用ANSL C语言编写。在阅读本书时,你应当有一本你所使用的UNIX系统的Unix程序员手册,关于实施的细节等有时需参考该手册。对于几乎每一个函数和系统调用,本书都用一个小的完整的程序进行演示。这可以让我们清楚地了解它们的用法,包括参数、返回值等。有些小程序还不是以说明库函数和系统调用的复杂功能和应用技巧,所以本书中也包含了一些较大的实例(第十六、十七、十八和十九章)。所有实例的源代码文件都可经Internet用匿名ftp取到,其主机站点是,文件名是published/books/stevens.advprog.tar.Z。在你的机器上可对这些源代码进行修改并运行它们。用于测试实例的系统不幸的是所有操作系统都在不断变更,Unix也不例外。下图示出了系统和4.XBSD的最近进展情况。4.XBSD是由加州大学贝克莱分校计算机系统研究组开发的。该研究组也发布BSD NET1和BSD NET2版,它们包含了4.XBSD系统公众可用源代码。SVRX是AT& T的系统V的简称。XPG3是X/Open可移植性指南的第三次发行本的简称。ANSI C是C程序设计语言的ANSI标准。POSIX.1是Unix类系统界面的IEEE和ISD标准。2.2和2.3节将对这些标准和版本之间的差别作更多说明。 在本书中,用4.3+BSD表示BSD NET2和4.4BSD之间的Unix系统。 在本书写作时,4.4BSD尚未发行,所以不能称一个系统为4.4BSD。尽管为此, 需要一个简单的名字以引用此种系统,在全书中我们用4.3+BSD。本书中的大多数实例曾在4种Unix系统上运行过,它们是:1. U.H公司(UHC)的Unix System V/386 Release4.0 Version 2.0。该系统在Intel 80386处理机上运行。2. 加州大学贝克莱分校计算机科学系(Computer Science Division)计算机系统研究组的4.3+BSD,该系统在一台HP工作站上运行。3. 贝克莱软件设计公司的BSD/386(是BSD Net2的导出版),该系统在一台Intel 80386处理机系统上运行。该系统几乎与我们称之为4.3+BSD的相同。4. Sun Microsystems的Sun OS4.1.1和4.1.2(该系统与贝克莱系统有很深的渊源团系,但也包含了很多系统V的特征),这些系统在SPARC工作站SLC上运行。本书提供了与测试有关的许多时间信息,也说明了用于测试的系统实际系统。目录译者序前言第1章 UNIX基础知识11 引言12 登录121 登录名122 shell13 文件和目录131文件系统132 文件名133路径名134工作目录135起始目录14输入和输出141文件描述符142标准输入、标准输出和标准出错143不用缓存的I/O144标准I/O15程序和进程151程序152进程和进程ID153进程控制16 ANSI C161 函数原型1.6.2类属指针1.6.3原始系统数据类型1.7 出错处理1.8 用户标识1.8.1 用户ID1.8.2 组ID1.8.3 添加组ID1.9 信号1.10 UNIX时间值1.11 系统调用和库函数1.12 小结习题第2章 UNIX标准化及实现2.1 引言2.2 UNIX标准化2.2.1 ANSI C2.2.2 IEEE POSIX2.2.3 X/Open XPG32.2.4 FIPS2.3 UNIX实现2.3.1 SVR42.3.2 4.3+BSD2.4 标准和实现的关系2.5 限制2.5.1 ANSI C限制2.5.2 POSIX限制2.5.3 XPG3限制2.5.4 sysconf、pathconf和 fpathconf函数2.5.5 FIPS 151-1要求2.5.6 限制总结2.5.7 未确定的运行时间限制2.6 功能测试宏2.7 基本系统数据类型2.8 标准之间的冲突2.9 小结习题第3章 文件I/O3.1 引言3.2 文件描述符3.3 open函数3.4 creat函数3.5 close函数3.6 lseek函数3.7 read函数3.8 write函数3.9 I/O的效率3.10 文件共享3.11 原子操作3.11.1 添加至一个文件3.11.2 创建一个文件3.12 dup和dup2函数3.13 fcntl函数3.14 ioctl函数3.15 /dev/fd3.16 小结习题第4章 文件和目录4.1 引言4.2 stat,fstat和lstat函数4.3 文件类型4.4 设置-用户-ID和设置-组-ID4.5 文件存取许可权4.6 新文件和目录的所有权4.7 access函数4.8 umask函数4.9 chmod 和fchomod函数4.10 粘住位4.11 chown, fchown和 lchown函数4.12 文件长度4.13 文件截短4.14 文件系统4.15 link, unlink, remove和 rename 函数4.16 符号连接4.17 symlink 和readlink函数4.18 文件的时间4.19 utime函数4.20 mkdir和 rmdir函数4.21 读目录4.22 chdir, fchdir和 getcwd函数4.23 特殊设备文件4.24 sync和 fsync函数4.25 文件存取许可权位小结4.26 小结习题第5章 标准I/O库5.1 引言5.2 流和FILE对象5.3 标准输入、标准输出和标准出错5.4 缓存5.5 打开流5.6 读和写流5.7 每次一行I/O5.8 标准I/O的效率5.9 二进制I/O5.10 定位流5.11 格式化I/O5.12 实现细节5.13 临时文件5.14 标准I/O的替代软件5.15 小结习题第6章 系统数据文件和信息6.1 引言6.2 口令文件6.3 阴影口令6.4 组文件6.5 添加组ID6.6 其他数据文件6.7 登录会计6.8 系统标识6.9 时间和日期例程6.10 小结习题第7章 UNIX进程的环境7.1 引言7.2 main 函数7.3 进程终止7.3.1 exit和_exit函数7.3.2 atexit函数7.4 命令行参数7.5 环境表7.6 C程序的存储空间布局7.7 共享库7.8 存储器分配7.9 环境变量7.10 setjmp 和longjmp函数7.10.1 自动, 寄存器和易失变量7.10.2 自动变量的潜在问题7.11 getrlimit 和setrlimit函数7.12 小结习题第8章 进程控制8.1 引言8.2 进程标识8.3 fork函数8.4 vfork 函数8.5 exit函数8.6 wait和waitpid函数8.7 wait3和 wait4函数8.8 竞态条件8.9 exec函数8.10 更改用户ID 和组ID 8.10.1 setreuid 和setregid函数8.10.2 seteuid和 setegid函数8.10.3 组ID8.11 解释器文件8.12 system函数8.13 进程会计8.14 用户标识8.15 进程时间8.16 小结习题第9章 进程关系9.1 引言9.2 终端登录9.2.1 4.3+BSD终端登录9.2.2 SVR4终端登录9.3 网络登录9.3.1 4.3+BSD网络登录9.3.2 SVR4网络登录9.4 进程组9.5 对话期9.6 终端控制9.7 tcgetpgrp 和tcsetpgrp函数9.8 作业控制9.9 shell执行程序9.10 孤儿进程组9.11 4.3+BSD实现9.12 小结习题第10章 信号10.1 引言10.2 信号的概念10.3 signal函数10.3.1 程序起动10.3.2 进程创建10.4 不可靠的信号10.5 中断的系统调用10.6 可再入函数10.7 SIGCLD语义10.8 可靠信号术语和语义10.9 kill 和raise函数10.10 alarm和 pause函数10.11 信号集10.12 sigprocmask 函数10.13 sigpending函数10.14 sigaction函数10.15 sigsetjmp 和siglongjmp函数10.16 sigsuspend函数10.17 abort函数10.18 system 函数10.19 sleep函数10.20 作业控制信号10.21 其他特征10.21.1 信号名字10.21.2 SVR4信号处理程序的附加参数10.21.3 4.3+BSD信号处理程序的附加参数10.22 小结习题第11章 终端I/O11.1 引言11.2 综述11.3 特殊输入字符11.4 获得和设置终端属性11.5 终端选择标志11.6 stty命令11.7 波特率函数11.8 行控制函数11.9 终端标识11.10 规范方式11.11 非规范方式11.12 终端的窗口大小11.13 termcap, terminfo和 curses11.14 小结习题第12章 高级I/O12.1 引言12.2 非阻塞I/O12.3 记录锁12.3.1 历史12.3.2 fcntl记录锁12.3.3 锁的隐含继承和释放12.3.4 4.3+BSD的实现12.3.5 建议性锁和强制性锁12.4 流12.4.1 流消息12.4.2 putmsg和 putpmsg函数12.4.3 流ioct1操作12.4.4 write至流设备12.4.5 写方式12.4.6 getmsg和 getpmsg函数12.4.7 读方式12.5 I/O多路转接12.5.1 select函数12.5.2 poll函数12.6 异步I/O 12.6.1 SVR412.6.2 4.3+BSD12.7 readv和writev函数12.8 readn和 writen函数12.9 存储映射I/O12.10 小结习题第13章 精灵进程13.1 引言13.2 精灵进程的特征13.3 编程规则13.4 出错记录13.4.1 SVR4流log驱动程序13.4.2 4.3+BSD syslog设施13.5 客户机-服务器模型13.6 小结习题第14章 进程间通信14.1 引言14.2 管道14.3 popen和 pclose函数14.4 协同进程14.5 FIFO14.6 系统V IPC14.6.1 标识符和关键字14.6.2 许可权结构14.6.3 结构限制14.6.4 优点和缺点14.7 消息队列14.8 信号量14.9 共享存储14.10 客户机-服务器属性14.11 小结习题第15章 高级进程间通信15.1 引言15.2 流管道15.3 传送文件描述符15.3.1 SVR415.3.2 4.3BSD15.3.3 4.3+BSD15.4 open服务器第1版15.5 客户机-服务器连接函数15.5.1 SVR415.5.2 4.3+BSD15.6 open服务器第2版15.7 小结习题第16章 数据库函数库16.1 引言16.2 历史16.3 函数库16.4 实现概述16.5 集中式或非集中式16.6 并发16.6.1 粗锁16.6.2 细锁16.7 源码16.8 性能16.8.1 单进程的结果16.8.2 多进程的结果16.9 小结习题第17章 与PostScript打印机通信17.1 引言17.2 PostScript通信机制17.3 假脱机打印17.4 源码17.5 小结习题第18章 调制解调器拨号器18.1 引言18.2 历史18.3 程序设计18.4 数据文件18.5 服务器设计18.6 服务器源码18.7 客户机设计18.7.1 终端行规程18.7.2 一个进程还是两个进程18.8 客户机源码18.9 小结习题 第19章 伪终端19.1 引言19.2 概述19.2.1 网络登录服务器19.2.2 script程序19.2.3 expect程序19.2.4 运行协同程序19.2.5 观看长时间运行程序的输出19.3 打开伪终端设备19.3.1 SVR419.3.2 4.3+BSD19.4 pty_fork函数19.5 pty程序19.6 使用pty程序19.6.1 utmp文件19.6.2 作业控制交互19.6.3 检查长时间运行程序的输出19.6.4 script程序19.6.5 运行协同进程19.6.6 用交互模式驱动交互式程序19.7 其他特性19.7.1 打包模式19.7.2 远程模式19.7.3 窗口大小变化19.7.4 信号发生19.8 小结习题附录A 函数原型附录B 其他源代码附录C 习题答案参考书目BFQ第一章引言11引言所有操作系统都向它们运行的程序提供服务。典型的服务是执行一道新程序、打开一个文件、读一个文件、分配一个存储区、获得当前时间等等,本书的焦点是说明各种Unix操作系统版本所提供的服务。以严格的步进方式,不超前引用尚未说明过的术语来说明Unix几乎是不可能的(可能也会是令人厌烦的)。本章从程序设计人员的角度快速周游Unix,我们将对书中引用的一些术语和概念进行简要的说明并给示实例。在以后各章中,我们将对这些作更详细的说明。本章也对不熟悉Unix的程序设计人员介绍、概述Unix提供的各种服务。12登录(Logging ln)登录名当我们向Unix系统登录时,先键入登录名,然后键入口令字。系统在其口令文件,通常是/etc/passwd文件中查看我们的登录名。在口令文件中的登录项,由7个以冒号分隔的字段组成:登录名,加密口令字,数字用户ID(224),数字组ID(20),注释字段,起始目录(/home/stevens),以及shell程序(/bin/ksh)。很多比较新的系统已将加密口令字移到另一个文件中。在第六章,我们将说明这种文件,以及存取它们的函数。shell我们登录后,系统先典型地显示一些消息,然后我们就可以向shell程序键入命令。shell是一个命令行解释器,它读用户输入,然后执行命令,用户通常用终端,有时则通过文件(称为shell脚本)向shell进行输入。常用的shell是:Bourne shell,/bin/shCshell,/bin/cshKornshell,/bin/ksh系统从口令字文件中与我们相关的登录项的最后一个字段了解到应为我们执行那一个shell。自Version 7(第七版)以来,一直在使用Bourne shell,几乎每一个现存的Unix系统都提供Bourne shell。CShell是在Berkeley(贝克莱)开发的,所有BSD版本都提供这种shell。另外,Cshell也由AT&T系统V386 R32和系统VR4(SVR4)提供,(在下一章,我们将对这些不同的Unix版本作更多说明。KornShell是Bourne shell的后继者,它由SVR4提供。Kornshell在大多数Unix系统上运行,但在SVR4之前,通常它需要另行购买,所以没有其它两种shell流行。Bourne shell是由Steve Bourne在Bell实验室中开发的其控制流结构使人想起Algol68C Shell是在贝克莱由Bill Joy完成的,其基础是第6版shell(不是Bourne shell)。其控制结构很象C语言,它支持了一些Bourne shell没有提供的功能作业控制,历史机制和命令行编辑。Kornshell是由David Korn在Bell实验室中开发的,它兼容Bourne shell,并且也包含了使C shell非常流行的一些功能作业控制、命令行编辑等。在全书中,我们都会使用这种形式的注释以说明历史沿革,并对不同的Unix实现进行比较。当说明了历史缘由后,常常使得采用一种特定实现技术的理由变得清晰起来。在全书中,我们将使用很多shell实例,以执行我们已开发的程序,其中将应用Bourne shell和Kornshell都具有的功能。13文件和目录文件系统(Filesystem)Unix文件系统是目录和文件的一种分层次的安排,目录的起点称为根(root),其名字是一个字符/。一个目录是一个包含目录项的文件,在逻辑上,我们可以认为每个目录项都包含一个文件名,同时还包含说明该文件属性的信息。文件属性是:文件类型,文件长度,文件属主,文件的许可权(例如,其他用户能否存取该文件?)文件的最后修改时间等。stat和fstat函数返回一个包含所有文件属性的信息结构。在第四章中,我们将详细说明文件的各种属性。文件名(Filename)一个目录中的各个名字称为文件名。不能出现在文件名中的字符只有两个,它们是斜线(/)和空操作(null)字符,斜线分隔构成路径名(在下面说明)的各文件名,空操作符则终止一个路径名,尽管如此,一个好的习惯是只使用印刷字符的一个子集作为文件名字符(只使用子集的理由是:如果在文件名中使用了某些shell特殊字符,则必须使用shell的引号机制来引用文件名)。当创建一个新目录时,自动创建了两个文件名:(称为点)和(称为点点)。点引用当前目录,点点则引用文目录。在最高层次的根目录中,点点与点相同。某些Unix文件系统限制文件名的最大长度为14个字符,BSD版本则将这种限制扩展为255个字符。路径名(Palhname)0个或多个以斜线分隔的文件名序列(可以任选地以斜线开头)构成路径名,以斜线开头的路径名称为绝对路径名,否则称为相对路径名。实例不难列出一个目录中所有文件的名字,程序11是ls(1)命令的主要实现部分程序11列出一个目录中的所有文件ls(1)这种表示方法是Unix的惯用方法,用以引用Unix手册集中的一个特定项。它引用第一部分中的ls项,各部分通常用数字1至8表示,在每个部分中的各项则按字母顺序排列。在全书中,我们都假定你有一份你所使用的Unix系统的手册。较早的Unix系统把8个部分都集中在一本Unix程序手册中,现在的趋势是把这些部分分别按排在不同的手册中:一本是由用户使用的,一本是由程序员使用的,一本是由系统管理员使用的等等。某些Unix系统把一个给定部分中的手册页又用一个大写字母进一步分成若干小部分,例如,AT&T1990e中的所有标准I/O函数都被指明在3s部分中,例如fopen(3s)。某些Unix系统,例如以Xenix为基础的系统,不同数字将手册分成若干部分,代之,它们用C表示命令(第1部分),S表示服务(通常是第2、3部分)等等。如果你有联机手册,则阅看ls命令手册页的方法一般是:man 1 ls程序11只打印一个目录中各个文件的名字,不显示其它信息,如若该源文件名为mylsc,则我们可以用下面的命令对其进行编辑,编辑的结果送入系统默认名为aout的可执行文件名:cc mylsc某种样本输出是:$ aout /devMAKEDEVconsolettymemkmemnullprinter$ aout /var/spool/mqueuecant open /var/spool/mqueue:Permission denied$ aout /dev/ttycant open /dev/tty:Not a directory在全书中,我们都将以这种方式表示我们输入的命令以及其输出:我们输入的字符以这种字体表示程序输出则以另一种字体表示。如果我们欲对输出添加注释,则以表示注释,在我们输入之前的美元符号($)是shell打印的提示符,我们总是将shell提示符显示为$。注意,列出的目录项不是以字母序排列的,ls命令本身则一般以字母序列出目录项。在这20行程序中,有很多细节可以考虑:首先,其中包含了一个我们自己的头文件ourhdrh。在本书中,几乎每一道程序都包含此头文件。它包含了某些标准系统头文件,定义了许多常数及函数原型,这些都将用于本书的各个例子中,此头文件包含在附录B中。main函数的说明使用了ANSI C标准所支持的新风格。(在下一章中,我们将对ANSI C作更多说明。)我们取命令行的第1个参数argv作为要列表的目录名。在第七章中,我们将说明main函数是如何被调用的,程序如何存取命令行参数和环境变量。因为各种不同Unix系统的目录项的实际格式是不一样的,所以我们使用函数opendir,readdir和closedir处理目录。opendir函数返回指向DIR结构的指针,并将该指针传向readdir函数。我们并不关心DIR结构中包含了什么。然后,我们在循环中调用readdir,以读每个目录项。它返回一个指向dirent结构的指针,而当目录中已无目录项可读时则返回null指针。我们在dirent结构中取出的只是每个目录项的名字(dCD#*2name)。使用该名字,我们此后就可调用stat函数(42节)以决定该文件的所有属性。调用了两个我们自编的函数对错误进行处理:err-sys和err-quit。我们从上面的输出中可以看到,err-sys函数打印一条消息,说明遇到了什么类型的错误。(“Permission denied”或“Not a directory”(“许可权拒绝”或“不是一个目录”。)这两个出错处理函数也在附录B中说明,我们也将在17节中更多地叙述出错处理。当程序将结束时,它以参数O调用函数exit。函数exit终止一道程序。按惯例,参数O的意思是正常结束,参数值则表示出了一种错。在85节中,我们将说明一道程序(例如一个shell或我们所编写的程序)如何获得它所执行的另一道程序的exit状态。工作目录(Working Directory)每个进程都有一个工作目录(有时称为当前工作目录)。所有相对路径名都从工作目录开始解释。进程可以用chdir函数更改其工作目录。例如,相对路径名doc/memo/joe指的是文件joe,它在目录memo中,而memo又在目录doc中,doc则应是工作目录中的一个目录项。从该路径名可以看出,doc和memo都应当是目录,但是我们却不清楚joe是文件还是目录。路径名/urs/Lib/Lint是一个绝对路径名,它指的是文件(或目录)Lint,而Lint在目录lib中,lib则在目录usr中,usr则在根目录中。起始目录(Home directory)当我们登录时,工作目录设置为起始目录,该起始目录从口令字文件(见12节)中我们的记录项中取得。14输入和输出文件描述符(File Descriptors)文字描述符是一个小的非负整数,系统核用以标识一个特定进程正在存访的文件。无论何时,系统核打开一个现存文件或创建一个文件,它就返回一个文件描述符。当读、写文件时,我们就使用它。标准输入、标准输出和标准出错按惯例,每当运行一道新程序,所有的shell,都与其打开三个文件描述符:标准输入、标准输出以及标准出错。如若象简单命令ls那样,没有做什么特殊处理,则所有这三个都连向我们的终端。大多数shell都提供一种方法,使任何一个或所有这三个描述符都能重新定向到某一个文件,例如:lsfilelist执行ls命令,其标准输出重新定向到名为filelist的文件点。不用缓存的I/O函数open、read、write、lseek以及close提供了不同缓存的I/O。这些函数都用文件描述符进行工作。实例如若我们愿望从标准输入读,并写向标准输出,则程序12可以复制任一Unix文件。程序12将标准输入复制到标准输出头文件(ourhdrh中包含了此头文件)及两个常数STDIN和STDOUTFILENO是POSIX标准的一部分(在下一章,我们将对此作更多的说明)。很多Unix系统服务的函数原型,例如我们调用的read和write都在此头文件中。函数原型也是ANSI C标准的一部分,我们将在本章的稍后部分对此作更多说明。两个常数STDIN-FILENO和STDOUT-FILENO定义在头文件中,它们指定了标准输入和标准输出的文件描述符。它们的典型值是0和1,但是为了可移植性,我们将使用这些新名字。在39节中,我们将详细地讨论BUFSIZE常数,说明各种不同值将如何影响程序的效率。但是不管该常数的值如何,此程序总能复制任一Unix文件。read函数返回读得的字节数,此值用作为要写的字节数。当到了文件的尾端时,read返回0,程序停止执行。如果发生了一个读错误,read返回-1,出错时,大多数系统函数返回-1。如若编辑读程序,其结果送入标准的aout文件,并以下列方式执行它:aoutdata那么,标准输入是终端,标准输出则重新定向至文件data,标准出错也是终端。如果此输出文件并不存在,则shell创建它。在第三章中,我们将更详细地说明不用缓存的I/O函数。标准I/O标准I/O函数提供一种对不用缓存的I/O函数的带缓存的界面。使用标准I/O使我们无需担心如何选取最佳的缓存长度,例如程序12中的BUFSIZE常数。另一个使用标准I/O函数的优点与处理输入行有关(常常发生在Unix的应用中)。例如,fgets函数读一完整的行,而另一方面,read函数读指定字节数。我们最熟悉的标准I/O函数是printf。在调用printf的程序中,我们总是包括(通常包括在ourhdrh中),因为此头文件包括了所有标准I/O函数的原型。实例程序13的功能类似于调用read和write的前一道程序12,我们将在58中对程序13作更详细的说明。它将标准输入复制到标准输出,于是也就能复制任一Unix文件。程序13用标准I/O将标准输入复制到标准输出函数getc一次读1个字符,然后putc将此字符写到标准输出。读到输入的最后1个字节时,getc返回常数EOF。标准输入、输出常数stdin和stdout定义在头文件中,它们分别表示标准输入和标准输出文件。15程序和进程程序(Program)一道程序是存放在一个磁盘文件中的可执行文件。使用6个exec函数中的一个由核将程序读入存储器,并使其执行。我们将在89节中说明这些exec函数。进程和进程ID(Processes and Process ID)一道程序的一个执行实例被称为一个进程。在本书的几乎每一页中都会使用这一术语。某些操作系统用任务表示正被执行的程序。每个Unix进程都一定有一个唯一的数字标识符,被称之为进程ID。进程ID总是一非负整数。实例程序14打印其进程ID程序14打印进程ID如若编辑该程序,其结果送入aout文件,然后执行它,则有:$ aouthello world from process ID 851$ aouthello world from precess ID 854此程序运行时,它调用函数getpid得到其进程ID。进程控制有三个用于进程控制的主要函数:fork、exec和waitpid。(exec函数有六种变体,但我们经常把它们统称为exec函数。)实例程序15从标准输入读命令并执行它们Unix的进程控制功能可以用一个较简单的程序(程序15)说明,该程序从标准输入读命令,然后执行这些命令。这是一个类似于shell程序的基本实施部分。在这30行程序中,有很多功能可以思考:用标准I/O函数fgets从标准输入一次读一行,当作为行的第1个字符键入文件结束字符(通常是控制-D)时,fgets返回一个null指针,于是循环终止,进程也就终止。在第十一章中,我们将说明所有特殊的终端字符(文件结束、退格字符、擦除整行等等),以及如何改变它们。因为fgets返回的每一行都以新行符终止,后附一个null字节,我们用标准C函数strlen计算此字符串的长度,然后用一个null字节代换新行符。这一操作的目的是因为execlp函数要求的是以null结束的参数,而不是以新行符结束的参数。调用fork创建一个新进程,新进程是调用进程的复制品,我们称调用进程为父进程,新创建的进程为子进程。fork对父进程,返回新子进程的非负进程ID,对子进程则返回0。因为fork创建一新进程,所以我们说它被调用一次(由父进程),但返回两次(在父进程中和在子进程中)。在子进程中,我们调用execlp以执行从标准输入读入的命令。这使子进程更换了新的程序文件。fork和跟附其后的exec的组合是某些操作系统所称的产生一个新进程。在Unix中,这两个部分分成两个函数。在第八章中,我们将对这些函数作更多说明。子进程调用execlp执行新程序文件,而父进程希望等待子进程终止,这一要求由调用waitpid实现,其参数指定要等待的进程(在这里,pid参数是子进程ID)。waitpid函数也返回子进程的终止状态(status变量)。在此简单程序中,我们没有使用该值。如若需要,可以用此值精确地确定子进程是如何终止的。该程序的最主要限制是可能向执行的命令传递参数。例如我们不能指定要列表的目录名。我们只能对工作目录执行ls命令。为了传递参数,先要分析输入行,然后用某种约定把参数分开(很可能使用空格或制表符),然后将分隔后的各个参数传递给execlp函数。尽管如此,此程序仍可用来说明Unix的进程控制功能。如果运行此程序,则得下列结果。注意,该程序使用了一个不同的提示符(%)。$ aout% dateFri Jun 7 15:50:36 MST 1991% whostevens console Jun 5 06:01stevens ttyp0 Jun 5 06:02% pwd/home/stevens/doc/apue/proc% lsMakefileaoutshelllc% DWB键入我们的文件结束符$DW输出常规的shell提示16ANSI C本书中的所有实例都按ANSI C编写函数原型头文件包含了许多Unix系统服务的函数原型,例如我们已调用过的read,write和getpid函数。函数原型是ANSI C标准的组成部分。这些函数原型如下列形式:ssizeCD#*2t read(int,void *,sizeCD#*2t);ssizeCD#*2t write(int,void *,sizeCD#*2t);pidCD#*2t getpid(void);最后一个的意思是:getpid没有参数(void),返回值的数据类型pidCD#*2t。提供了这些函数原型后,编辑程序在编译时就可以检查我们在调用函数时是否使用了正确的参数。在程序14中,如果我们带一个参数调用getpid(如同在getpid(1)中一样),则我们将从ANSI C编辑程序得到下列形式的出错信息:line 8:too many arguments to function getpid另外,因为编辑程序知道参数的数据类型,所以如果可能,它就会将参数强制转换成所需的数据类型。类属指针从上面所示的函数原型中我们可以注意到的另一个区别是:read和write的第二个参数现在是void *类型。所有较早的Unix系统都使用char *这种指针类型。作这种更改的原因是:ANSI C使用void *作为类属指针,以代替char *。函数原型和类属指针相组合使我们消去了很多非ANSI C编辑程序需要的显式类型强制转换。例如,给出了write原型后,我们可以写成:float data;write (fd,data,sizeof(data);若使用非ANSI编程程序,或没有给出函数原型,则我们需写成:write(fd,(void *)data,sizgof(data);我们也将void *指针特征用于malloc函数(见78节)。malloc的原型现在是:void * malloc(sizeCD#*2t);这使得我们可以写下面的程序段:int * ptr;ptr=malloc (1000 * sizeof(int);它无需将返回的指针强制转换成int *类型。原始系统数据类型前面所示的getpid函数的原型定义了其返回值为pidCD#*2t类型。这也是POSIX中的新规定。Unix的早期版本规定此函数返回一整型。与此类似,read和write返回类型为SSIZECD#*2t的值,以及要求第3个参数的类型是SIZECD#*2t。以-t结尾的这些数据类型被称为原始系统数据类型。它们通常在头文件中定义(头文件应已包括该头文件)。它们通常以C typedef说明加以定义,typedef说明在C语言中已超过15年了(所以这并不要求ANSI C),它们的目的是阻止程序(在用专门的数据类型(例如int,short或long)以允许对于一种特定系统的每个实现,选择所要求的数据类型。在需要存储进程ID处,我们将分配类型为pidCD#*2t的一个变量。(注意,我们在程序15中,已对名为pid的变量这样做了。)在各种不同的实现中,这种数据类型的定义可能是不同的,但是这种差别现在只出现在一个头文件中。我们所需做的只是在另一个系统上重新编辑应用程序。17出错处理当Unix函数出错时,往常返回一个负值,而且整型变量errno通常设置为具有特定信息的一个值。例如,open函数如成功执行则返回一个非负文件描述符。如若出错则返回-1。在open出错时,有大约15种不同的errno值(文件下存在,许可权问题等)。某些函数使用不是返回负值的另一种约定。例如,返回一个指向一个对象的指针的大多数函数,在出错时,返回一个null指针。文件中定义了变量errno,以及可以赋与它的各种常数。这些常数都以E开头,另外,Unix手册第二部分的第一页是intro(2),它通常列出了所有这些出错常数。例如,若errno等于常数EACCES,这表示产生了许可权问题(例如,我们没有打开所要求文件的许可权)。POSIX定义errno为:extern int errno;POSIX1中errno的定义较C标准中的定义更为苛刻。C标准允许errno可以是一个宏,它扩认成可修改的整型左值(lvalue)(例如一个函数,它返回一个指向出错数的指针)。对于errno应当知道两个规则。第一个规则是:如果没有出错,则其值不会被一个例程消除。因此,仅当函数的返回值指明出错时,才检验其值。第二个规则是:任一函数都不会将errno值设置为0,在中定义的所有常数都不具值0。C标准定义了两个函数,它们帮助打印出错信息。#include char *strerror(int WTBXerrnumWTBZ);返回:指向消息字符串的指针此函数将errnum(它通常就是errno值)映射为一个出错信息字符串,并且返回此字符串的指针。perror函数在标准出错上产生一条出错消息(基于errno的当前值),然后返回。#include void perror(const char *msg);它首先输出由msg指向的字符串,然后是一个冒号,一个空格,然后是对应于errno值的出错信息,然后是一个新行符。实例程序16显示了这两个出错函数的使用方法。程序16例示strerror和perror如果此程序经编辑,结果送入文件aout,则有:$ aoutEACCES:Permission deniedaout:NO such file or directory注意,我们将程序名作为参数(argv,其值是aout)传递给perror。这是一个标准的Unix惯例。使用这种方法,如若程序是作为管道线的一部分执行的,如:prog 1 outputfile我们就能分清三个程序中的那一个产生了一条特定的出错消息。在本书中的所有实例基本上都不直接调用strerror或perror,而是使用在附录B中的出错函数。在该附录中的出错函数使用了ANSI C的可变参数表设施,用一条
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 专升本培训班知识点课件
- 皮革制品行业时尚趋势与市场消费行为分析
- 2025深圳公寓租房合同
- 中国银行湖州市德清县2025秋招笔试会计学专练及答案
- 中国银行苏州市昆山市2025秋招笔试英语阅读理解题专练30题及答案
- 邮储银行太原市迎泽区2025秋招笔试金融学专练及答案
- 邮储银行佳木斯市郊区2025秋招笔试英语选词填空题专练50题及答案
- 邮储银行阜新市细河区2025秋招笔试计算机基础专练及答案
- 邮储银行邵阳市隆回县2025秋招笔试计算机基础专练及答案
- 中国银行长治市长子县2025秋招英文结构化面试题库含答案
- 板栗脱壳机设计说明书
- 《黑曼巴传奇》课件
- 《电气材料》课件
- 2025届高考作文素材积累:高中五册课本素材及运用
- 学校思想政治教育管理制度
- 《设备管理台账》
- 人教版(2024新版)七年级上册 Unit1单元测试卷(含答案)
- 丽声北极星分级绘本五年级上教案:Stop!-Everyone-Stop
- 管理信息系统 课件 第6章 管理信息系统的战略规划
- PEP小学英语3-6年级单词(带音标)
- pymodbus-实例说明文档
评论
0/150
提交评论