UNIX标准化及实现之限制
UNIX标准化及实现之限制
前言
UNIX系统实现定义了很多幻数和常量,其中有很多已被硬编码(关于硬编码和软编码:http://www.cnblogs.com/chenkai/archive/2009/04/10/1432903.html)进程序中,或用特定的技术确定。由于大量标准化工作的努力,已有若干种可移植的方法用以确定这些幻数和实现定义的限制。这非常有助于软件的可移植性。
以下两种类型的限制是必需的:
(1)(例如,短整型的最大值是什么?)。
(2)(例如,文件名可以有多少个字符?)。
编译时限制可在头文件中定义,程序在编译时可以包含这些头文件。但是,运行时限制则要求进程调用一个函数以获得此种限制值。
另外,某些限制在一个给定的实现中可能是固定的(因此可以静态地在一个头文件中定义),而在另一个实现上则可能是变化的(需要一个运行时函数调用)。
为了解决这类问题,提供了以下三种限制:
(1)编译时限制(头文件)。
(2)不与文件或目录相关联的运行时限制(sysconf函数)。
(3)与文件或目录相关联的运行时限制(pathconf和fpathconf函数)。
使事情变得更复杂的是,如果一个特定的运行时限制在一个给定的系统上并不改变,则可将其静态地定义在一个头文件中。但是,如果没有将其定义在头文件中,则应用程序就必须调用三个conf函数中的一个,以确定其运行时的值。
一、ISO C 限制
ISO C定义的限制都是编译时限制。文件<limits.h>中定义的C标准限制如下:
表1 <limits.h>中定义的整型值大小
名字 | 说明 | 最小可接受值(用于16位整型系统) | 典型值(32位Linux系统) |
CHAR_BIT CHAR_MAX CHAR_MIN SCHAR_MAX SCHAR_MIN UCHAR_MAX | 字符的位数 字符的最大值 字符的最小值 带符号字符的最大值 带符号字符的最小值 不带符号字符的最大值 | 8 | 8 |
INT_MAX INT_MIN UINT_MAX | 整型的最大值 整型的最小值 不带符号整型的最大值 | 32 767 | 2 147 483 647 |
SHRT_MIN SHRT_MAX USHRT_MAX | 短整型的最小值 短整型的最大值 不带符号短整型的最大值 | -32767 | -32768 |
LONG_MAX LONG_MIN ULONG_MAX | 长整型的最大值 长整型的最小值 不带符号长整型的最大值 | 2 147 483 647 | 2 147 483 647 |
LLONG_MAX LLONG_MIN ULLONG_MAX | 长长整型的最大值 长长整型的最小值 不带符号长长整型的最大值 | 9 223 372 036 854 775 807 | 9 223 372 036 854 775 807 |
MB_LEN_MAX | 多字节字符常量中的最大字节数 | 1 | 16 |
这些常量总是定义在头文件中,而且在一个给定系统中不会改变。注意,对不带符号的数据类型都没有列出其最小值,它们都应为0。在64位系统中,其long整型的最大值与表中long long整型最大值相匹配。
我们将会遇到的一个不同之处是系统是否提供带符号或不带符号的字符集。从上表中,我们可以看到CHAR_MIN等于SCHAR_MIN,CHAR_MAX等于SCHAR_MAX;如果系统使用不带符号的字符集,则CHAR_MIN等于0,CHAR_MAX等于UCHAR_MAX。
我们会遇到的另一个ISO C常量是FOPEN_MAX,这是具体实现保证可同时打开的标准I/O流的最小数。该值在头文件<stdio.h>中定义,其值最小是8。POSIX.1中的STREAM_MAX(若定义的话)必需有与FOPEN_MAX相同的值。
ISO C在<stdio.h>中还定义了常量TMP_MAX,这是由tmpnam函数产生的唯一文件名的最大数。
表2 在各种平台上的ISO限制
限制 | FreeBSD5.2.1 | Linux 2.4.22 | Mac OS X 10.3 | Solaris 9 |
FOPEN_MAX TMP_MAX | 20 | 16 | 20 | 20 |
ISO C还定义了常量FILENAME_MAX,因为某些操作系统实现在历史上将它定义得太小,以至于不能满足应用的需求,所以我们应避免使用该常量。
二、POSIX限制
POSIX.1定义了很多涉及操作系统实现限制的常量,我们只关心与基本POSIX.1接口有关的部分。这些限制和常量被分成下列5类。
(1)不变的最小值:见表3。
(2)不变值:SSIZE_MAX。
(3)运行时可以增加的值:CHARCLASS_NAME_MAX、COLL_WEIGHTS_MAX、LINE_MAX、NGROUPS_MAX以及RE_DUP_MAX。
(4)运行时不变的值(可能不确定):ARG_MAX、CHILD_MAX、HOST_NAME_MAX、LOGIN_NAME_MAX、OPEN_MAX、PAGESIZE、RE_DUP_MAX、STREAM_MAXS、SYMLOOP_MAX、TTY_NAME_MAX以及TZNAME_MAX。
(5)路径名可变值(可能不确定):FILESIZEBITS、LINK_MAX、MAX_CANON、MAX_INPUT、NAME_MAX、PATH_MAX、PIPE_BUF以及SYMLINK_MAX。
表3 <limits.h>中的POSIX.1不变最小值
名字 | 说明:以下各项的最小可接受值 | 值 |
_POSIX_ARG_MAX | exec函数的参数长度 | 4096 |
_POSIX_CHILD_MAX | 每个实际用户ID的子进程数 | 25 |
_POSIX_HOST_NAME_MAX | gethostname函数返回的主机名最大长度 | 255 |
_POSIX_LINK_MAX | 指向一个文件的链接数 | 8 |
_POSIX_LOGIN_NAME_MAX | 登录名的最大长度 | 9 |
_POSIX_MAX_CANON | 终端规范输入队列的字节数 | 255 |
_POSIX_MAX_INPUT | 终端输入队列的可用空间 | 255 |
_POSIX_NAME_MAX | 文件名中的字节数,不包括终止字符null | 14 |
_POSIX_NGROUPS_MAX | 每个进程同时对添加组ID数 | 8 |
_POSIX_OPEN_MAX | 每个进程的打开文件数 | 20 |
_POSIX_PATH_MAX | 路径名中的字节数,包括终止字符null | 256 |
_POSIX_PIPE_BUF | 能原子地写到管道的字节数 | 512 |
_POSIX_RE_DUP_MAX | 当使用间隔表示法\{m,n\}时,regexec和regcomp函数允许的基本正则表达式的重复出现次数 | 255 |
_POSIX_SSIZE_MAX | 能存储在ssize_t对象中的值 | 32767 |
_POSIX_STREAM_MAX | 一个进程能同时打开的标准I/O流数 | 8 |
_POSIX_SYMLINK_MAX | 符号链接中的字节数 | 255 |
_POSIX_SYMLOOP_MAX | 在解析路径名时可遍历的符号链接数 | 8 |
_POSIX_TTY_NAME_MAX | 终端设备名长度,包括终止字符null | 9 |
_POSIX_TZNAME_MAX | 时区名字节数 | 6 |
上述5类共44个限制和常量中,有一些可定义在<limits.h>中,其余的则按照具体条件可定义或不定义。
表3中列出了19个不变最小值,这些值是不变的——它们并不随系统而改变。它们指定了这些特征最具约束性的值。一个符合POSIX.1的实现应当提供至少这样大的值。这就是为什么将它们称为最小值的原因,虽然它们的名字都包含了MAX。另外,为了保证可移植性,一个严格遵循POSIX的应用程序不应要求更大的值。
一个严格遵循(strictly conforming)POSIX的应用程序有别于仅遵循POSIX(merely POSIX Confirming)的应用程序。一个遵循POSIX的应用程序只使用在IEEE标准1003.1-2001中定义的接口。一个严格遵循POSIX的应用程序也是遵循POSIX的,但除此之外,它还应不依赖于POSIX未定义的行为,不使用任何已废弃的接口,以及不要求所使用的常量值大于上表中所列出的最小值。
不幸的是,这些不变最小值中的某一些在实际应用中太小了。例如,目前在大多数UNIX系统中,每个进程可同时打开的文件数远远超过20。另外,_POSIX_PATH_MAX的最小限制值为255,这也太小了,路径名可能会超过这一限制。这意味着在编译时不能使用_POSIX_OPEN_MAX和_POSIX_PATH_MAX这两个常量作为数组长度。
表3中的每一个不变最小值都有一个相关的实现值,其名字是将表中的名字删除前缀_POSIX_后构成的。不带有前导_POSIX_的名称打算作为给定实现支持的实际值(这19个实现值是上面5类中(2)-(5):不变值、运行时可增加的值、运行时不变的值以及路径名可变值)。问题是并不能确保所有这19个实现值都在<limits.h>头文件中定义。
例如,某个特定值可能不在此头文件中定义,其理由是:一个给定进程的实际值可能依赖于系统的存储总量。如果没有在头文件中定义这些值,则不能在编译时使用它们作为数组边界。所以POSIX提供了三个运行时函数以供调用,它们是:sysconf、pathconf以及fpathconf。使用这三个函数可以在运行时得到实际的实现值。但是,还有一个问题,例如在Linux中,readv或writev可用的iovec结构数仅受系统存储总量的限制。所以,在Linux系统中,IOV_MAX被认为是不确定的。
三、XSI限制
XSI还定义了处理实现限制的下面几个常量:
(1)不变最小值:表4中列出的10个常量。
(2)数值限制:LONG_BIT和WORE_BIT。
(3)运行时不变值(可能不确定):ATEXIT_MAX、IOV_MAX以及PAGE_SIZE。
表4 <limits.h>中的XSI不变最小值
名字 | 说明 | 最小可接受值 | 典型值 |
NL_ARGMAX | printf和scanf调用中的最大数字值 | 9 | 9 |
NL_LANGMAX | LANG环境变量中的最大字节数 | 14 | 14 |
NL_MSGMAX | 最大消息数 | 32767 | 32767 |
NL_NMAX | N对1映射字符中的最大字节数 | 未指定 | 1 |
NL_SETMAX | 最大集合数 | 255 | 255 |
NL_TEXTMAX | 消息字符串中的最大字符数 | _POSIX2_LINE_MAX | 2048 |
NZERO | 默认的进程优先级 | 20 | 20 |
_XOPEN_IOV_MAX | readv或writev可使用的最大iovec结构数 | 16 | 16 |
_XOPEN_NAME_MAX | 文件名中的字节数 | 255 | 255 |
_XOPEN_PATH_MAX | 路径名中的字节数 | 1024 | 1024 |
四、sysconf、pathconf和fpathconf函数
我们已列出了实现必须支持的各种最小值,但是怎样才能找到一个特定系统实际支持的限制值呢?正如前面提到的,某些限制值在编译时是可用的,而另外一些则必须在运行时确定。我们也曾提及在一个给定的系统中某些限制值是不会更改的,而其他限制值则与文件和目录相关联,是可以改变的。运行时限制可通过调用下面三个函数中的一个而取得:
#include <unistd.h>
long sysconf( int name );
long pathconf( const char *pathname, int name );
long fpathconf( int filedes, int name );
所有函数返回值:
#include <unistd.h>
POSIX allows an application to test at compile- or run-time whether cer-
tain options are supported, or what the value is of certain configurable
constants or limits.
If name is invalid, -1 is returned, and errno is set to EINVAL. wise, the value returned is the value of the system resource and not changed. In the case of options, a positive value is returned if a
queried option is available, and -1 if it is not. -1 means that there is no definite limit.
#include <unistd.h>
long pathconf(char *path, int name);
fpathconf() gets a value for the configuration option name for the open
file descriptor filedes.
path.
The limit is returned, if one exists. for the requested resource, -1 is returned, . If
there is an error, -1 is returned, and errno is set to reflect the nature
of the error.
后两个函数之间的差别是一个用路径名作为其参数,另一个则取文件描述符作为参数。
表5 sysconf的限制及name参数(用于标识系统限制,以_SC_开始的常量用作标识运行时限制的sysconf参数)
限制名 | 说明 | name参数 |
ARG_MAX | exec函数的参数最大长度(字节数) | _SC_ARG_MAX |
ATEXIT_MAX | 可用atexit函数登记的最大函数个数 | _SC_ATEXIT_MAX |
CHILD_MAX | 每个实际用户ID的最大进程数 | _SC_CHILD_MAX |
clock ticks/second | 每秒时钟滴答数 | _SC_CLK_TCK |
COLL_WEIGHTS_MAX | 在本地定义文件中可以赋予LC_COLLATE顺序关键字项的最大权重数 | _SC_COLL_WEIGHTS_MAX |
HOST_NAME_MAX | gethostname函数返回的主机名最大长度 | _SC_HOST_NAME_MAX |
IOV_MAX | readv或writev函数可以使用的iovec结构的最大数 | _SC_IOV_MAX |
LINE_MAX | 实用程序输入行的最大长度 | _SC_LINE_MAX |
LOGIN_NAME_MAX | 登录名的最大长度 | _SC_LOGIN_NAME_MAX |
NGROUPS_MAX | 每个进程同时添加的最大进程组ID数 | _SC_NGROUPS_MAX |
OPEN_MAX | 每个进程的最大打开文件数 | _SC_OPEN_MAX |
PAGESIZE | 系统存储页长度(字节数) | _SC_PAGESIZE |
PAGE_SIZE | 系统存储页长度(字节数) | _SC_PAGE_SIZE |
RE_DUP_MAX | 当使用间隔表示法\{m,n\}时,regexec和regcomp函数允许的基本正则表达式的重复出现次数 | _SC_RE_DUP_MAX |
STREAM_MAX | 在任一时刻每个进程的最大标准I/O流数,如若定义,则其值一定与FOPEN_MAX相同 | _SC_STREAM_MAX |
SYMLOOP_MAX | 在解析路名期间,可遍历的符号链接数 | _SC_SYMLOOP_MAX |
TTY_NAME_MAX | 终端设备名长度,包括终止字符null | _SC_TTY_NAME_MAX |
TZNAME_MAX | 时区名的最大字节数 | _SC_TZNAME_MAX |
表6 pathconf和fpathconf的限制及name参数(以_PC_开始的常量用作标识运行时限制的pathconf或fpathconf参数)
限制名 | 说明 | name参数 |
FILESIZEBITS | 以带符号整型值表示在指定目录中允许的普通文件最大长度所需的最少位数 | _PC_FILESIZEBITS |
LINK_MAX | 文件链接数的最大值 | _PC_LINK_MAX |
MAX_CANON | 终端规范输入队列的最大字节数 | _PC_MAX_CANON |
MAX_INPUT | 终端输入队列可用空间的字节数 | _PC_MAX_INPUT |
NAME_MAX | 文件名的最大字节数(不包括终止字符null) | _PC_NAME_MAX |
PATH_MAX | 相对路径名的最大字节数,包括终止字符null | _PC_PATH_MAX |
PIPE_BUF | 能原子地写到管道的最大字节数 | _PC_PIPE_BUF |
SYMLINK_MAX | 符号链接中的字节数 | _PC_SYMLINK_MAX |
我们需要更详细地说明这三个函数的不同返回值:
(1)如果name不是表5和表6的第3列中的一个合适的常量,则所有这三个函数都会返回-1,并将errno设置为EINVAL。
(2)有些name可以返回变量的值(返回值>=0),或者返回-1,这表示该值是不确定的,此时并不改变errno的值。
(3)_SC_CLK_TCK的返回值是每秒钟的时钟滴答数,以用于times函数的返回值。
对于pathconf的参数pathname以及fpathconf的参数filedes有一些限制。如果不满足其中任何一个限制,则结果是未定义的:
。
如果是目录,则返回值用于目录本身(而不是用于目录内的文件名项)。
,返回值用于该目录中的文件名。
。当所指定的目录是工作目录时,返回值是相对路径名的最大长度(不幸的是,这不是我们想要知道的一个绝对路径名的实际最大长度)。
。在管道或FIFO情况下,返回值是对所引用的管道或FIFO的限制值。对于目录,返回值是对在该目录中创建的任一FIFO的限制值。
。返回值是该目录中符号链接可能包含的字符串的最大长度。
程序清单2-1 构建C程序以打印所有得到支持的系统配置限制(竟然可以使用awk编写C程序,就这个实例来说,使用awk构建C程序比直接编写要简单高效多了)
[root apue]
((((((((((((=
(getline < > (, $(, $, $((, $((, $(, $, $((, $(((getline < > (, $(, $, $((, $((, $(, $, $((, $(((((((((((