1.引言
人们在UNIX编程环境和C程序设计语言的标准化方面已经做了很多工作。虽然UNIX应用程序在不同的UNIX操作系统版本之间进行移植相当容易,但是20世纪80年代UNIX版本种类的剧增以及它们之间差别的扩大,导致很多大用户呼吁对其进行标准化。
本章首先回顾过去近25年人们在UNIX标准化方面做出的种种努力,然后讨论这些UNIX编程标准对本章所列举的各种UNIX操作系统实现的影响。所有标准化工作的一个重要部分是对每种实现必须定义的各种限制进行说明,所以我们讲说明这些限制以及确定它们值的各种方法。
2.UNIX标准化
(1)ISO C
1989年下半年,C程序设计语言的ANSI标准X3.159-1989得到批准,此标准被采纳为国际标准ISO/IEC 9899:1990。ANSI是美国国家标准学会的缩写,它是国际标准化组织中代表美国的成员。IEC是国际电子技术委员会的缩写。
ISO C标准现在由ISO/IEC的C程序设计语言国际标准工作组维护和开发,该工作组称为ISO/IEC JTCI/SC22/WG14,简称WG14。ISO C标准的意图是提供C程序的可移植性,使其能适合于大量不同的操作系统,而不只是适合UNIX系统。此标准不仅定义了C程序设计语言的语法和语义,还定义了其标准库。因为所有现今的UNIX系统都提供C标准中定义的库函数,所以该标准库非常重要。
1999年,ISO C标准被更新,并被批准为ISO/IEC 9899:1999,它显著改善了对进行数值处理的应用软件的支持。除了对某些函数原型增加了关键字restrict外,这种改变并不影响POSIX接口。restrict关键字告诉编译器,那些指针引用是可以优化的,其方法是指出指针引用的对象在函数中只能通过该指针进行访问。
1999年以来,以及公布了3个技术勘误来修正ISO C标准中的错误,分别在2001年,2004年和2007年公布。如同大多数标准一样,在批准标准和修改软件使其符合标准两者之间有一段时间延迟。随着供应商编译系统的不断演化,对最新ISO C标准的支持也就越来越多。
按照该标准定义的各个头文件可将ISO C库分成24个区。POSIX.1标准包括这些头文件以及另外一些头文件。从图2-1中可以看出,所有这些头文件在4中UNIX实现中都支持。本章后面将对这4中UNIX实现进行说明。
头文件
<assert.h>:验证程序断言
<complex.h>:复数算数运算支持
<ctype.h>:字符分类和映射支持
<errno.h>:出错码
<float.h>:浮点常量及特性
<inttypes.h>:整型格式变换
<iso646.h>:赋值,关系及一元操作符宏
<limits.h>:实现常量
<locale.h>:本地化类别及相关定义
<math.h>:数学函数,类型声明及常量
<setjmp.h>:非局部goto
<signal.h>:信号
<stdarg.h>:可变长度参数表
<stdbool.h>:布尔类型和值
<stddef.h>:标准定义
<stdint.h>:整型
<stdio.g>:标准IO库
<stdlib.h>:使用函数
<string.h>:字符串操作
<tgmath.h>:通用类型数学宏
<time.h>:时间和日期
<wchar.h>:扩充的多字节和宽字符支持
<wctype.h>:宽字符分类和映射支持
(2)IEEE POSIX
POSIX是一个最初由IEEE制定的标准族。POSIX指的是可以指操作系统接口,它原来指的只是IEEE标准1003.1-1988,后来则扩展成包括很多标记为1003的标准及标准草案,如shell和使用程序(1003.2)。
1003.1标准的目的是提升应用程序在各种UNIX系统环境之间的可移植性。它定义了"符合POSIX的"操作系统必须提供的各种服务。该标准已被很多计算机制造商采用。
由于1003.1标准说明了一个接口而不是一种实现,所以并不区分系统调用和库函数。所有在标准中的例程都被称为函数。
2001年的1003.1版本与以前各大版本有较大的差别,它组合了多个1003.1的修正,1003.2标准以及single UNIX Specification第2版的若干部分。
2004年POSIX.1随着技术勘误得到更新,2008年作了更多综合的改动,ISO在2009年进行发布。
POSIX.1没有包括超级用户的概念,代之规定某些操作要求"适当的优先权"。
(3)Single UNIX Specification
Single UNIX Specification是POSIX标准的一个超集,它定义了一些附加接口扩展POSIX.1规范提供的功能。
POSIX.1中的X/open系统接口选项描述了可选的接口,也定义了遵循XSI的实现必须支持POSIX.1的那些可选部分。这些必须支持的部分包括:文件同步,线程栈地址和长度属性,线程进程共享同步以及_XOPEN_UNIX符号常量。只有遵循XSI的实现才能称为UNIX系统。
(4)FIPS
FIPS代表的是联邦信息处理标准,这一标准是由美国政府发布的,并由美国政府用于计算机系统的采购。
3.UNIX系统的历史
略…
4.标准和实现的关系
略…
5.限制
UNIX系统实现定义了很多幻数和常量,其中有很多以及被硬编码到程序中,或用特定的技术确定。由于大量标准化工作的努力,已有若干种可移植的方法用以确定这些幻数和具体实现定义的限制。这非常有助于改善UNIX环境下软件的可移植性。
以下两种类型的限制是必需的:
①编译时限制(例如,短整型的最大值是什么?)
②运行时限制(例如,文件名有多少个字符)
编译时限制可在头文件中定义。程序在编译时可以包含这些头文件。但是,运行时限制则要求进程调用一个函数获得限制值。
另外某些限制在一个给定的实现中可能是固有的(因此可以静态地在一个文件头中定义),而在另一个实现中则可能是变动的(需要一个运行时函数调用)。这种类型限制的一个例子是文件名的最大字符数。SVR4之前的系统V由于历史原因只允许文件名最多包含14个字符,而源于BSD的系统则将此增加为255。目前,大多数UNIX支持多文件系统类型,而每一种类型有它自己的限制。文件名的最大长度依赖于该文件处于何种文件系统,例如,根文件系统中的文件名长度限制可能是14个字符,而在另一个文件系统中文件名长度限制可能是255个字符,这是运行时限制的一个例子。
为了解决这类问题,提供了以下3种限制:
①编译时限制(头文件)
②与文件或目录无关的运行时限制(sysconf)
③与文件或目录相关的运行时限制(pathconf和fpathconf函数)。
使事情变得更加复杂的是,如果一个特定的运行时限制在一个给定的系统上并不改变,则将其静态地定义在一个头文件中,但是,如果没有将其定义在头文件中,应用程序就必须调用3个conf函数中的一个,以确定其运行时的值。
(1)ISO C限制
ISO C定义的所有编译时限制都列在头文件<limits.h>中,这些限制常量在一个给定系统中并不会改变。第3列列出了ISO C标准可接受的最小值。这用于16为整型的系统,用1的不嘛表示。第4列列出了32为整型Linux系统的值,用2的补码表示。注意,我们没有列出无符号数据类型的最小值,这些值应该都为0。在64位系统中,其long整型的最大值与表中long long整型的最大值相匹配。
示例:2-6
我们将会遇到一个区别是系统是否提供带符号或无符号的字符值。从2-6示例中的第4列可以看出,该特定系统使用带符号字符。从图中可以看出CHAR_MIN等于SCHAR_MIN,SCHAR_MAX等于SCHAR_MAX。如果系统使用无符号字符,则CHAR_MIN等于0,CHAR_MAX等于UCHAR_MAX。
在头文件<float.h>中,对浮点数据类型也有类似的一组定义。如若读者在工作中设计大量浮点数据类型,则应该仔细查看该文件。
我们会遇到应一个ISO C常量是FOPEN_MAX,这是具体实现保证可同时可开的标准IO流的最小个数,该值在头文件<stdio.h>中定义,其最小值是8。POSIX.1中的STREAM_MAX则应与FOPEN_MAX具有相同的值。
ISO C还在<stdio.h>中定义了常量TMP_MAX,这是由tmpnam函数产生的唯一文件名的最大个数。
虽然ISO C定义了常量FILENAME_MAX,但我们应该便面使用该常量,因为POSIX.1提供了更好的替代常量(NAME_MAX和PATH_MAX