版本之间进行移植相当容易,但是8 0年代U N I X版本的剧增以及它们之间差别的扩大导致很多
大用户(例如美国政府)要求对其进行标准化。
ANSI C标准的意图是提供C程序的可移植性,使其能适合于大量不同的操作系统,而不只
是UN I X。此标准不仅定义了C程序设计语言的语法和语义,也定义了其标准库〔ANSI 1989第
4章;Plauger 1992;Kernighan及Ritchie 1988中的附录B〕。因为很多新的U N I X系统(例如本书
介绍的几个U N I X系统)都提供C标准中说明的库函数,所以此库对我们来讲是很重要的。
按照该标准定义的各个头文件,可将该库分成1 5区。表2 - 1中列出了C标准定义的头文件,
以及下面几节中说明的另外两个标准( P O S I X . 1和X P G 3 )定义的头文件。在其中也列举了S V R 4
和4 . 3 + B S D所支持的头文件。本章也将对这两种U N I X实现进行说明。
P O S I X是一个由I E E E (电气和电子工程师学会)制订的标准族。P O S I X的意思是计算机环境
的可移植操作系统界面(Portable Operating System Interface>for Computer Environment)。它原来
指的只是I E E E标准1 0 0 3 . 1 - 1 9 8 8 (操作系统界面),但是, I E E E目前正在制订P O S I X族中的其他
有关标准。例如, 1 0 0 3 . 2将是针对s h e l l和公用程序的标准, 1 0 0 3 . 7将是系统管理方面的标准。
在1 0 0 3工作组中至少有1 5个子委员会。
与本书相关的是1 0 0 3 . 1操作系统界面标准,该标准定义了“ P O S I X依从的”操作系统必须
提供的服务。虽然1 0 0 3 . 1标准是以U N I X操作系统为基础的,但是它又不仅仅限于U N I X和类似
于U N I X的系统。确实,有些供应专有操作系统的制造商也声称这些系统将依从P O S I X (同时还
保有它们的所有专有功能)。
由于1 0 0 3 . 1标准说明了一个界面( i n t e r f a c e)而不是一种实现( i m p l e m e n t a t i o n),所以并
不区分系统调用和库函数。所有在标准中的例程都被称为函数。
上面一节说明了三个由各自独立的组织所制定的标准: ANSI C、IEEE POSIX以及X / O p e n
X P G 3。但是,标准只是界面的规格说明。这些标准是如何与现实世界相关连的呢?这些标准由
制造商采用,然后转变成具体实施。本书中我们感兴趣的是这些标准和它们的具体实施。
我们已提及的标准定义了任一实际系统的子集。虽然IEEE POSIX正致力于在其他所需方
面(例如,网络界面,进程间的通信,系统管理)制订出标准,但在编著本书写作时,这些标准
还并不存在。
2.5限制
有很多由实现定义的幻数和常数,其中有很多已被编写到程序中,或由特定的技术所确定。
由于大量标准化工作的努力,已有若干种可移植的方法用以确定这些幻数和实现定义的限制。
这非常有助于软件的可移植性。
以下三种类型的功能是必需的:
• 编译时间选择项(该系统是否支持作业控制)。
• 编译时间限制(短整型的最大值是什么)。
• 运行时间限制(文件名的最大字符数为多少)。
前两个,编译时间选择项和限制可在头文件中定义。程序在编译时可以包含这些头文件。
但是,运行时间限制则要求进程调用一个函数以获得此种限制值。
另外,某些限制在一个给定的实现中可能是固定的(因此可以静态地在一个头文件中定义),
而在另一个实现上则可能是变动的(需要有一个运行时间函数调用)。这种类型限制的一个例
子是文件名的最大字符数。系统V由于历史原因只允许文件名有1 4个字符,而伯克利的系统则
将此增加为2 5 5。S V R 4允许我们对每一个创建的文件系统指明是系统V文件系统还是B S D文件
系统,而每个系统有不同的限制。这就是运行时间限制的一个实例,文件名的最大长度依赖于
文件所处的文件系统。例如,根文件系统中的文件名长度限制可能是1 4个字符,而在某个其他
文件系统中文件名长度限制可能是2 5 5个字符。
为了解决这些问题,提供了三种限制:
(1) 编辑时间选择项及限制(头文件)。
(2) 不与文件或目录相关联的运行时间限制。
(3) 与文件或目录相关联的运行时间限制。
使事情变得更加复杂的是,如果一个特定的运行时间限制在一个给定的系统上并不改变,
则可将其静态地定义在一个头文件中,但是,如果没有将其定义在头文件中,则应用程序就必
须调用三个c o n f函数中的一个(我们很快就会对它们进行说明),以确定其运行时间值。
2.5.1 ANSI C限制
所有由ANSI C定义的限制都是编译时间限制。表2 - 2中列出了文件< l i m i t s . h >中定义的C标
准限制。这些常数总是定义在该头文件中,而且在一个给定系统中并不会改变。第3列列出了
ANSI C标准可接受的最小值。这用于整型长度为1 6位的系统,它使用1的补码表示。第4列列
出了整型长度为3 2位的当前系统的值,用的是2的补码表示法。注意,对不带符号的数据类型
都没有列出其最小值,它们都应为0。
我们将会遇到的一个区别是系统是否提供带符号( s i g n e d)或不带符号的( u n s i g n e d)的
字符值。从表2 - 2中的第4列可以看出,该特定系统使用带符号字符。从表中可以看到
C H A R M I N等于S C H A R M I N,C H A R M A X等于S C H A R M A X。如果系统使用不带符号字符,
则C H A R M I N等于0,C H A R M A X等于U C H A R M A X。
在头文件< f l o a t . h >中,对浮点数据类型也有类似的一组定义。
我们会遇到的另一个ANSI C常数是F O P E N M A X,这是实现保证的可同时打开的标准I / O
流的最小数,该值在头文件< s t d i o . h >中定义,其最小值是8。P O S I X . 1中的S T R E A M M A X(若
定义的话),则应具与F O P E N M A X相同的值。
ANSI C在< s t d i o . h >中也定义了常数T M P M A X,这是由t m p n a m函数产生的唯一文件名的
最大数。关于此常数我们将在5 . 1 3节中进行更多说明。
表2-2 <limits.h>中的整型值大小
名字说明最小可接受值典型值
C H A R B I T c h a r的位8 8
C H A R M A X c h a r的最大值(见后) 1 2 7
C H A R M I N c h a r的最小值(见后) -1 2 8
S C H A R M A X 带符号c h a r的最大值1 2 7 1 2 7
S C H A R M I N 带符号c h a r的最小值-1 2 7 -1 2 8
U C H A R M A X 不带符号c h a r的最大值2 5 5 2 5 5
I N T M A X i n t最大值32 767 2 147 483 647
I N T M I N i n t最小值-32 767 -2 147 483 648
U I N T M A X 不带符号的i n t的最大值65 535 4 294 967 295
S H R T M I N s h o r t最小值-32 767 -32 768
S H R T M A X s h o r t最大值32 767 32 767
U S H R T M A X 不带符号的s h o r t的最大值65 535 65 535
L O N G M A X l o n g最大值2 147 483 647 2 147 483 647
L O N G M I N l o n g最小值-2 147 483 647 -2 147 483 648
U L O N G M A X 不带符号的l o n g的最大值4 294 967 295 4 294 967 295
M B L E N M A X 一多字节字符常数中的最大字节数1 1
2.5.2 POSIX限制
P O S I X . 1定义了很多涉及操作系统实现限制的常数,不幸的是,这是P O S I X . 1中最令人迷惑不解的部分之一。
它包括3 3个限制和常数,它们被分成下列八类:
(1) 不变的最小值(表2 - 3中的1 3个常数)。
(2) 不变值:S S I Z E M A X。
(3) 运行时间不能增加的值:N G R O U P S M A X。
(4) 运行时间不变的值(可能不确定):ARG_MAX, CHILD_MAX, OPEN_MAX, STREAM_
M A X以及T Z N A M E M A X。
(5) 路径名可变值(可能不确定):LINK_MAX, MAX_CANON, MAX_INPUT, NAME_
MAX, PAT H M A X以及P I P E B U F。
(6) 编辑时间符号常数: _ P O S I X _ S AVED_IDS, _POSIX_VERSION以及_ P O S I X _ J O B _
C O N T R O L。
(7) 执行时间符号常数: _POSIX_NO_TRUNC, _POSIX_VDISABLE 以及_ P O S I X _
C H O W N R E S T R I C T E D。
(8) 不再使用的常数:C L K T C K。
在这3 3个限制和常数中, 1 5个是必须定义的,其余的则按具体条件可定义可不定义。在
2 . 5 . 4节中,在说明sysconf, pathconf和f p a t h c o n f函数时,我们描述了可定义可不定义的限制和
常数(第4 ~ 8条)。在表2 - 7中我们总结了所有限制和常数。1 3个不变最小值则示于表2 - 3中。
表2-3 <limits.h>中的P O S I X . 1不变最小值
名字描述值
_ P O S I X A R G M A X e x e c函数的参数长度4 0 9 6
_ P O S I X C H I L D M A X 每个实际用户I D的子进程数6
_ P O S I X L I N K M A X 至一个文件的连接数8
_ P O S I X M A X C A N O N 终端规范输入队列的字节数2 5 5
_ P O S I X M A X I N P U T 终端输入队列的可用空间2 5 5
_ P O S I X N A M E M A X 文件名中的字节数1 4
_ P O S I X N G R O U P S M A X 每个进程同时的添加组I D数0
_ P O S I X O P E N M A X 每个进程的打开文件数1 6
_ P O S I X P A T H M A X 路径名中的字节数2 5 5
_ P O S I X P I P E B U F 能原子地写到一管道的字节数5 1 2
_ P O S I X S S I Z E M A X 能存在s s i z e t对象中的值32 767
_ P O S I X S T R E A M M A X 一个进程能一次打开的标准I / O流数8
_ P O S I X T Z N A M E M A X 时区名字节数3
这些值是不变的——它们并不随系统而改变。它们指定了这些特征最严格的值。一个符合
P O S I X . 1的实现应当提供至少这样大的值。这就是为什么将它们称为最小值的原因,虽然它们
的名字都包含了M A X。另外,一个可移植的应用程序不应要求更大的值。我们将在本书的适
当部分说明每一个常数的含意。
不幸的是,这些不变最小值中的某一些在实际应用中太小了。例如,目前U N I X系统每个
进程可同时打开的文件数远远超过1 6,即使是1 9 7 8 年的V 7也提供了2 0个。另外,
_ P O S I X PAT H M A X的最小值为2 5 5也太小了,路径名可能会超过这一限制。这意味着在编辑
时不能使用这两个常数_ P O S I X O P E N M A X和_ P O S I X PAT H M A X作为数组长度。
表2 - 3中的1 3个不变最小值的每一个都有一个相关的实现值,其名字是将表2 - 3中的名字删
除前缀_ P O S I X 后构成的。(这1 3个实现值是本节开始部分所列出的2 ~ 5项:不变值、运行时不
能增加的值、运行时不变的值、以及路径名可变值。)问题是所有这1 3个实现值并不能确保在
< l i m i t . h >头文件中定义。某个特定值可能不在此头文件中定义的理由是:对于一个给定进程的
实际值可能依赖于系统的存储器总量。如果没有在头文件中定义它们,则不能在编译时使用它
们作为数组边界。所以,P O S I X . 1决定提供三个运行时间函数以供调用: sysconf, pathconf以及
f p a t h c o n f,用它们可以在运行时间得到实际的实现值。但是,还有一个问题,因为其中某些值
是由P O S I X . 1定义为“可能不确定的” (逻辑上无限的),这就意味着该值没有实际上限。
2.5.4 sysconfig、pathconfig和fpathconf函数
我们已列出了一个实现必须支持的各种最小值,但是怎样才能找到一个特定系统实际支持
的限制值呢? 正如前面提到的,某些限制值在编译时是可用的,而另外一些则必须在运行时确
定。我们也曾提及在一个给定的系统中某些限制值是不会更改的,而其他则与文件和目录相关
联。运行时间限制是由调用下面三个函数中的一个而取得的。
#include <unistd.h>
long sysconf(int n a m e) ;
log pathconf(const char * pathname,int n a m e) ;
log fpathconf(int fieldes ,int n a m e) ;
所有函数返回:若成功为相应值,若出错为- 1(见后)
最后两个函数之间的差别是一个用路径名作为其参数,另一个则取文件描述符作为参数。
表2 - 5中列出了这三个函数所使用的n a m e参数。以_ S C 开始的常数用作为s y s c o n f的参数,
而以_ P C 开始的常数则作为p a t h c o n f或f p a t h c o n f的参数。
对于pathconf 的参数p a t h n a m e , fpathconf 的参数filedes 有很多限制。如果不满足其中任何
一个限制,则结果是未定义的。
(1) _PC_MAX_CANON,_ P C M A X I N P U T以及_ P C V D I S A B L E所涉及的文件必须是终
端文件。
(2) _PC_LINK_MAX所涉及的文件可以是文件或目录。如果是目录,则返回值用于目录本
身(不用于目录内的文件名项)。
(3) _PC_NAME_MAX和_ P C N O T R U N C所涉及的文件必须是目录,返回值用于该目录中
的文件名。
(4) _PC_PAT H M A X涉及的必须是目录。当所指定的目录是工作目录时,返回值是相对路
径名的最大长度。(不幸的是,这不是我们想要知道的一个绝对路径名的实际最大长度,我们
将在2 . 5 . 7节中再回到这一问题上来。)
(5) _PC_PIPE_BUF所涉及的文件必须是管道, F I F O或目录。在管道或F I F O情况下,返回
值是对所涉及的管道或F I F O的限制值。对于目录,返回值是对在该目录中创建的任一F I F O的
限制值。
(6) _PC_CHOWN_RESTRICTED必须是文件或目录。如果是目录,则返回值指明此选择项
是否适用于该目录中的文件。
表2-5 对s y s c o n f、p a t h c o n f和f p a t h c o n f的限制及n a m e参数
限制名说明n a m e 参数
A R G M A X 如e x e c函数的参数最大长度(字节) _ S C A R G M A X
C H I L D M A X 如每个实际用户I D的最大进程数_ S C C H I L D M A X
clock ticks/ second 如每秒时钟滴答数_ S C C L K T C K
N G R O U P S M A X 如每个进程的最大同时添加组I D数_ S C N G R O U P S M A X
O P E N M A X 如每个进程最大打开文件数_ S C O P E N M A X
P A S S M A X 如口令中的最大有效字符数_ S C P A S S M A X
S T R E A M M A X 如在任一时刻每个进程的最大标准I /O 流数——如_ S C S T R E A M M A X
如若定义,则其值一定与FOPEN_MAX相同
T Z N A M E M A X 时区名中的最大字节数_ S C T Z N A M E M A X
_ P O S I X J O B C O N T R O L 如指明实现是否支持作业控制_ S C J O B C O N T R O L
_ P O S I X S A V E D I D S 如指明实现是否支持保存的设置-用户- I D和保存的_ S C S A V E D I D S
设置-组- I D
_ P O S I X V E R S I O N 如指明P O S I X . 1版本_ S C V E R S I O N
_ X O P E N V E R S I O N 如指明X P G版本(非P O S I X . 1) _ S C X O P E N V E R S I O N
L I N K M A X 如文件连接数的最大值_ P C L I N K M A X
M A X C A N O N 如在一终端规范输入队列的最大字节数_ P C M A X C A N O N
M A X I N P U T 如终端输入队列可用空间的字节数_ P C M A X I N P U T
N A M E M A X 如文件名中的最大字节数(不包括n u l l结束符) _ P C N A M E M A X
P A T H M A X 如相对路径名中的最大字节数(不包括n u l l结束符) _ P C P A T H M A X
P I P E B U F 如能原子地写到一管道的最大字节数_ P C P I P E B U F
_ P O S I X C H O W N R E S T R I C T E D 指明使用c h o w n是否受到限制_ P C C H O W N R E S T R I C T E D
_ P O S I X N O T R U N C 如指明若路径名长于N A M E M A X是否产生一错误_ P C N O T R U N C
_ P O S I X V D I S A B L E 如若定义,终端专用字符可用此值禁止_ P C V D I S A B L E
我们需要更详细地说明这三个函数的不同返回值。
(1) 如果n a m e不是表2 - 5第3列中的一个合适的常数,则所有这三个函数都返回- 1,并将
e r r o r设置为E I N VA L。
(2) 包含M A X的1 2个n a m e以及n a m e_ P C P I P E B U F可能或者返回该变量的值(返回值≥O ),
或者返回-1,这表示该值是不确定的,此时并不更改e r r n o的值。
(3) 对_ S C C L K T C K的返回值是每秒的时钟滴答数,以用于t i m e s函数的返回值(见8 . 1 5
节)。
(4) 对_ S C V E R S I O N的返回值以4位数和2位数分别表示此标准的年和月。这可能或者是
1 9 8 8 0 8 L或1 9 9 0 0 9 L,或此标准某个以后版本的值。
(5) 对_ S C X O P E N V E R S I O N的返回值表示此系统所遵从的X P G版本,其当前值是3。
(6) _SC_JOB_CONTROL和_ S C S AV E D I D S是两个可选功能。若s y s c o n f返回-1(没有更
改e r r n o)则不支持相应的功能。这两个功能也可在编译时从< u n i s t d . h >头文件中决定。
(7) 对_ P C C H O W N R E S T R I C T E D和_ P C N O T R U N C的返回值若为-1 (不改变e r r n o ),则
表示对所指定的p a t h n a m e或f i l e d e s不支持此功能。
(8) 对_ P C V D I S A B L E的返回值若为- 1(不改变e r r n o),则表示对所指定的p a t h n a m e或
f i l e d e s不支持此功能。若支持此功能,则返回值是被用于禁止特定终端输入字符的字符值(见
表11 - 6)。