UNIX环境高级编程(Unix标准化及实现)
尤晋元 译
第二章 Unix标准化及实现
21 引言
在使各种风格的Unix和C程序设计语言标准化方面已经做了很多工作。虽然Unix应用程序在不同的Unix版本之间进行移植是相当容易的,但是八十年代中Unix版本的剧增以及它们之间差别的扩大导致很多大工作(例如美国政府)要求对其进行标准化。
本章将介绍正在进行的各种标准化工作,然后讨论这些标准对本书所说明的实际Unix实现的影响。所有标准化工作的一个重要部分是对每种实现必须定义的各种限制的说明,所以我们将说明这些限制以及确定它们值的多种方法。
22 Unix标准化
221 ANSI C
在1989年后期,C程序设计语言的ANSI标准X3159-1989得到批准〔ANSI 1989〕。此标准已被采用为国际标准ISO/IEC 9899:1990。ANSI是美国国家标准学会,它是由制造商和用户组成的非赢利性组织。在美国,它是全面性的无偿标准交换站,在国际标准化组织(ISO)中是代表美国的成员。
ANSI C标准的意图是提供C程序的可移植性,使其能适合于大量不同的操作系统,而不只是Unix。此标准不仅定义C程序设计语言的语法和语义,也定义其标准库〔ANSI 1989第四章;Plauger 1992;Kernighan及Ritchie 1988中的附录B〕。因为很多新的Unix系统(例如在本书中说明的几个Unix系统)都提供C标准中说明的库函数,所以此库对我们来讲是很重要的。
按照该标准定义的各个头文件,可将该库分成15区。图21中列出了C标准定义的头文件,以及下面几节中说明的另外两个标准(POSIX1和XPG3)定义的头文件。在其中也列举了哪些头文件是SVR4和43+BSD所支持的。我们也将在本章中对这两种Unix实现进行说明。
222IEEE POSIX
POSIX是一个由IEEE(电气和电子工程师学会)制订的标准族。POSIX的意思是计算机环境的可移植操作系统界面(Portable Operating System Interface for Computer Environment)。
它原来指的只是IEEE标准10031—1988(操作系统界面),但是,IEEE现正在制订POSIX族中的其它有关标准。例如,10032将是针对shell和公用程序的标准,10037将是系统管理方面的标准。在1003工作组中有15个以上的子委员会。
与本书特别有关的是10031操作系统界面标准,该标准定义了“POSIX依从的”操作系统必须提供的服务。虽然10031标准是以Unix操作系统为基础的,但是它又不限定于Unix和类似于Unix的系统。确实,有些供应专有操作系统的制造商也声称这些系统将依从POSIX(同时还保有它们的所有专有功能)。
由于10031标准说明了一个界面而不是一种实现,所以并不区分系统调用和库函数。所有在标准中的例程都被称为函数。
标准是不断演变的,10031标准也不例外。该标准的1988版,IEEE 10031—1988经修改后递交国际标准化组织(ISO),没有增加新的界面或功能,但修改了文本。最后的文档作为IEEE Std10031—1990正式出版〔IEEE 1990〕,这也就是国际标准ISO/IEC 9945—1:1990。该标准通常被称之为POSIX1,我们将在本书中使用此标准。
IEEE 10031工作组此后对其又作了更多更改,它们应在1993被批准。这些更改(现在称之为10031a)应由IEEE作为IEEE标准10031—1990的附件出版,这些更改也对本书有所影响,主要是因为贝克莱风格的符号链接很可能将被加到标准中作为一种所要求的功能。这些更改也很可能成为ISO/IEC 9945—1:1990的一个附录。在本书中,我们用注释的方法来说明POSIX1的10031a版本,指出哪些功能很可能会加到10031a中。
POSIX1没有包括“超级用户”这样的概念。代之以规定某些操作要求“适当的优先权”,POSIX1将此术语的定义留由具体实现进行解释。某些符合国防部安全性指导原则要求的Unix系统具有很多不同的安全级。在本书中,我们仍使用传统的Unix术语,并指明要求超级用户特权的操作。
图21 由各种标准和实现定义的头文件
223 X/Open XPG3
X/OPEN是一个国际计算机制造商组织。提出了一个七卷布可移植性指南,移为X/Open可移植性指南,第三版〔X/Open 1989〕,我们将称之为XPG3。XPG3的第二卷(XSI系统界面和头文件)对类似Unix的系统定义了一个界面,该界面定义是在IEEE Std10031—1988界面的基础上制订的。XPG3包含了一些POSIX1没有的功能。
例如,一个POSIX1没有但XPG3却有的功能是X/Open的消息设施。该设施可由应用程序使用以在不同的语言中显示文件消息。
XPG3界面使用了ANSI C草案而不是最后的正式标准,所以在XPG3界面规格说明中包含的某些功能是不再使用的。这些问题很可能会在将来的XPG规格说明的新版本中解决。(有关XPG4的工作正在进行,很可能会在1993年完成)。
224 FIPS
FIPS的含意是联邦信息处理标准(Federal Information Processing Standard),这些标准是由美国政府出版的,并由美国政府用于计算机系统的操购。FIPS151—1(19894)是基于IEEE Std 10031—1988及ANSI C标准草案的。FIPS 151—要求某些POSIX1规定为可选的功能。这种FIPS有时称为POSIX1 FIPS。其255节列出了FIPS所要求的POSIX1的可选项。
POSIX1 FIPS的影响是:它要求任一希望向美国政府销售POSIX1依从的计算机系统的厂商应支持POSIX1的某些可选功能。我们将不把POSIX1 FIPS视作为另一个标准,因为实际上它只是一个更加严格的POSIX1标准。
23 Unix实现
上面一节说明了三个由各自独立的组织所制定的标准:ANSI C、IEEE POSIX以及X/Open XPG3。但是,标准只是界面的规格说明。这些标准是如何与现实世界相关连的呢?这些标准由制造商采用,然后转变成具体实施。本书中我们感兴趣的是这些标准和它们的具体实施两者。
在Leffler等著作〔1989〕的11节中给出了Unix族树的详细历史和关系图。Unix的各种版本和变体都起源于在PDP-11系统上运行的Unix分时系统第6版(1976)和第7版(1979)(通常称为Version 6和Version 7)。这两个版本是在贝尔实验室以外首先得到广泛应用的Unix系统。从这棵树上发展出三个分支:(a)AT&T分支,从此导出了系统Ⅲ和系统Ⅴ(被称之为Unix的商用版本),(b)加州大学贝克莱分校分支,从此导出4XBSD实现,(c)由AT&T贝尔实验室的计算科学研究中心不断开发的Unix研究版本,从此导出第8、第9和第10版。
231 系统Ⅴ第4版
系统Ⅴ第4版(SVR4)是AT&T Unix系统实验室的产品,它汇集了下列系统的功能:AT&T Unix系统Ⅴ第32版(SVR32),Sun Microsystem的Sunos系统,加州大学贝克莱分校的43 BSD以及Microsoft的Xenix系统。(Xenix是在Verson7基础上开发的,后来又采用了很多系统Ⅴ的功能。)其源代码于1989年后期分布,在1990年则开始向最终用户提供。SVR4符合于POSIX
10031标准和X/OPEN XPG3标准。
AT&T也制版了系统Ⅴ界面定义(SVID)〔AT&T 1989〕。SVID第三版说明了Unix系统要达到SVR4质量要求所应提供的功能。如同POSIX1一样,SVID说明了一个界面,而不是一种实现。
对于一个具体实现的4应查看其参考手册,以了解其不同之处。〔AT&T 1990e〕。SV包含了BSD的兼容库〔AT&T 1990c〕,它提供了功能与43BSD对应部分相同的函数和命令。但是其中某些函数与POSIX的对应部分有所不同,本书中的所有SVR4实例都没有此兼容库。只有在你有一些早期的应用程序,又不想改变它们时才使用此兼容库,新的应用程序不应使用它。
232〓43+BSD
BSD是由加州大学贝克莱分校的计算机系统研究组研究开发和分发的。42BSD在1983年问世,43BSD则在1986年。这两个版本都在VAX小型机上运行。它们的下一个版本43BSD Tahoe在1988年发布,在一台称为Tahoe的小型机上运行〔Leffler等的著作〔1989〕说明了43BSD Taboe版。〕其后又有1990年的43BSD Reno版,它支持很多POSIX1的功能。下一个主要版本44BSD应在1992年发布。
原来的BSD系统包含了AT&T专有的源代码,它们需要AT&T许可证。为了获得BSD系统的源代码,首先需要持有AT&T源代码正被代换成非AT&T源代码,很多加到BSD系统上的新功能也来自非AT&T方面。
在1989年,贝克莱将43BSD Taboe中很多非AT&T源代码包装成BSD网络软件,10版,并使其成为公众可用的软件。其后则有BSD网络软件的20版,它是从43BSD Reno版导出的其目的是使大部分(如果不是全部的话)44 BSD系统不再受AT&T许可证的限制,于是其全部源代码都可为公众使用。正如我们在前言中所说明的,在全书中,我们用术语43+BSD来指本书所说明的BSD系统,该系统位于BSD网络软件20版和将出现的44BSD之间。在贝克莱所进行的Unix开发工作是从PDP-11开始的,然后转移到VAX小型机上,然后又转移到工作站上。在九十年代早期,贝克莱得到支持在广泛得到应用的80386个人计算机上开发BSD版本,结果产生了386BSD。这一工作是由Bill Jolitg完成的。其相关文档是发表在1991年的DrDobb′s Journal上的系列文章(每月一篇)。其中很多代码出现在BSD网络软件,20版中。
24〓标准和实现的关系
我们已提及的标准定义了任一实际系统的子集。虽然IEEE POSIX正致力于在其它所需方面(例如,网络界面,进程间的通信,系统管理)制订出标准,但在编著本书时,这些标准还并不存在。
本书的注意力集中于说明两个实际的Unix系统:SVR4和43+BSD。因为这两个系统都宣称是依从POSIX的,所以我们一方面集中于说明POSIX1标准所要求的功能,同时POSIX和这两个系统具体实现之间的差别,为此,SVR4或43+BSD特有的功能和例程都被清楚地标记出来。因为XPG3是POSIX1的超集,所以我们叙述了属于XPG3,但不属于POSIX1的功能。
应当了解,SVR4和43+BSD都提供了对它们早期版本功能的兼容性(例如SVR32和43BSD)。例如,SVR4和POSIX规格说明中的非阻塞I/O(O[CD#*2]NONBLOCK)以及传统的系统Ⅴ方法(O[CD#*2]NDELAY)都提供了支持。在本书中,我们将只使用POSIX1的功能,但是也会提及它所代换的是哪一种非标准功能。与此相类似,SVR32和43BSD以某种方法提供了可靠信号机制,这种方法也有别于POSIX1标准。在第十章中,我们只说明POSIX1的信号机制。
25 限制
有很多由实现定义的幻数和常数。其中有很多已被编写到程序中,或由特定的技术所确定。由于大量标准化工作的努力,已经提供了若干种可移植的方法以确定这些幻数和实现定义的限制。这非常有助于软件的可移植性。
三种类型的功能是需要的:
·编辑时的可选项(该系统是否支持作业控制?)
·编辑时的限制(短整型的最大值是什么?)
·运行时的限制(文件名的最大字符数?)
前两个,编辑时的可选项和限制可在头文件中定义。程序在编辑时可以包含这些头文件。但是,运行时的限制则要求进程调用一个函数以获得此种限制值。
另外,某些限制在一个给定的实现中可能是固定的(因此可以静态地在一个头文件中定义),而在另一个实现上则可能是变动的(需要有一个运行时的函数调用)。这种在型限制的一个例子是文件名的最大字符数。系统Ⅴ由于历史原因只允许文件名有14个字符,而贝克莱类的系统则将此增加为255。SVR4允许我们对每一个我们所创建的文件系统指定,它是系统Ⅴ文件系统还是BSD文件系统,而每个系统有不同的限制。这就是运行时限制的一个实例,文件名的最大长度依赖于所述及的文件处在那个文件系统中。例如,在根文件系统中的一个文件名其长度限制可能是14个字符,而在某个其它文件系统中的一个文件,其文件名长度限制可能是255个字符。
为了解决这些问题,提供了三种限制:
1编辑时的可选项及限制。
2不与文件或目录相关联的运行时限制。
3与文件或目录相关联的运行时限制。
使事情变得更加复杂的是,如果一个特定的运行时限制在一个给定的系统上并不改变,则可将其静态地定义在一个头文件中,但是,如果没有将其定义在头文件中,则应用程序就必须调用三个conf函数中的一个(我们很快就会对它们进行说明)。以确定其在运行时的值。
251 ANSI C限制
所有由ANSI C定义的限制都是编辑时的限制。图22中列示了在文件中定义的C标准限制。这些常数总是定义在该头文件中,而且在一个给定系统中并不会改变。在第三列中列出了ANSI C标准可接受的最小值。这用于整型长度为16位的系统它使用1的补码表示。在第四列中列出了整型长度为32位的当前系统的值,用的是2的补码表示法。注意,对不带
符号的数据类型都没有列出其最小值,它们都应为0。
我们将会遇到的一个区别是系统是否提供带符号(signed)或不带符号的
(unsigned)的字符值,从图22中的第四列可见,该特定系统使用带符号字符。从表中可以看到CHAR[CD#*2]MIN等于SCHAR[CD#*2]MIN,CHAR[CD#*2]MAX等于SCHAR[CD#*2]MAX。如果系统使用不带符号字符,则CHAR[CD#*2]MIN等于0,CHAR[CD#*2]MAR等于UCHAR[CD#*2]MAX。
在头文件中,对浮点数据类型也有类似的一组定义。
我们会遇到的另一个ANSI C常数是FOPEN[CD#*2]MAX,这是实现保证的可同时打开的标准I/O流的最小数,该值在头文件中,其最小值是8。POSIX1中的值STREAM[CD#*2]MAX(若定义的话),则应具与FOPEN[CD#*2]MAX相同的值。
ANSI C在中也定义了常数TMP[CD#*2]MAX,这是由tmpnam函数产生的唯一文件名的最大数。关于此常数我们将在513节中进行更多说明。
图22 中的整型值大小
252 POSIX限制
POSIX定义了很多涉及操作系统实现限制的常数,不幸,这是POSIX1中最使人迷惑部分中的一个。
有33个限制和常数,它们被分成下列八类:
1不变的最小值(图23中的13个常数)。
2不变值:SSIZE[CD#*2]MAX。
3运行时不能增加的值:NGROUPS[CD#*2]MAX。
4运行时不变的值(可能不确定):
AGE[CD#*2]MAX,CHILD[CD#*2]MAX,OPEN [CD#*2]MAX,STREAM[CD#*2]MAX以及TZNAME[CD#*2]MAX。
5路径名可变值(可能不确定):
LINK[CD#*2]MAX,MAX[CD#*2]CHNON,MAX[CD#*2]INPUT,NAME[CD#*2]MAX,PATH[CD#*2]MAX以及PIPE[CD#*2]BUF。
6编辑时符号常数:
[CD#*2]POSIX[CD#*2]SAVED[CD#*2]IDS,[CD#*2]POSIX[CD#*2]VERSION以及
[CD#*2]POSIX[CD#*2]JOB[CD#*2]CONTROL。
7执行时符号常数:
[CD#*2]POSIX[CD#*2]NO[CD#*2]TRONC,[CD#*2]POSIX[CD#*2]VDISABLE以及[CD#*2]POSIX[CD
#*2]CHOWN[CD#*2]RESTRICTED。
8不再使用的常数:CLK[CD#*2]TCK。
在这33个限制和常数中,15个总是定义的,其余的则按具体条件可定义可不定义。
在254中在说明sysconf,pathconf和fpatheonf函数时说明可定义可不定义的限制和常数(第4-8条)。在图27中我们摘录了所有限制和常数。13个不变最小值则示于图23中。
图23 中的POSIX1不变最小值
这些值是不变的〖CD2〗它们并不附系统而改变。它们指定了这些特征方面的最严格的值。一个符合POSIX1的实现应当提供至少这样大的值。这就是为什么将它们称为最小的原因,虽然它们的名字都包含了MAX。另外,一个可移植的应用程序不应要求更大的值。我们将在本书的适当部分说明这些这些常数中每一个的含意。不幸的是,这些不变最小值中的某一些在实际应用中是太小了。例如,现时的Unix系统所提供的每个进程可同时打开文件数远超过16,即使是1978年的Version 7也向每个进程提供了20个打开文件。另外,[CD#*2]POSIX[CD#*2]PATH[CD#*2]MAX的最小值限制255也是太小了,路径名可能会超过这一限制。这意味着我们在编辑时不能使用这两个常数[CD#*2]POSIX[CD#*2]OPEN[CD#*2]MAX和[CD#*2]POSIX[CD#*2]PATH[CD#*2]MAX作为数组长度。
图23中的13个不变最小值的每一个都有一个相关的实现值,其名字是将图23中的名字删除前缀[CD#*2]POSIX[CD#*2]后构成的。(这13个实现值是我们在本节开始部分所列出的2-5项:不变值、运行时不能增加的值、运行时不变的值、以及路径名可变值。)问题是并不保证所有这13个实现值定义在头文件中。一个特定值可能不定义在此头文件中的理由是:例如对一个给定进程的实际值可能依赖于系统的存储器总量。如果没有在头文件中定义它们,则我们就不能在编辑时使用它们作为数组边界。所以,POSIX1决定提供三个运行时函数供我们调用,它们是:syseonf,pathconf以及fpathconf,用它们可以在运行时得到实际的实现值。但是,还有一个问题,因为其中某些值是由POSIX1定义为“可能不确定的”(逻辑上无限的),这就意味着该值没有实际上限。例如,SVR4的每个进程打开文件数限制在假想上是无限的,所以在SVR4中OPEN[CD#*2]MAX被认为是不确定的。在257中我们还将讨论运行时不确定限制的问题。
253 XPG3限制
XPG3定义了七个常数,它们总是包含在头文件中。POSIX1则会把它们称之为不变最小值。它们列于图24中。这些值的大多数都涉及消息。
图24 XPG3不变最小值(在中)
XPG3也定义了值PASS[CD#*2]MAX,作为口令字中的最大有效字符数(不包括终止字符null),它可能包含在中。POSIX1则把它称之为运行时不变的值(可能不确定),其最小
可接受的值是8。PASS[CD#*2]MAX值也可在运行时用sysconf函数取得,该函数将在254中说明。
254 sysconf、pathconf以及fpathconf函数
我们已列出了一个实现必须支持的各种最小值,但是怎样才能找到一个特定系统实际支持的限制值呢?正如我们在前面提到的,某些限制值在编辑时是可用的,而另外一些则必须在运行时确定。我们也曾提及在一个给定的系统中某些限制值是不会更改的,而其它则与文件和目录相关联。运行时限制是由调用下面三个函数中的一个而取得的。
#include
long sysconf(int [WTBX]name[WTBZ]);
log pathconf(const char *[WTBX]pathname[WTBZ],int [WTBX]name[WTBZ]);
log fpathconf(int [WTBX]filedes[WTBZ],int [WTBX]name[WTBZ]);
All three return:corresponding value if OK,-1 on error (see later)
最后两个函数之间的差别是一个用路径名作为其参数,另一个则取文件描述符作为参数。
图25中列出了这三个函数所使用的name参数。以[CD#*2]SC[CD#*2]开始的常数用作为sysconf的参数,而以[CD#*2]PC[CD#*2]开始的常数则作为pathconf或fpathconf的参数。
对于pathconf的参数pathname,fpathconf的参数filedes有很多限制。如果不满足其中任何一个限制,则结果是未定义的。
1[CD#*2]PC[CD#*2]MAX[CD#*2]CANON,[CD#*2]PC[CD#*2]MAX[CD#*2]INPUT以及[CD#*2]PC[CD#*2]VDISABLE所涉及的文件必须是终端文件。
2[CD#*2]PC[CD#*2]LINK[CD#*2]MAX所涉及的文件可以是文件或目录。如果这是一个目录,则返回值用于目录本身(不用于目录内的文件名项)。
3[CD#*2]PC[CD#*2]NAME[CD#*2]MAX和[CD#*2]PC[CD#*2]NO[CD#*2]TRUNC所涉及的文件必须是目录,返回值用于该目录中的文件名。
4[CD#*2]PC[CD#*2]PATH[CD#*2]MAX涉及的必须是目录。当所指定的目录是工作目录时,返回值是相对路径名的最大长度。(不幸的是,这不是我们想要知道的一个绝对路径名的实际最大长度,我们将在257中再回到这一问题上来)
5[CD#*2]PC[CD#*2]PIPE[CD#*2]BUF所涉及的文件必须是管道,FIFO或目录。在管道或FIFO情况下,返回值是对所涉及的管道或FIFO的限制值。对于目录,则返回值是对在该目录中创建的任一FIFO的限制值。
6[CD#*2]PC[CD#*2]CHOWN[CD#*2]RESTRICTED必须是文件或目录。如果它是目录,则返回值指明此可选项是否适用于该目录中的文件。
图25 对sysconf、pathconf和fpathconf的限制及name参数
我们需要更详细地说明这三个函数的不同返回值。
1如果name不是图25第3列中的一个合适常数,则所有这三个函数都返回-1,并将error设置为EINVAL。
2包含MAX的12个名字以及名字[CD#*2]PC[CD#*2]PIPE[CD#*2]BUF可能或者返回该变量的值(返回值ZO),或者返回-1,这表示该值是不确定的,此时并不更改errno的值。
3对[CD#*2]SC[CD#*2]CLK[CD#*2]TCK的返回值是每秒的时钟滴答数,以用于times函数的返回值(815节)。
4对[CD#*2]SC[CD#*2]VERSION的返回值以4位数,2位数分别表示以标准的年、月。这可能或者是198808L,或199009L,或此标准某个以后版本的值。
5对[CD#*2]SC[CD#*2]XOPEN[CD#*2]VERSION的返回值表示此系统所遵从的XPG版本,其当前值是3。
6[CD#*2]SC[CD#*2]JOB[CD#*2]CONTROL和[CD#*2]SC[CD#*2]SAVED[CD#*2]IDS是两个可选功能。若sysconf返回-1(没有更改errno)则不支持相应的功能。这两个功能也可在编辑时从头文件中决定。
7对[CD#*2]PC[CD#*2]CHOWN[CD#*2]RESTRICTED和[CD#*2]PC[CD#*2]NO[CD#*2]TRUNC的返回值若为-1(不改变errno),则表示对所指定的pathname或filedes不支持此功能。
8对[CD#*2]PC[CD#*2]VDISABLE的返回值若为-1(不改变errno),则表示对所指定的pathname或filedes不支持此功能。若支持此功能,则返回值是被用于禁止特定终端输入字符的字符值(图16)。
实例
程序21打印所有这些限制,并处理一个限制未被定义的情况。
程序21〓打印所有可能的sysconf和pathconf值
我们条件地包括了两个常数,它们已被加至POSIX1,但不是IEEEStd 10031—1988版本
的一部分。图26显示了在几个不同的系统上,程序21的样本输出。表中的“no def”表示该常数未定义。我们在414中可以了解到,SVR4 S5文件系统是可以回逆到Version 7的传统系统Ⅴ文件系统。UFS是贝克莱快速文件系统的SVR4实现。
图26〓配置限制的实例
255〓FIPS 151-1要求
FIPS 151-1标准(我们已在224节中提及)由于要求下列功能,所以它比POSIX1标准更严:
·要求下列POSIX1可选功能:
[CD#*2]POSIX[CD#*2]JOB[CD#*2]CONTROL,[CD#*2]POSIX[CD#*2]SAVED[CD#*2]IDS,[CD#*2]POSIX[CD#*2]NO[CD#*2]TRONC,[CD#*2]POSIX[CD#*2]CHOWN[CD#*2]RESTRICTED和[CD#*2]POSIX[CD#*2]VDISABLE。
·NGROUPS[CD#*2]MAX的最小值是8。
·新创建的文件或目录的组ID应设置为它所在目录的组ID(在46节中说明此功能)
·在已传输了一些数据后,若read或write被一个捕捉到的信号所中断,则这些函数应返回已被传输的字节数(在105节中讨论被中断的系统调用)。
·登录shell应定义环境变量HOME和LOGNAME。
因为美国政府购买很多计算机系统,所以大多数POSIX的制造商都将支持这些增加的FIPS要求。
图27〓编辑时和运行时限制的摘要
256〓限制摘要
我们已说明了很多限制和幻常数,其中某些总被包含在一头文件中,某些可选地包含在头文件中,其它则可在运行时决定。图27以字母序摘要列出了所有这些常数以及得到它们值的各种方法。以[CD#*2]SC[CD#*2]开始的运行时名字是sysconf函数的参数,以[CD#*2]PC[CD#*2]开始的名字是pathconf和fpathconf函数的参数,如果它有最小值,则也将其列于其中。
注意,图23中的13个POSIX1不变最小值示于图27中的最右一列。
257〓未确定的运行时限制
我们已提及图27中的某些值可能是未确定的,这些值是第三列标记为可选的(optional),其名字中或包含MAX,或是PIPE[CD#*2]BUF。我们遇到的问题是如果这些值没有在头文件中定义,那么在编辑时间我们也就不能使用它们。但是,如果它们的值是未确定的,那么在运行时间,它们可能也是未定义的。让我们观察两个特殊的例子〖CD2〗为一个路径名分配存储器,以及决定文件描述符数
路径名
很多程序需要为路径名分配存储器,典型地,在编辑时就为其分配了存储器,而且使用了各种幻数(其中很少值是正确的)作为数组长度:256,512,1024或标准I/O常数BUFSIZ在头文件中的43BSD常数MAXPATHLEN是正确值,但是很多43BSD应用程序并未用综。
POSIX1试图用PATH[CD#*2]MAX值来帮助我们,但是如果此值是不确定的,那么仍是毫无帮助的。程序22是一个我们在全书中都将使用的为路径名动态地分配存储器的函数。
如若在中定义了常数PATH[CD#*2]MAX,那么就没有任何问题,如果没有,则需调用pathconf。因为pathconf的返回值是把第一个参数视为基于工作目录的相对路径名。所以我们指定根为第一个参数,并将得到的返回值加1作为结果值。如果pathconf指明PATH[CD#*2]MAX是不确定的那么我们就只得猜测某个值,在调用malloc时+1,是为了在尾端加字符串结束符null字符(PATH[CD#*2]MAX没有考虑综)。
处理不确定结果情况的正确方法与如何使用分配到存储空间有关。例如,如果我们为getcwd调用分配空间,(返回当前工作目录的绝对路径名,见422节)而分配到的空间太小,于是返回一个出,errno设置为ERANGE。然后我们可调用realloc以增加分配空间(见78节和练习418)并再试。我们可以不断这样做,直至getcwd调用成功执行。
程序22〓为路径名动态地分配空间
最大打开文件数
在精灵进程(是在后台运行,不与终端相连接的一种进程)中一个常见的代码序列是关闭所有打开文件。某些程序中有下列形式的代码序列:
#include
for(i=0;i
close(i);
这段程序假定在头文件中定义了常数NOFILE。另外一些程序则使用某些版本提供作为上限的常数[CD#*2]NFILE。某些程序则直接将其上限值定为20。
我们希望用POSIX1的OPEN[CD#*2]MAX确定此值以提高可移植性,但是如果此值是不确定的,则仍然有问题,如果我们使用下列代码
#include
for(i=0;i
close(i);
而且如果OPEN[CD#*2]MAX是不确定的,那么sysconf将返回-1,于是,for循环根本不会执行。在这种情况下,最好的选择就是关闭所有描述符直至某个任意的限制值(例如256)。如同上面的路径名一样,这并不能保证在所有情况下都能正确工作,但这却是我们所能选择的最好方法。我们在程序23中使用了这种技术。
程序23〓确定文件描述符数
我们可以耐心地调用close,直至得到一个出错返回,但是从close出错返回(EBADF)并不区分无效描述符和并未打开的描述符。如果我们试用此技术,而且描述符9未打开,而描述符10打开了,我们将停止在9上,而不会关闭10。dup函数(312节)在超过了OPEN[CD#*2]MAX时会返回一个特定的出错值,但是用复制一个描述符一、二百次的方法来确定此值是一种极端的方法。
SVR4和43+BSD的getrlimit函数(711节)以及43+BSD的getdtablesize(2)函数返回一个进程可以打开的最大描述符数,但是调用这两个函数不是可移植的。
OPEN[CD#*2]MAX被POSIX称为运行时不变值,其意思是在一个进程的生命期其值不应被改变,但是在SVR4和43+BSD之下,我们可以调用setrlimit(2)函数更改一个运行进程的这一值(此值也可用C shell的limit命令改变,用Bourne shell和Kornshell的limit命令更改)如果我们的系统支持这种功能,则可以将程序23更改为每次调用此程序时就调用sysconf,而不只是第一次调用此程序时。
26〓功能测试宏
正如前述,在头文件中定义了很多POSIX1和XPG3的符号。但是除了POSIX1和XPG3定义外,大多数实现在这些头文件中也加上了它们自己的定义。如果在编辑一道程序时,希望它只使用POSIX定义而不使用任何实现定义的限制,那么我们就需定义常数[CD#*2]POSIX[CD#*2]SOURCE,所有POSIX1头文件中都使用此常数,当该常数定义时,就能排除任何实现专有的定义。
常数[CD#*2]POSIX[CD#*2]SOURCE及其对应的常数[CD#*2]XOPEN[CD#*2]SOURCE被称之为功能测试宏,所有功能测试宏都以下划线开始,当要使用它们时,通常在CC命令行中以下列方式定义它们:
CC -D[CD#*2]POSIX[CD#*2]SOURCE filec
这使得在C程序包括任何头文件之前,定义了功能测试宏。如果我们只要使用POSIX1定义,那么也可将源文件的第一行设置为:
#define [CD#*2]POSIX[CD#*2]SOURCE 1
另一个功能测试宏是:[CD#*2][CD#*2]STDC[CD#*2][CD#*2],它是由符合ANSI C标准的编辑程序自动定义的。这样就允许我们编写ANSI C编辑程序和非ANSI C编辑程序都能编辑的程序。例如,在一个头文件可能会是:
# ifdef [CD#*2]STDC[CD#*2]
void *myfunc(const char *,int);
#else
void *myfunc();
#endif
这样就能发挥ANSI C原型功能的长处,要注意在开始和结束处的两个连续的下划线常常打印成一个长下划线(如同上面一个样本源代码中一样)。
27〓基本系统数据类型
历史上,某些Unix变量已与某些C数据类型联系在一起,例如,主、次设备号在历史上存放在一个16位的短整型中,用8位表示主设备号,另外8位表示次设备号。但是,很多较大的系统需要用多于256个值来表示其设备号,于是,就需要有一种不同的技术。(确实,SVR4用32位表示设备号:14位用于主设备号,18位用于次设备号)。
头文件中定义了某些与实现有关的数据类型,它们被称之为基本系统数据类型。有很多这种数据类型定义在其它头文件中。在头文件中这些数据类型都是用C的typedef设施来定义的。它们绝大多数都以[CD#*2]t结尾。图28中列出了本书将使用的基本系统数据类型。
图28〓基本系统数据类型
用这种方式定义了这些数据类型后,我们在编辑时就不再需要考虑附系统不同而变的实施细节,在本书中涉及到这些数据类型处,我们会说明为什么使用它们。
28〓标准之间的冲突
就整体而言,这些不同的标准之间配合得是相当好的。但是我们也很关注它们之间的差别,特别是ANSIC标准和POSIX1之间的差别。(因为XPG3是一个较尽的正在被修订的标准,FIPS则是一个要求更严的POSIX1。)
ANSI C定义了函数clock,它返回进程使用的CPU时间量,返回值是clock[CD#*2]t类型值。为了将此值变换成以秒为单位,将其除以在头文件中定义的CLOCKS[CD#*2]PER[CD#*2]SEC。POSIX1定义了函数times,它返回其调用者及其所有终止子进程的CPU时间以及时钟时间,所有这些值都是clock[CD#*2]t类型值。IEEEStd、10031—1988将符号CLK[CD#*2]TCK定义为每秒滴答数,上述clock[CD#*2]t值都是以此度量的。而1990 POSIX1标准中则说明不再使用,CLK[CD#*2]TCK应当使用sysconf函数来获得每秒滴答数,并将其用于times函数的返回值。术语是同一个,每秒滴答数,但ANSI C和POSIX1的定义却不同。这两个标准也用同一数据类型(clock[CD#*2]t)来保存这些不同的值,这种差别可以在SVR4中看到,其中clock返回微秒数(CLOCK[CD#*2]PER[CD#*2]SEC是一百万),而CLK[CD#*2]TCK通常是50,60或100(与CPU类型有关)。
另一个可能产生冲突的区域是:在ANSI C标准说明函数时,ANSI C所说明的函数可能会没有考虑到POSIX1的某些要求。有些函数在POSIX环境下可能要求有一个与C环境下不同的实现,因为POSIX环境中有多个进程,而C语言环境则很少会考虑宿主操作系统。尽管如此,很多POSIX依从的系统为了兼容性的关系也实现ANSI C函数,signal函数就是一个例子。如果我们在不了解的情况下使用了SVR4所提供的signal函数(希望编写可在ANSI C环境和较早Unix系统中运行的可兼容程序),那么它提供了与POSIX1 sigaction函数不同的语义。在第十章中我们会对signal函数作更多说明。
29〓摘要
在过去几年中,在Unix不同版本的标准经方面已经有了很大进展。本章对三个主要标准—ANSI C、POSIX和XPG3—进行了说明,也分析了这些标准对本书主要关注的两个实现:SVR4和43+BSD所产生的影响。这些标准都试图定义一些可能附实现而更改的参数,但是我们已经看到这些限制是并不完善的。在本书中,我们会涉及所有这些限制和幻常数。
在本书最后的参数书目中,说明了如何订购这些标准的方法。[LM]
发布人:netbull 来自:LinuxAid