UNIX标准化及实现

UNIX环境高级编程--第2章 Unix标准化及实现(一)

2.1引言

在使各种风格的Unix和C程序设计语言标准化方面已经做了很多工作。虽然Unix应用程序在
不同的Unix版本之间进行移植是相当容易的,但是八十年代中Unix版本的剧增以及它们之
间差别的扩大导致很多大工作(例如美国政府)要求对其进行标准化。

本章将介绍正在进行的各种标准化工作,然后讨论这些标准对本书所说明的实际Unix实现
的影响。所有标准化工作的一个重要部分是对每种实现必须定义的各种限制的说明,所以
我们将说明这些限制以及确定它们值的多种方法。 

 2.2Unix标准化

2.2.1〓ANSI C

在1989年后期,C程序设计语言的ANSI标准X3 159-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区。图2 1中列出了C标准定义的头文件,
以及下面几节中说明的另外两个标准(POSIX 1和XPG3)定义的头文件。在其中也列举了哪些
头文件是SVR4和4 3+BSD所支持的。我们也将在本章中对这两种Unix实现进行说明。

2.2.2〓IEEE POSIX

POSIX是一个由IEEE(电气和电子工程师学会)制订的标准族。POSIX的意思是计算机环境的
可移植操作系统界面(Portable Operating System Interface for Computer Environment
)。它原来指的只是IEEE标准1003.1-1988(操作系统界面),但是,IEEE现正在制订POSIX族
中的其它有关标准。例如,1003.2将是针对shell和公用程序的标准,1003.7将是系统管理
方面的标准。在1003工作组中有15个以上的子委员会。

与本书特别有关的是1003.1操作系统界面标准,该标准定义了"POSIX依从的"操作系统必须
提供的服务。虽然1003.1标准是以Unix操作系统为基础的,但是它又不限定于Unix和类似
于Unix的系统。确实,有些供应专有操作系统的制造商也声称这些系统将依从POSIX(同时
还保有它们的所有专有功能)。

由于1003.1标准说明了一个界面而不是一种实现,所以并不区分系统调用和库函数。所有
在标准中的例程都被称为函数。

标准是不断演变的,1003.1标准也不例外。该标准的1988版,IEEE 1003.1-1988经修改后
递交国际标准化组织(ISO),没有增加新的界面或功能,但修改了文本。最后的文档作为IE
EE Std1003 1-1990正式出版〔IEEE 1990〕,这也就是国际标准ISO/IEC 9945-1:1990。
该标准通常被称之为POSIX.1,我们将在本书中使用此标准。

IEEE 1003.1工作组此后对其又作了更多更改,它们应在1993被批准。这些更改(现在称之
为1003.1a)应由IEEE作为IEEE标准1003.1-1990的附件出版,这些更改也对本书有所影响
,主要是因为贝克莱风格的符号链接很可能将被加到标准中作为一种所要求的功能。这些
更改也很可能成为ISO/IEC 9945-1:1990的一个附录。在本书中,我们用注释的方法来说
明POSIX.1的1003.1a版本,指出哪些功能很可能会加到1003.1a中。

POSIX.1没有包括"超级用户"这样的概念。代之以规定某些操作要求"适当的优先权",POSI
X.1将此术语的定义留由具体实现进行解释。某些符合国防部安全性指导原则要求的Unix系
统具有很多不同的安全级。在本书中,我们仍使用传统的Unix术语,并指明要求超级用户
特权的操作。

图2.1〓由各种标准和实现定义的头文件

2.2.3〓X/Open XPG3

X/OPEN是一个国际计算机制造商组织。提出了一个七卷布可移植性指南,移为X/Open可移
植性指南,第三版〔X/Open 1989〕,我们将称之为XPG3。XPG3的第二卷(XSI系统界面和
头文件)对类似Unix的系统定义了一个界面,该界面定义是在IEEE Std 1003.1-1988界面的
基础上制订的。XPG3包含了一些POSIX.1没有的功能??
例如,一个POSIX.1没有但XPG3却有的功能是X/Open的消息设施。该设施可由应用程序使用
以在不同的语言中显示文件消息。

XPG3界面使用了ANSI C草案而不是最后的正式标准,所以在XPG3界面规格说明中包含的某
些功能是不再使用的。这些问题很可能会在将来的XPG规格说明的新版本中解决。(有关XPG
4的工作正在进行,很可能会在1993年完成)。

2.2.4〓FIPS
FIPS的含意是联邦信息处理标准(Federal Information Processing Standard),这些标准
是由美国政府出版的,并由美国政府用于计算机系统的操购。FIPS151-1(1989 4)是基于IE
EE Std 1003.1-1988及ANSI C标准草案的。FIPS 151-要求某些POSIX.1规定为可选的功能
。这种FIPS有时称为POSIX 1 FIPS。其2 5 5节列出了FIPS所要求的POSIX.1的可选项。

POSIX.1 FIPS的影响是:它要求任一希望向美国政府销售POSIX.1依从的计算机系统的厂商
应支持POSIX.1的某些可选功能。我们将不把POSIX 1 FIPS视作为另一个标准,因为实际上
它只是一个更加严格的POSIX 1标准。

2.3Unix实现

上面一节说明了三个由各自独立的组织所制定的标准:ANSI C、IEEE POSIX以及X/Open XP
G3。但是,标准只是界面的规格说明。这些标准是如何与现实世界相关连的呢?这些标准由
制造商采用,然后转变成具体实施。本书中我们感兴趣的是这些标准和它们的具体实施两
者。

在Leffler等著作[1989]的1.1节中给出了Unix族树的详细历史和关系图。Unix的各种版本
和变体都起源于在PDP-11系统上运行的Unix分时系统第6版(1976)和第7版(1979)(通常称为
Version 6和Version 7)。这两个版本是在贝尔实验室以外首先得到广泛应用的Unix系统。
从这棵树上发展出三个分支:(a)AT&T分支,从此导出了系统Ⅲ和系统Ⅴ(被称之为Unix的
商用版本),(b)加州大学贝克莱分校分支,从此导出4.XBSD实现,(c)由AT&T贝尔实验室的
计算科学研究中心不断开发的Unix研究版本,从此导出第8、第9和第10版。

2.3.1〓系统Ⅴ第4版

系统Ⅴ第4版(SVR4)是AT&T Unix系统实验室的产品,它汇集了下列系统的功能:AT&T Unix
系统Ⅴ第3 2版(SVR3 2),Sun Microsystem的Sunos系统,加州大学贝克莱分校的4.3 BSD
以及Microsoft的Xenix系统。(Xenix是在Verson7基础上开发的,后来又采用了很多系统Ⅴ
的功能。)其源代码于1989年后期分布,在1990年则开始向最终用户提供。SVR4符合于POSI
X 1003.1标准和X/OPEN XPG3标准。

AT&T也制版了系统Ⅴ界面定义(SVID)〔AT&T 1989〕。SVID第三版说明了Unix系统要达到SV
R4质量要求所应提供的功能。如同POSIX 1一样,SVID说明了一个界面,而不是一种实现。
对于一个具体实现的4应查看其参考手册,以了解其不同之处。〔AT&T 1990e〕。

SVR4包含了BSD的兼容库〔AT&T 1990c〕,它提供了功能与4.3BSD对应部分相同的函数和命
令。但是其中某些函数与POSIX的对应部分有所不同,本书中的所有SVR4实例都没有此兼容
库。只有在你有一些早期的应用程序,又不想改变它们时才使用此兼容库,新的应用程序
不应使用它。

2.3.2〓4.3+BSD

BSD是由加州大学贝克莱分校的计算机系统研究组研究开发和分发的。4.2BSD在1983年问世
,4.3BSD则在1986年。这两个版本都在VAX小型机上运行。它们的下一个版本4.3BSD Tahoe
在1988年发布,在一台称为Tahoe的小型机上运行(Leffler等的著作[1989]说明了4.3BSD T
aboe版。)其后又有1990年的4.3BSD Reno版,它支持很多POSIX 1的功能。下一个主要版本
4.4BSD应在1992年发布。

原来的BSD系统包含了AT&T专有的源代码,它们需要AT&T许可证。为了获得BSD系统的源代
码,首先需要持有AT&T源代码正被代换成非AT&T源代码,很多加到BSD系统上的新功能也来
自非AT&T方面。

在1989年,贝克莱将4.3BSD Taboe中很多非AT&T源代码包装成BSD网络软件,1.0版,并使
其成为公众可用的软件。其后则有BSD网络软件的2.0版,它是从4.3BSD Reno版导出的,其
目的是使大部分(如果不是全部的话)4.4 BSD系统不再受AT&T许可证的限制,于是其全部源
代码都可为公众使用。

正如我们在前言中所说明的,在全书中,我们用术语4.3+BSD来指本书所说明的BSD系统,
该系统位于BSD网络软件2.0版和将出现的4.4BSD之间。

在贝克莱所进行的Unix开发工作是从PDP-11开始的,然后转移到VAX小型机上,然后又转移
到工作站上。在九十年代早期,贝克莱得到支持在广泛得到应用的80386个人计算机上开发
BSD版本,结果产生了386BSD。这一工作是由Bill Jolitg完成的。其相关文档是发表在199
1年的Dr Dobb′s Journal上的系列文章(每月一篇)。其中很多代码出现在BSD网络软件,2
.0版中。

2.4标准和实现的关系

我们已提及的标准定义了任一实际系统的子集。虽然IEEE POSIX正致力于在其它所需方面(
例如,网络界面,进程间的通信,系统管理)制订出标准,但在编著本书时,这些标准还并
不存在。

本书的注意力集中于说明两个实际的Unix系统:SVR4和4.3+BSD。因为这两个系统都宣称是
依从POSIX的,所以我们一方面集中于说明POSIX.1标准所要求的功能,同时POSIX和这两个
系统具体实现之间的差别,为此,SVR4或4.3+BSD特有的功能和例程都被清楚地标记出来。
因为XPG3是POSIX.1的超集,所以我们叙述了属于XPG3,但不属于POSIX 1的功能。

应当了解,SVR4和4.3+BSD都提供了对它们早期版本功能的兼容性(例如SVR3.2和4.3BSD)。
例如,SVR4和POSIX规格说明中的非阻塞I/O(O_NONBLOCK)以及传统的系统Ⅴ方法(O_NDELAY
)都提供了支持。在本书中,我们将只使用POSIX.1的功能,但是也会提及它所代换的是哪
一种非标准功能。与此相类似,SVR3.2和4.3BSD以某种方法提供了可靠信号机制,这种方
法也有别于POSIX.1标准。在第十章中,我们只说明POSIX.1的信号机制。

 

2.5限制

有很多由实现定义的幻数和常数。其中有很多已被编写到程序中,或由特定的技术所确定
。由于大量标准化工作的努力,已经提供了若干种可移植的方法以确定这些幻数和实
现定义的限制。这非常有助于软件的可移植性。

三种类型的功能是需要的:

·编辑时的可选项(该系统是否支持作业控制?)
·编辑时的限制(短整型的最大值是什么?)
·运行时的限制(文件名的最大字符数?)

前两个,编辑时的可选项和限制可在头文件中定义。程序在编辑时可以包含这些头文件。
但是,运行时的限制则要求进程调用一个函数以获得此种限制值。

另外,某些限制在一个给定的实现中可能是固定的(因此可以静态地在一个头文件中定义)
,而在另一个实现上则可能是变动的(需要有一个运行时的函数调用)。这种在型限制的一
个例子是文件名的最大字符数。系统Ⅴ由于历史原因只允许文件名有14个字符,而贝克莱
类的系统则将此增加为255。SVR4允许我们对每一个我们所创建的文件系统指定,它是系统
Ⅴ文件系统还是BSD文件系统,而每个系统有不同的限制。这就是运行时限制的一个实例,
文件名的最大长度依赖于所述及的文件处在那个文件系统中。例如,在根文件系统中的一
个文件名其长度限制可能是14个字符,而在某个其它文件系统中的一个文件,其文件名长
度限制可能是255个字符。

为了解决这些问题,提供了三种限制:

1 编辑时的可选项及限制。
2 不与文件或目录相关联的运行时限制。
3 与文件或目录相关联的运行时限制。

使事情变得更加复杂的是,如果一个特定的运行时限制在一个给定的系统上并不改变,则
可将其静态地定义在一个头文件中,但是,如果没有将其定义在头文件中,则应用程序就
必须调用三个conf函数中的一个(我们很快就会对它们进行说明)。以确定其在运行时的值


2.5.1〓ANSI C限制

所有由ANSI C定义的限制都是编辑时的限制。图2 2中列示了在文件<limits h>中定义的C
标准限制。这些常数总是定义在该头文件中,而且在一个给定系统中并不会改变。在第三
列中列出了ANSI C标准可接受的最小值。这用于整型长度为16位的系统它使用1的补码表示
。在第四列中列出了整型长度为32位的当前系统的值,用的是2的补码表示法。注意,对不
带符号的数据类型都没有列出其最小值,它们都应为0。

我们将会遇到的一个区别是系统是否提供带符号(signed)或不带符号的(unsigned)的字符
值,从图2 2中的第四列可见,该特定系统使用带符号字符。从表中可以看到CHAR_MIN等于
SCHAR_MIN,CHAR_MAX等于SCHAR_MAX。如果系统使用不带符号字符,则CHAR_MIN等于0,CH
AR_MAR等于UCHAR_MAX。

在头文件<float.h>中,对浮点数据类型也有类似的一组定义。

我们会遇到的另一个ANSI C常数是FOPEN_MAX,这是实现保证的可同时打开的标准I/O流的
最小数,该值在头文件<stdio.h>中,其最小值是8。POSIX.1中的值STREAM_MAX(若定义的
话),则应具与FOPENMAX相同的值。

ANSI C在<stdio.h>中也定义了常数TMP_MAX,这是由tmpnam函数产生的唯一文件名的最大
数。关于此常数我们将在5.13节中进行更多说明。

图2.2〓<limits.h>中的整型值大小

2.5.2〓POSIX限制

POSIX定义了很多涉及操作系统实现限制的常数,不幸,这是POSIX 1中最使人迷惑部分中
的一个。

有33个限制和常数,它们被分成下列八类:

1 不变的最小值(图2.3中的13个常数)。
2 不变值:SSIZE_MAX。
3 运行时不能增加的值:NGROUPS_MAX。
4 运行时不变的值(可能不确定):
AGE_MAX,CHILD_MAX,OPEN_MAX,STREAM_MAX以及TZNAME_MAX。
5 路径名可变值(可能不确定):
LINK_MAX,MAX_CHNON,MAX_INPUT,NAME_MAX,PATH_MAX以及PIPE_BUF。
6 编译时符号常数:
_POSIX_SAVED_IDS,_POSIX_VERSION以及_POSIX_JOB_CONTROL。
7 执行时符号常数:
_POSIX_NO_TRONC,_POSIX_VDISABLE以及_POSIX_CHOWN_RESTRICTED。
8 不再使用的常数:CLK_TCK。

在这33个限制和常数中,15个总是定义的,其余的则按具体条件可定义可不定义。在2.5.4
中在说明sysconf,pathconf和fpatheonf函数时说明可定义可不定义的限制和常数(第4-8条
)。在图2.7中我们摘录了所有限制和常数。13个不变最小值则示于图2.3中。
   
图2.3〓<limits.h>中的POSIX 1不变最小值

这些值是不变的----它们并不附系统而改变。它们指定了这些特征方面的最严格的值。一
个符合POSIX/1的实现应当提供至少这样大的值。这就是为什么将它们称为最小的原因,虽
然它们的名字都包含了MAX。另外,一个可移植的应用程序不应要求更大的值。我们将在本
书的适当部分说明这些这些常数中每一个的含意。

不幸的是,这些不变最小值中的某一些在实际应用中是太小了。例如,现时的Unix系统所
提供的每个进程可同时打开文件数远超过16,即使是1978年的Version 7也向每个进程提供
了20个打开文件。另外,_POSIX_PATH_MAX的最小值限制255也是太小了,路径名可能会超
过这一限制。这意味着我们在编译时不能使用这两个常数_POSIX_OPEN_MAX和_POSIX_PATH_
MAX作为数组长度。

图2.3中的13个不变最小值的每一个都有一个相关的实现值,其名字是将图2.3中的名字删
除前缀_POSIX_后构成的。(这13个实现值是我们在本节开始部分所列出的2-5项:不变值、
运行时不能增加的值、运行时不变的值、以及路径名可变值。)问题是并不保证所有这13个
实现值定义在<limit.h>头文件中。一个特定值可能不定义在此头文件中的理由是:例如对
一个给定进程的实际值可能依赖于系统的存储器总量。如果没有在头文件中定义它们,则
我们就不能在编译时使用它们作为数组边界。所以,POSIX.1决定提供三个运行时函数供我
们调用,它们是:syseonf,pathconf以及fpathconf,用它们可以在运行时得到实际的实现
值。但是,还有一个问题,因为其中某些值是由POSIX 1定义为"可能不确定的"(逻辑上无
限的),这就意味着该值没有实际上限。例如,SVR4的每个进程打开文件数限制在假想上是
无限的,所以在SVR4中OPEN_MAX被认为是不确定的。在2.5.7中我们还将讨论运行时不确定
限制的问题。

2.5.3〓XPG3限制

XPG3定义了七个常数,它们总是包含在<limits.h>头文件中。POSIX.1则会把它们称之为
不变最小值。它们列于图2.4中。这些值的大多数都涉及消息。    

图2.4〓XPG3不变最小值(在<limits.h>中)

XPG3也定义了值PASS_MAX,作为口令字中的最大有效字符数(不包括终止字符null),它可
能包含在<limits.h>中。POSIX.1则把它称之为运行时不变的值(可能不确定),其最小可接
受的值是8。PASS[CD#*2]MAX值也可在运行时用sysconf函数取得,该函数将在2.5.4中说明


2.5.4〓sysconf、pathconf以及fpathconf函数

我们已列出了一个实现必须支持的各种最小值,但是怎样才能找到一个特定系统实际支持
的限制值呢?正如我们在前面提到的,某些限制值在编辑时是可用的,而另外一些则必须在
运行时确定。我们也曾提及在一个给定的系统中某些限制值是不会更改的,而其它则与文
件和目录相关联。运行时限制是由调用下面三个函数中的一个而取得的。

#include <unistd h>
long sysconf(int name);
log pathconf(const char *pathname,int name);
log fpathconf(int filedes,int name);

All three return:corresponding value if OK,-1 on error (see later)

最后两个函数之间的差别是一个用路径名作为其参数,另一个则取文件描述符作为参数。

图2.5中列出了这三个函数所使用的name参数。以_SC_开始的常数用作为sysconf的参数,
而以_PC_开始的常数则作为pathconf或fpathconf的参数。

对于pathconf的参数pathname,fpathconf的参数filedes有很多限制。如果不满足其中任何
一个限制,则结果是未定义的。
1 _PC_MAX_CANON,_PC_MAX_INPUT以及_PC_VDISABLE所涉及的文件必须是终端文件。
2 _PC_LINK_MAX所涉及的文件可以是文件或目录。如果这是一个目录,则返回值用于目录
本身(不用于目录内的文件名项)。
3 _PC_NAME_MAX和_PC_NO_TRUNC所涉及的文件必须是目录,返回值用于该目录中的文件名。
4 _PC_PATH_MAX涉及的必须是目录。当所指定的目录是工作目录时,返回值是相对路径名
的最大长度。(不幸的是,这不是我们想要知道的一个绝对路径名的实际最大长度,我们将
在2.5.7中再回到这一问题上来)
5 _PC_PIPE_BUF所涉及的文件必须是管道,FIFO或目录。在管道或FIFO情况下,返回值是
对所涉及的管道或FIFO的限制值。对于目录,则返回值是对在该目录中创建的任一FIFO的
限制值。
6 _PC_CHOWN_RESTRICTED必须是文件或目录。如果它是目录,则返回值指明此可选项是否
适用于该目录中的文件。     

图2.5〓对sysconf、pathconf和fpathconf的限制及name参数

我们需要更详细地说明这三个函数的不同返回值。

1 如果name不是图2.5第3列中的一个合适常数,则所有这三个函数都返回-1,并将error
设置为EINVAL。
2 包含MAX的12个名字以及名字_PC_PIPE_BUF可能或者返回该变量的值(返回值ZO),或者返
回-1,这表示该值是不确定的,此时并不更改errno的值。
3 对_SC_CLK_TCK的返回值是每秒的时钟滴答数,以用于times函数的返回值(8.15节)。
4 对_SC_VERSION的返回值以4位数,2位数分别表示以标准的年、月。这可能或者是198808
L,或199009L,或此标准某个以后版本的值。
5 对_SC_XOPEN_VERSION的返回值表示此系统所遵从的XPG版本,其当前值是3。
6 _SC_JOB_CONTROL和_SC_SAVED_IDS是两个可选功能。若sysconf返回-1(没有更改errno)
则不支持相应的功能。这两个功能也可在编译时从<unistd.h>头文件中决定。
7 对_PC_CHOWN_RESTRICTED和_PC_NO_TRUNC的返回值若为-1(不改变errno),则表示对所指
定的pathname或filedes不支持此功能。
8 对_PC_VDISABLE的返回值若为-1(不改变errno),则表示对所指定的pathname或filedes
不支持此功能。若支持此功能,则返回值是被用于禁止特定终端输入字符的字符值(图1.6)


实例

程序2.1打印所有这些限制,并处理一个限制未被定义的情况。

程序2.1〓打印所有可能的sysconf和pathconf值

我们条件地包括了两个常数,它们已被加至POSIX.1,但不是IEEEStd 1003.1-1988版本的
一部分。图2.6显示了在几个不同的系统上,程序2.1的样本输出。表中的"not def"表示该
常数未定义。我们在4.14中可以了解到,SVR4 S5文件系统是可以回逆到Version 7的
传统系统Ⅴ文件系统。UFS是贝克莱快速文件系统的SVR4实现。   

图2.6〓配置限制的实例

2.5.5〓FIPS 151-1要求

FIPS 151-1标准(我们已在2.2.4节中提及)由于要求下列功能,所以它比POSIX.1标准更严


·要求下列POSIX 1可选功能:
_POSIX_JOB_CONTROL,_POSIX_SAVED_IDS,_POSIX_NO_TRONC,_POSIX_CHOWN_RESTRICTED和
_POSIX_VDISABLE。
·NGROUPS_MAX的最小值是8。
·新创建的文件或目录的组ID应设置为它所在目录的组ID(在4.6节中说明此功能)
·在已传输了一些数据后,若read或write被一个捕捉到的信号所中断,则这些函数应返回
已被传输的字节数(在10.5节中讨论被中断的系统调用)。
·登录shell应定义环境变量HOME和LOGNAME。

因为美国政府购买很多计算机系统,所以大多数POSIX的制造商都将支持这些增加的FIPS要
求。     

图2.7〓编辑时和运行时限制的摘要

2.5.6〓限制摘要

我们已说明了很多限制和幻常数,其中某些总被包含在一头文件中,某些可选地包含在头文
件中,其它则可在运行时决定。图2 7以字母序摘要列出了所有这些常数以及得到它们值的
各种方法。以_SC_开始的运行时名字是sysconf函数的参数,以_PC_开始的名字是pathconf
和fpathconf函数的参数,如果它有最小值,则也将其列于其中。注意,图2.3中的13个POS
IX.1不变最小值示于图2.7中的最右一列。

2.5.7〓未确定的运行时限制

我们已提及图2.7中的某些值可能是未确定的,这些值是第三列标记为可选的(optional),
其名字中或包含MAX,或是PIPE_BUF。我们遇到的问题是如果这些值没有在头文件<limits
.h>中定义,那么在编辑时间我们也就不能使用它们。但是,如果它们的值是未确定的,那
么在运行时间,它们可能也是未定义的。让我们观察两个特殊的例子----为一个路径名分
配存储器,以及决定文件描述符数。

路径名

很多程序需要为路径名分配存储器,典型地,在编辑时就为其分配了存储器,而且使用了
各种幻数(其中很少值是正确的)作为数组长度:256,512,1024或标准I/O常数BUFSIZ在头
文件<sys/param.h>中的4 3BSD常数MAXPATHLEN是正确值,但是很多4.3BSD应用程序并未使
用。

POSIX.1试图用PATH_MAX值来帮助我们,但是如果此值是不确定的,那么仍是毫无帮助的。
程序2.2是一个我们在全书中都将使用的为路径名动态地分配存储器的函数。

如若在<limits.h>中定义了常数PATH_MAX,那么就没有任何问题,如果没有,则需调用path
conf。因为pathconf的返回值是把第一个参数视为基于工作目录的相对路径名。所以我们
指定根为第一个参数,并将得到的返回值加1作为结果值。如果pathconf指明PATH_MAX是不确定的那么我们就只得猜测某个值,在调用malloc时+1,是为了在尾端
加字符串结束符null字符(PATH_MAX没有考虑中)。

处理不确定结果情况的正确方法与如何使用分配到存储空间有关。例如,如果我们为getcw
d调用分配空间,(返回当前工作目录的绝对路径名,见4.22节)而分配到的空间太小,于是
返回一个出,errno设置为ERANGE。然后我们可调用realloc以增加分配空间(见7.8节和练
习4.18)并再试。我们可以不断这样做,直至getcwd调用成功执行。    

程序2.2〓为路径名动态地分配空间

最大打开文件数

在精灵进程(是在后台运行,不与终端相连接的一种进程)中一个常见的代码序列是关闭所
有打开文件。某些程序中有下列形式的代码序列:

#include <sys/param h>
for(i=0;i<NOFILE;i++)
   close(i);

这段程序假定在<sys/parm.h>头文件中定义了常数NOFILE。另外一些程序则使用某些<stdi
o.h>版本提供作为上限的常数_NFILE。某些程序则直接将其上限值定为20。

我们希望用POSIX.1的OPEN_MAX确定此值以提高可移植性,但是如果此值是不确定的,则仍
然有问题,如果我们使用下列代码

#include <unistd h>
for(i=0;i<sysconf(_SC_OPEN_MAX);i++)
   close(i);

而且如果OPEN_MAX是不确定的,那么sysconf将返回-1,于是,for循环根本不会执行。在
这种情况下,最好的选择就是关闭所有描述符直至某个任意的限制值(例如256)。如同上面
的路径名一样,这并不能保证在所有情况下都能正确工作,但这却是我们所能选择的最好
方法。我们在程序2.3中使用了这种技术。   

程序2.3〓确定文件描述符数

我们可以耐心地调用close,直至得到一个出错返回,但是从close出错返回(EBADF)并不区
分无效描述符和并未打开的描述符。如果我们试用此技术,而且描述符9未打开,而描述符
10打开了,我们将停止在9上,而不会关闭10。dup函数(3.12节)在超过了OPEN_MAX时会返
回一个特定的出错值,但是用复制一个描述符一、二百次的方法来确定此值是一种极端的
方法。

SVR4和4.3+BSD的getrlimit函数(7.11节)以及4.3+BSD的getdtablesize(2)函数返回一个进
程可以打开的最大描述符数,但是调用这两个函数不是可移植的。

OPEN_MAX被POSIX称为运行时不变值,其意思是在一个进程的生命期其值不应被改变,但是
在SVR4和4.3+BSD之下,我们可以调用setrlimit(2)函数更改一个运行进程的这一值(此值
也可用C shell的limit命令改变,用Bourne shell和Kornshell的limit命令更改)如果我们
的系统支持这种功能,则可以将程序2.3更改为每次调用此程序时就调用sysconf,而不只
是第一次调用此程序时。

2.6功能测试宏 
正如前述,在头文件中定义了很多POSIX.1和XPG3的符号。但是除了POSIX.1和XPG3定义外
,大多数实现在这些头文件中也加上了它们自己的定义。如果在编辑一道程序时,希望它
只使用POSIX定义而不使用任何实现定义的限制,那么我们就需定义常数_POSIX_SOURCE,
所有POSIX.1头文件中都使用此常数,当该常数定义时,就能排除任何实现专有的定义。 

常数_POSIX_SOURCE及其对应的常数_XOPEN_SOURCE被称之为功能测试宏,所有功能测试宏
都以下划线开始,当要使用它们时,通常在CC命令行中以下列方式定义它们: 

CC -D_POSIX_SOURCE file c 

这使得在C程序包括任何头文件之前,定义了功能测试宏。如果我们只要使用POSIX.1定义
,那么也可将源文件的第一行设置为: 

#define _POSIX_SOURCE 1 

另一个功能测试宏是:__STDC__,它是由符合ANSI C标准的编译程序自动定义的。这样就
允许我们编写ANSI C编译程序和非ANSI C编辑程序都能编译的程序。例如,在一个头文件
可能会是: 

# ifdef __STDC__ 
void *myfunc(const char *,int); 
#else 
void *myfunc(); 
#endif 

这样就能发挥ANSI C原型功能的长处,要注意在开始和结束处的两个连续的下划线常常打
印成一个长下划线(如同上面一个样本源代码中一样)。 

2.7基本系统数据类型 
历史上,某些Unix变量已与某些C数据类型联系在一起,例如,主、次设备号在历史上存放
在一个16位的短整型中,用8位表示主设备号,另外8位表示次设备号。但是,很多较大的
系统需要用多于256个值来表示其设备号,于是,就需要有一种不同的技术。(确实,SVR4
用32位表示设备号:14位用于主设备号,18位用于次设备号)。 

头文件<sys/types h>中定义了某些与实现有关的数据类型,它们被称之为基本系统数据类
型。有很多这种数据类型定义在其它头文件中。在头文件中这些数据类型都是用C的typede
f设施来定义的。它们绝大多数都以_t结尾。图2 8中列出了本书将使用的基本系统数据类
型。    
用这种方式定义了这些数据类型后,我们在编辑时就不再需要考虑附系统不同而变的实施
细节,在本书中涉及到这些数据类型处,我们会说明为什么使用它们。 

2.8标准之间的冲突 
就整体而言,这些不同的标准之间配合得是相当好的。但是我们也很关注它们之间的差别
,特别是ANSIC标准和POSIX.1之间的差别。(因为XPG3是一个较尽的正在被修订的标准,FI
PS则是一个要求更严的POSIX.1。)
 
ANSI C定义了函数clock,它返回进程使用的CPU时间量,返回值是clock_t类型值。为了将
此值变换成以秒为单位,将其除以在<time.h>头文件中定义的CLOCKS_PER_SEC。POSIX.1定
义了函数times,它返回其调用者及其所有终止子进程的CPU时间以及时钟时间,所有这些
值都是clock_t类型值。IEEEStd、1003.1-1988将符号CLK_TCK定义为每秒滴答数,上述clo
ck_t值都是以此度量的。而1990 POSIX.1标准中则说明不再使用,CLK_TCK应当使用syscon
f函数来获得每秒滴答数,并将其用于times函数的返回值。术语是同一个,每秒滴答数,
但ANSI C和POSIX 1的定义却不同。这两个标准也用同一数据类型(clock_t)来保存这些不
同的值,这种差别可以在SVR4中看到,其中clock返回微秒数(CLOCK_PER_SEC是一百万),
而CLK_TCK通常是50,60或100(与CPU类型有关)。 

另一个可能产生冲突的区域是:在ANSI C标准说明函数时,ANSI C所说明的函数可能会没
有考虑到POSIX.1的某些要求。有些函数在POSIX环境下可能要求有一个与C环境下不同的实
现,因为POSIX环境中有多个进程,而C语言环境则很少会考虑宿主操作系统。尽管如此,
很多POSIX依从的系统为了兼容性的关系也实现ANSI C函数,signal函数就是一个例子。如
果我们在不了解的情况下使用了SVR4所提供的signal函数(希望编写可在ANSI C环境和较早
Unix系统中运行的可兼容程序),那么它提供了与POSIX.1 sigaction函数不同的语义。在
第十章中我们会对signal函数作更多说明。 

你可能感兴趣的:(c,工作,unix,Microsoft,Path,Signal)