不确定的运行时限制:
如果某些限制值没有在<limits.h>中定义,则在编译时不能使用这些限制;而且即使对于运行时限制,如果它们的值是不确定的,那么它们也是未定义的。
如下的程序用来为路径名动态分配存储区(一般来说,很多程序在编译时就为其分配了存储区,而且不同的程序使用不同的幻数,例如256,512,1024或标准I/O常量BUFSIZ,但很少是正确的)。
/*
* Copyright (C) fuchencong@163.com
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#define SUSV3 200112L
#define PATH_MAX_GUESS 1024
#ifdef PATH_MAX
static int path_max = PATH_MAX;
#else
static int path_max = 0;
#endif
static long posix_version = 0;
char *path_alloc(int *sizep);
char *
path_alloc(int *sizep)
{
int size;
char *ptr;
if (posix_version == 0) {
posix_version = sysconf(_SC_VERSION);
}
if (path_max == 0) {
errno = 0;
if ( (path_max = pathconf("/", _PC_PATH_MAX) < 0)) {
if (errno == 0) {
path_max = PATH_MAX_GUESS;
} else {
printf("pathconf errnor for _PC_PATH_MAX\n");
}
} else {
path_max++;
}
}
if (posix_version < SUSV3) {
size = path_max + 1;
} else {
size = path_max;
}
if ( (ptr = malloc(size)) == NULL) {
printf("malloc error\n");
}
if (sizep != NULL) {
*sizep = size;
}
return ptr;
}
int
main()
{
int size;
char *ptr;
ptr = path_alloc(&size);
printf("PATH_MAX = %d\n", size);
if (ptr != NULL) {
free(ptr);
}
exit (0);
}
关于该程序有如下几点说明:
(1)pathconf函数返回的基于工作目录的相对路径名的最大长度,而工作目录是其第一个参数。所以使用根目录作为参数进行调用,并将得到的返回值加1,即可得到路径名的最大长度。
(2)SUS v3之前,对于PATH_MAX是否在路径名末尾已经包含了null字符这一点表述的不清楚,如果操作系统遵循先前的标准版本,则需在路径名分配的存储数量上加1。
(3)如果PATH_MAX是不确定的,我们只能猜一个值了。
最大打开文件数:
守护进程(daemon,是指在后台运行且不与终端相连接的一种进程,也称为精灵进程或后台进程)中一个常见的代码序列是关闭所有打开的文件。我们可以使用POSIX.1中的OPEN_MAX来确定最大打开文件数,以提高代码的可移植性。
下列程序获取最大的打开文件数,如果OPEN_MAX是未确定的,则只能猜测一个限制值。尽管这并不能保证在所有情况下都正确动作,但这却是我们所能选的最好的方法。
/*
* Copyright (C) fuchencong@163.com
*/
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#ifdef OPEN_MAX
static long openmax = OPEN_MAX;
#else
static long openmax = 0;
#endif
#define OPEN_MAX_GUESS 256
long open_max(void);
long
open_max(void)
{
if (openmax == 0) {
errno = 0;
if ( (openmax = sysconf(_SC_OPEN_MAX)) < 0) {
if (errno == 0) {
openmax = OPEN_MAX_GUESS;
} else {
printf("sysconf error for _SC_OPEN_MAX\n");
}
}
}
return openmax;
}
int
main()
{
printf("oepnmax = %d\n", open_max());
exit(0);
}
程序的运行结果如下:
这与ulimt -n的结果一致:
上述程序还有一个问题,由于Linux中可以调用ulimit命令随时更改可打开的最大文件数,而上述程序只在第一次调用open_max函数时调用sysconf函数,所以为了应对这种情况,应该修改上述程序,使得每次调用open_max时都会调用sysconf函数。
支持Single UNIX Specification 的XSI扩展的系统提供了getrlimit(2)函数,它可用来返回一个进程可以同时打开的最大描述符数。
选项:
如果我们要先写一些可移植的应用程序而这些程序与所得到支持的选项有关,那么就需要一种可移植的方法已决定是否支持一个给定的选项。
如同对限制的处理一样,SUS定义了三种处理方法:
- 编译时选项定义在<unistd.h>中;
- 与文件或目录无关的选项可用sysconf函数来确定;
- 与文件或目录相关的选项可用pathconf或fpathconf函数来确定;
- 如果符号常量的定义值为-1,那么该平台不支持相应的选项;
- 如果符号常量的定义值大于0,那么该平台支持相应的选项;
- 如果符号常量的定义值为0,则必须调用sysconf、pathconf以确定相应的选项是否受到支持;
功能测试宏:
大多数实现在头文件中除了定义很多POSIX符号和XSI符号,也在这些头文件中加上了它们自己的定义。如果编译一个程序时,只希望使用POSIX的定义而不使用任何实现定义的限制,那么就需要定义常量_POSIX_C_SOURCE,所有的POSIX头文件都使用此常量。当定义此常量时,就能排除任何实现专有的定义。
常量_POSIX_C_SOURCE以及_XOPEN_SOURCE被称为功能测试宏。
另一个功能测试宏是:__STDC__,它由符合ISO C标准的C编译器自动定义。这就允许我们编写ISO C编译器和非ISO C编译器都能编译的程序。尽管大多数编译器都支持ISO C标准,但是很多头文件中仍旧使用__STDC__功能测试宏。
基本系统数据类型:
头文件<sys/types.h>中定义了某些与实现有关的数据类型,他们被称为基本系统数据类型(primitive system data type)。还有很多这种数据类型定义在其它头文件中。在头文件中,这些数据类型都是用C的typedef功能来定义的,它们绝大多数都以_t结尾。
用这种方法定义了这些数据类型后,就不再需要考虑因系统而异的程序实现细节了。
标准之间的冲突:
就整体而言,这些不同的标准之间配合得相当好。但它们之间也有一些差别,例如ISO C和POSIX.1标准之间就存在一些差别,而SUS v3本身就是POSIX.1的超集。