17、【Linux系统编程】 sysconf,、pathconf

  对于运行时的限制值和选项,我们已经知道可以用sysconf()、pathconf()和fpathconf()三个函数之一来确定它们的值。具体地,sysconf()用于获得与文件或目录无关的限制值,以及系统特征选项;pathconf()和fpathconf()用于获得与文件或目录有关的限制值。这三个函数的原型为:

1 #include <unistd.h>
2 long int sysconf(int parameter);
3 long int pathconf(const char *pathname,int parameter);
4 long int fpathconf(int filedes,int parameter);

  sysconf()用于确定当前的系统变量之值(即限制值和特征选项),其中参数parameter指出要询问的是哪个系统变量,它应当是头文件<unistd.h>中定义的以'_SC_'开头的符号常数之一,下表列出了其中的一部分。

parameter参数描述
_SC_LINK_MAN询问LINK_MAX的值
_SC_MAX_CANON询问MAX_CANON的值
_SC_MAX_INPUT询问MAX_INPUT的值
_SC_NAME_MAX询问NAME_MAX的值
_SC_PATH_MAX询问PATH_MAX的值
_SC_CHOWN_RESTRICTED询问CHOWN_RESTRICTED的值
_SC_NO_TRUNC询问NO_TRUNC的值

  该函数调用成功将返回所询问参数之值,这个值在调用进程的生存期内不会改变。如果parameter参数不合法,sysconf()返回–1并设置errno指出错误。如果系统不支持所询问的参数或者所询问的限制值不确定,sysconf ()返回–1但不设置errno。

  因为–1既是正常返回值也是异常返回值,因此,希望检查错误情形的应用在调用它们之前应当先设置errno为0。如果返回值为–1,则通过检查errno是否为非0来判别错误。

  另外,建议首先用特征测试宏检查你感兴趣的宏名是否有定义并且仅当它没有定义时才调用sysconf(),这样做可使程序更有效率。例如,下面的例子说明了如何测试系统是否支持作业控制。

 1 int have_job_control(void)
 2 {
 3 #ifdef _POSIX_JOB_CONTROL
 4    return 1;
 5 #else
 6    int value;
 7    errno = 0;
 8    value = sysconf(_SC_JOB_CONTROL);
 9    if (value < 0)
10       if (errno == 0)
11          value = 0;
12       else
13          fatal(strerror(errno));
14    return value;
15 #endif
16 }

函数pathconf()和fpathconf()作用相同,它们都用于查询与文件系统限制和选项有关的值,不同的只是pathconf()作用于文件名pathname,而fpathconf()作用于已打开的文件描述字 filedes。参数parameter必须是定义在头文件<unistd.h>中以'_PC_'开头的符号常数之一,下表列出了其中的一部分。

parameter参数
描述
_PC_LINK_MAX
询问LINK_MAX的值
_PC_MAX_CANON
询问MAX_CANON的值
_PC_MAX_INPUT
询问MAX_INPUT的值
_PC_NAME_MAX
询问NAME_MAX的值
_PC_PATH_MAX
询问PATH_MAX的值
_PC_CHOWN_RESTRICTED
询问CHOWN_RESTRICTED的值
_PC_NO_TRUNC
询问NO_TRUNC的值

  这两个函数调用成功返回所查询参数之值。如果系统不施加限制或者当有错时,返回值为–1。在前一种情况下不设置errno,在后一种情况下设置errno指明错误原因。因此,同sysconf()一样,可靠调用这两个函数的方法是在调用它们之前将errno清零。

  对于不同的查询参数,这两个函数对参数pathname和filedes有不同的要求,例如,对于_PC_MAX_CANON、_PC_ MAX_INPUT和_PC_ VDISABLE,要求所引用的文件必须是一个终端文件;对于_PC_PATH_MAX,要求所引用的文件必须是一个目录;对于_PC_PIPE_BUF,要求所引用的文件必须是管道、FIFO或者目录,并且当pathname指向一个FIFO文件,或filedes指向一个管道或FIFO文件时,返回值适用于这个文件本身,当pathname或filedes指向一个目录时,返回值适用于此目录内已存在的或能创建的FIFO文件,具体的细节可查阅联机说明。

  至此,我们已经讲述了UNIX系统的常用限制值和有关的选项,并且知道了它们要么静态地定义在头文件<limits.h>中,要么必须通过这一节介绍的函数询问它们的值。如果它们在头文件<limits.h>中有定义,通过包含这个头文件并引用有关的系统变量,可以直接引用它们;如果它们没有定义在头文件<limits.h>中,就必须调用函数来确定它们。但问题并不完全这样简单,它们还可能是不确定的,不确定通常意味着没有限制。因此,在编写可移植程序时,应当充分注意到这种情况。

  例1 有许多程序需要为路径名分配存储空间,典型的做法是在编译时通过指定一个固定的常数来确定空间大小,比如256、512甚至1024。然而,一个固定的常数往往不能适合所有的系统,为了程序的可移植性,需要利用限制值PATH_MAX。程序1-3就是这样一个例子。其中函数get_current_dir()的功能是获得进程当前工作目录的绝对路径名。为了分配存放路径名的缓冲区,我们利用PATH_MAX来确定它的大小。如果PATH_MAX是定义在<limits.h>中的常数,或者调用pathconf()能够得到PATH_MAX的值,则缓冲区的大小就是PATH_MAX+1,因为PATH_MAX不包括结尾的空字符,实际缓冲区的大小要多一个字节。如果pathconf()指出PATH_MAX是不确定的,我们则必须试探性地分配足够的空间来存放路径名。对于这种限制值不确定的情形,正确的做法取决于所分配存储空间的用法。在这个例子中,分配的缓冲区用于调用getcwd(),这个函数返回当前工作目录的绝对路径名于指定的缓冲区中并返回缓冲区指针。如果指定的缓冲区太小,getcwd()将返回NULL指针并置errno为ERANGE。这时我们扩大缓冲区一倍后重复这一过程直至调用getcwd()成功。

程序1同时也示例了一种既具有可移植性又不失高效的编程方法。如果系统在<limits.h>中定义了PATH_MAX,这个程序将在编译时直接使用其值,从而省去了调用pathconf()和反复进行存储分配的开销。反之,如果不考虑系统可能给出PATH_MAX之值的情形而简单地采用最后一种手段,尽管它具有可移植性,但对大多数系统而言,该程序是低效的,因为多数系统都定义了PATH_MAX。

程序1 get_current_dir()之例

 1 #include "ch01.h"
 2 char *get_current_dir()
 3 {
 4    char *buffer;
 5    char *value;
 6    int size = 0;
 7    /*确定当前工作目录路径名的最大长度于size,当PATH_MAX 不确定时,size为-1*/
 8 #ifdef PATH_MAX
 9    size = PATH_MAX;
10 #else
11    errno = 0;
12    if((size = pathconf(“./”,_PC_PATH_MAX)) < 0)
13       if(errno != 0){
14          printf("pathconf error for _PC_PATH_MAX\n");
15          exit(-1);
16      }
17 #endif
18    if(size > 0){ /* PATH_MAX有定义,可以保证分配的空间足以存放路径名*/
19       buffer = (char *)malloc(size+1);
20       value = getcwd(buffer, size);
21    }else{  /* PATH_MAX没有定义,必须试探性地分配足够的空间来存放路径名*/
22       size = _POSIX_PATH_MAX;
23       buffer = (char *)malloc(size);
24       while (1) {
25          value = getcwd(buffer, size);
26          if(value == 0 && errno ==ERANGE){ /* buffer太小,重新申请更大的空间 */
27             size *= 2;
28             free (buffer);
29             buffer = (char *)malloc(size);
30          }
31       }
32    }
33    return buffer;
34 }