APUE学习笔记01——UNIX标准与限制
Xilong Yang

本章主要介绍了UNIX的标准与限制,说明了三个标准范围ISO C、POSIX的SUS,并且对三个运行时限制函数sysconf、pathconf和fpathconf进行了介绍。

UNIX系统中存在三个标准范围与一些限制。为了写出良好的可移植代码,了解这些标准是非常有必要的。

标准范围

ISO C

所有Unix系统都提供对ISO C标准的完整支持。包括以下头文件:

头文件 说明
assert.h 验证程序断言
complex.h 复数算术运算支持
ctype.h 字符分类和映射支持
errno.h 出错码
fenv.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.h 标准I/O库
stdlib.h 实用函数
string.h 字符串操作
tgmath.h 通用类型数学宏
time.h 时间和日期
wchar.h 扩充的多字节和宽字符支持
wctype.h 宽字符分类和映射支持

POSIX

可移植操作系统接口,最初是IEEE POSIX。在1988年由ISO标准化,称为POSIX.1,这个版本被广泛支持。包括三组头文件和一组可选接口组。

POSIX必需头文件:

头文件 说明
aio.h 异步I/O
cpio.h cpio归档值
dirent.h 目录项
dlfcn.h 动态链接
fcntl.h 文件控制
fnmatch.h 文件名匹配类型
glob.h 路径名模式匹配与生成
grp.h 组文件
iconv.h 代码集变换实用程序
langinfo.h 语言信息变量
monetary.h 货币类型与函数
netdb.h 网络数据库操作
nl_types.h 消息类
poll.h 投票函数
pthread.h 线程
pwd.h 口令文件
regex.h 正则表达式
sched.h 执行调度
semaphore.h 信号量
strings.h 字符串操作
tar.h tar归档值
termios.h 终端I/O
unistd.h 符号常量
wordexp.h 字扩充类型
arpa/inet.h Internet定义
net/if.h socket本地接口
netinet/in.h Internet地址族
netinet/tcp.h TCP协议定义
sys/mman.h 存储管理声明
sys/select.h select函数
sys/socket.h socket接口
sys/stat.h 文件状态
sys/statvfs.h 文件系统信息
sys/times.h 进程时间
sys/types.h 基本系统数据类型
sys/un.h UNIX域socket定义
sys/utsname.h 系统名
sys/wait.h 进程控制

XSI可选头文件:

头文件 说明
fmtmsg.h 消息显示结构
ftw.h 文件树漫游
libgen.h 路径名管理函数
ndbm.h 数据库操作
search.h 搜索表
syslog.h 系统出错日志记录
utmpx.h 用户账户数据库
sys/ipc.h IPC
sys/msg.h XSI消息队列
sys/resource.h 资源操作
sys/sem.h XSI信号量
sys/shm.h XSI共享存储
sys/time.h 时间类型
sys/uio.h 矢量I/O操作

可选头文件:

头文件 说明
mqueue.h 消息队列
spawn.h 实时spawn接口

可选接口组和选项码:

选项码 符号常量 说明
ADV _POSIX_ADVISORY_INFO 建议性信息
CPT _POSIX_CPUTIME 进程CPU时间时钟
FSC _POSIX_FSYNC 文件同步
IP6 _POSIX_IPV6 IPv6接口
ML _POSIX_MEMLOCK 进程存储区加锁(实时)
MLR _POSIX_MEMLOCK_RANGE 存储区域加锁(实时)
MON _POSIX_MONOTONIC_CLOCK 单调时钟(实时)
MSG _POSIX_MSEEAGE_PASSING 消息传送(实时)
MX __STDC_IEC_559__ IEC 60559 浮点选项
PIO _POSIX_PRIORITIZED_IO 优先输入和输出
PS _POSIX_PRIORITIZED_SCHEDULING 进程调度(实时)
RPI _POSIX_THREAD_REBUST_PRIO_INHERIT 健壮的互斥量优先权继承(实时)
RPP _POSIX_THREAD_REBUST_PRIO_PROTECT 健壮的互斥量优先权保护(实时)
RS _POSIX_RAW_SOCKETS 原始Socket
SHM _POSIX_SHARED_MEMORY_OBJECTS 共享存储对象
SIO _POSIX_SYNCHRONIZED_IO 同步输入和输出
SPN _POSIX_SPAWN 产生
SS _POSIX_SPORADIC_SERVER 进程阵发性服务器
TCT _POSIX_THREAD_CPUTIME 线程CPU时间时钟
TPI _POSIX_THREAD_PRIO_INHERIT 非健壮的互斥量优先权继承
TPP _POSIX_THREAD_PRIO_PROTECT 非健壮的互斥量优先权保护
TPS _POSIX_THREAD_PRIORITY_SCHEDULING 线程执行调度
TSA _POSIX_THREAD_ATTR_STACKADDR 线程栈地址属性
TSH _POSIX_THREAD_PROCESS_SHARED 线程进程共享同步
TSP _POSIX_THREAD_SPORADIC_SERVER 线程阵发性服务器
TSS _POSIX_THREAD_ATTR_STACKSIZE 线程栈长度属性
TYM _POSIX_TYPED_MEMORY_OBJECTS 类型存储对象
XSI _XOPEN_UNIX X/Open扩充接口

SUS

单一UNIX规范,是POSIX.1标准的一个超集,定义了一些附加接口扩展了POSIX.1。

SUS标准中,必须支持POSIX可选接口组中加粗的5组。同时一个系统必须满足此要求才能称为UNIX系统。

有些标准在SUS中也是可选的,包括:

  • 加密:由符号常量_XOPEN_CRYPE标记。
  • 实时:由符号常量_XOPEN_REALTIME标记
  • 高级实时。
  • 实时线程:由符号常量_XOPEN_REALTIME_THREADS标记
  • 高级实时线程。

限制

要使得程序具备良好的可移植性,就需要一种方法来获取系统的各种限制量。主要分为:

  1. 编译时限制,如整型的最大值。
  2. 运行时限制,如文件名最大长度。

编译时限制可以定义在头文件里,而运行时限制往往需要运行特定的函数取得。主要包括ISO C 限制、POSIX 限制和XSI限制。

ISO C限制

ISO C的所有编译时限制都定义在文件<limits.h>中。这些常量在一个特定的系统中是不会改变的。

名称 说明 可接受的最小值 典型值
CHAR_BIT char的位数 8 8
CHAR_MAX char的最大值 取决于系统使用的字符是否带类型 127
CHAR_MIN char的最小值 取决于系统使用的字符是否带类型 -128
SCHAR_MAX signed char的最大值 127 127
SCHAR_MIN signed char的最小值 -127 -127
UCHAR_MAX unsigned char的最大值 255 255
INT_MAX int的最大值 32767 2147483647
INT_MIN int的最小值 -32767 -2147483647
UINT_MAX unsigned int的最大值 65535 4294967295
SHRT_MAX short的最大值 32767 32767
SHRT_MIN short的最小值 -32767 -32767
USHRT_MAX unsigned short的最大值 65535 65535
LONG_MAX long的最大值 2147483647 2147483647
LONG_MIN long的最小值 -2147483647 -2147483647
ULONG_MAX unsigned long的最大值 4294967295 4294967295
LLONG_MAX long long的最大值 9223372036854775807 9223372036854775807
LLONG_MIN long long的最小值 -9223372036854775807 -9223372036854775807
ULLONG_MAX unsigned long long的最大值 18446744073709551615 18446744073709551615
MB_LEN_MAX 一个多字节字符常量中的最大字符数 1 6

表中典型值为主流32位系统的限制值,但在主流64位系统中也仅存在一个差别:long的长度为64bit。

除此之外,关于浮点数的编译时限制定义在文件<float.h>中,若需要大量使用浮点数时应仔细查看该文件。

另外还有一些常量定义在文件<stdio.h>中,FOPEN_MAX保证可同时打开标准I/P流的最小个数。ISO C规定其最小值为8,POSIX中的STREAM_MAX若有定义则应与FOPEN_MAX具有相同的值。TMP_MAX是由tmpnam函数产生的唯一文件名的最大个数,之后的章节中会对其进行说明。FILENAME_MAX是文件名的最大长度,我们应避免使用这个常量,因为POSIX提供了更好的替代常量(NAME_MAXPATH_MAX)。

POSIX限制

POSIX定义了很多涉及操作系统实现限制的常量。我们只关心其中与基本POSIX接口有关的部分,可以分为下列7类。

  1. 数值限制

    基中一部分是对ISO C标准进行的扩充,包括INT_MAX的最小值为2147483647,INT_MIN为-2147483647,UINT_MAX为4294967295,CHAR_BIT必须是8,SCHAR_MIN必须是-128,SCHAR_MAX必须是127,UCHAR_MAX必须是255。

    另外还包括LONG_BITSSIZE_MAXWORD_BIT

  2. 最小值,定义在<limits.h>中

    名称 说明
    _POSIX_ARG_MAX exec函数的参数长度 4096
    _POSIX_CHILD_MAX 每个实际用户ID的子进程数 25
    _POSIX_DELAYTIMER_MAX 定时器最大超限运行次数 32
    _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 当使用间隔表示法时,regexec和regcomp函数允许的基本正则表达式重复发生次数 255
    _POSIX_RTSIG_MAX 为应用预留的实时信号编号个数 8
    _POSIX_SEM_NSEMS_MAX 一个进程可以同时使用的信号量个数 256
    _POSIX_SEM_VALUE_MAX 信号量可持有的值 32767
    _POSIX_SIGQUEUE_MAX 一个进程可发送和挂起的排队信号的个数 32
    _POSIX_SSIZE_MAX 能存在ssize_t对象中的值 32767
    _POSIX_STREAM_MAX 一个进程能同时打开的标准I/O流数 8
    _POSIX_SYMLINK_MAX 符号链接中的字节数 255
    _POSIX_SYMLOOP_MAX 在解析路径名时,可遍历的符号链接数 8
    _POSIX_TIMER_MAX 每个进程的定时器数目 32
    _POSIX_TTY_NAME_MAX 终端设备名长度,包括终止null字节 9
    _POSIX_TZNAME_MAX 时区名字节数 6

    这些不变值的名字里虽然都有个max,但称为最小值,因为这是一个符合POSIX标准的系统至少应该提供的值。某些不变值在实际应用中太小了。例如大多数UNIX系统中,每个进程可打开的文件数远大于20。另外_POSIX_PATH_MAX的最小限制值为255,也太小了,实际使用中可能会超过这个值。

  3. 最大值:_POSIX_CLOCKRES_MIN

  4. 运行时可以增加的值:CHAR_CLASS_NAME_MAXCOLL_WEIGHTS_MAXLINE_MAXNGROUPS_MAXRE_DUP_MAX

  5. 运行时不变值,定义在<limits.h>中

    名称 说明 最小可接受值
    ARG_MAX exec函数族的参数最大长度 _POSIX_ARG_MAX
    ATEXIT_MAX 可用atexit函数登记的最大函数个数 32
    CHILD_MAX 每个实际用户ID的子进程最大个数 _POSIX_CHILD_MAX
    DELAYTIMER_MAX 定时器最大超限运行次数 _POSIX_DELAYTIMER_MAX
    HOST_NAME_MAX gethostname返回的主机名长度 _POSIX_HOST_NAME_MAX
    LOGIN_NAME_MAX 登录名最大长度 _POSIX_LOGIN_NAME_MAX
    OPEN_MAX 赋予新建文件描述符的最大值+1 _POSIX_OPEN_MAX
    PAGESIZE 系统内存页大小(以字节为单位) 1
    RTSIG_MAX 为应用程序预留的实时信号的最大个数 _POSIX_RTSIG_MAX
    SEM_NSEMS_MAX 一个进程可使用的信号量最大个数 _POSIX_SEM_NSEMS_MAX
    SEM_VALUE_MAX 信号量的最大值 _POSIX_SEM_VALUE_MAX
    SIGQUEUE_MAX 一个进程可排队信号的最大个数 _POSIX_SIGQUEUE_MAX
    STREAM_MAX 一个进程一次可打开的标准I/O流的最大个数 _POSIX_STREAM_MAX
    SYMLOOP_MAX 路径解析过程中可访问的符号链接数 _POSIX_SYMLOOP_MAX
    TIMER_MAX 一个进程的定时器最大个数 _POSIX_TIMER_MAX
    TTY_NAME_MAX 终端设备名长度,其中包括终止的null字节 _POSIX_TTY_NAME_MAX
    TZNAME_MAX 时区名的字节数 _POSIX_TZNAME_MAX
  6. 其他不变值:NL_ARGMAXNL_MAGMAXCOLL_WEIGHTS_MAXLINE_MAXNGROUPS_MAXRE_DUP_MAX

  7. 路径名可变值:FILESIZEBITSLINK_MAXMAX_CANONMAX_INPUTNAME_MAXPATH_MAXPIPE_BUFSYMLINK_MAX

XSI限制

XSI定义了两类常量:

  1. 最小值,定义在<limits.h>中:

    名称 说明 最小可接受值 典型值
    NL_LANGMAX 在LANG环境变量中最大字节数 14 14
    NZERO 默认进程优先级 20 20
    _XOPEN_IOV_MAX readv在writev可使用的最多iovec结构个数 16 16
    _XOPEN_NAME_MAX 文件名中的字节数 255 255
    _XOPEN_PATH_MAX 路径名中的字节数 1024 1024

    关于文件名和路径名的两个常量值是SUS为了弥补POSIX中定义的最小值大小(这可能是考虑了嵌入式POSIX设备)而做的弥补。

  2. 运行时不变值(未必真的不变):IOV_MAXPAGE_SIZE

三个运行时限制相关函数

之前提到的编译时限制可以在相关头文件中获得,而运行时变量则需要在运行时使用这三个函数之一获取,分别是:sysconfpathconffpathconf。这三个函数都定义在<unistd.h>中,函数原型为:

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

对于这三个函数,都使用一个name参数指定要查询的限制,其中:

  • 如果name参数并不是一个合适的常量,则返回-1,并将errno置为EINVAL
  • 如果要返回的值是不确定的,也通过返回-1来体现,不过此时errno不会改变。
  • 其它情况下返回name参数对应的值。

其中sysconf用于获取系统限制,用以_SC_开头的常量作为标识运行时限制的name参数,具体参数列表如下:

限制名 说明 name参数
ARG_MAX exec函数的参数最大长度 _SC_ARG_MAX
ATEXIT_MAX 可用atexit函数登记的最大函数个数 _SC_ATEXIT_MAX
CHILD_MAX 每个实际用户ID的最大进程数 _SC_CHILD_MAX
clock ticks / second 每秒时钟tick数 _SC_CLK_TCK
COLL_WEIGHTS_MAX 在本地定义文件中可以赋予LC_COLLATE顺序关键字项的最大权重数 _SC_COLL_WEIGHTS_MAX
DELAYTIMER_MAX 定时器最大超限运行次数 _SC_DELAYTIMER_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 当使用间隔表示法时,函数regexec和regcomp允许的基本正则表达式重复发生次数 _SC_RE_DUP_MAX
RTSIG_MAX 为应用程序预留的实时信号的最大个数 _SC_RTSIG_MAX
SEM_NSEMS_MAX 一个进程可使用的信号量的最大个数 _SC_SEM_NSEMS_MAX
SEM_VALUE_MAX 信号量的最大值 _SC_SEM_VALUE_MAX
SIGQUEUE_MAX 一个进程可排除信号的最大个数 _SC_SIGQUEUE_MAX
STREAM_MAX 一个SC_STREAM_MAX进程在任意给定时刻标准I/O流的最大个数。如果定义,必须与FOPEN_MAX有相同的值。 _SC_STREAM_MAX
SYMLOOP_MAX 解析路径名时,可遍历的符号链接数。 _SC_SYMLOOP_MAX
TIMER_MAX 每个进程的最大定时器个数。 _SC_TIMER_MAX
TTY_NAME_MAX 终端设备名长度,包括终止null字节。 _SC_TTY_NAME_MAX
TZNAME_MAX 时区名中的最大字节数 _SC_TZNAME_MAX

而pathconf和fpathconf用以标识与路径相关的限制,它们的差别是前者使用路径作为参数,而后者使用一个文件描述符。它们都使用以_PC_开头的常量来表示name值,如下:

限制名 说明 name参数
FILESIZEBITS 以带符号整型值表示在指定目录中允许的普通文件最大长度所需的最小位数 _PC_FILESIZEBITS
LINK_MAX 文件链接计数的最大值 _PC_LINK_MAX
MAX_CANON 终端规范输入队列的最大字节数 _PC_MAX_CANON
MAX_INPUT 终端输入队列可用空间的字节数 _PC_MAX_INPUT
NAME_MAX 文件名的最大字节数 _PC_NAME_MAX
PATH_MAX 相对路径名的最大字节数,包括终止null字节 _PC_PATH_BUF
PIPE_BUF 能原子地写到管道的最大字节数 _PC_PIPE_BUF
_POSIX_TIMESTAMP_RESOLUTION 文件时间戳的纳秒精度 _PC_TIMESTAMP_RESOLUTION
SYMLINK_MAX 符号链接的字节数 _PC_SYMLINK_MAX

其中:

  • _PC_MAX_CANON_PC_MAX_INPUT引用的文件必须是终端文件。

  • _PC_LINK_MAX_PC_TIMESTAMP_RESOLUTION引用的文件可以是文件或目录,如果是目录则返回值作用与目录本身,而不用于目录的文件名项。

  • _PC_FILESIZEBITS_PC_NAME_MAX引用的文件必须是目录,返回值用于目录中的文件名。

  • _PC_PATH_MAX引用的文件必须是目录。当所指定的目录是工作目录时,返回值是相对路径名的最大长度。

  • _PC_PIPE_BUF引用的文件必须是管道、FIFO或目录,当引用管道或FIFO时返回的值是对所引用的文件的限制值,引用目录时返回该目录中创建的任一FIFO的限制值。

  • _PC_SYMLINK_MAX引用的文件必须是目录。返回值是该目录中符号链接可包含字符串的最大长度。

选项

如果编写的程序需要用到XSI选项组中的可选功能,我们就需要一种方法来判断实现是否支持一个给定的选项。和对限制的处理很类似,POSIX定义了三种处理选项的方法:

  • 编译时选项定义在<unistd.h>中
  • 与文件或目录无关的运行时选项用sysconf来判断。
  • 与文件或目录有关的运行时选项用pathconf或fpathconf来判断。

对于每一个选项有三种可能的支持状态:

  • 如果符号常量没有定义或定义值为-1,则不受支持。
  • 如果符号常量定义值大于0,则受支持。
  • 如果符号常量的定义值等于0,则必须调用sysconf、pathconf或fpathconf来判断是否受支持。

pathconf和fpathconf的选项与name参数:

选项名 说明 name参数
_POSIX_CHOWN_RESTRICTED 使用chown是否是受限的 _PC_CHOWN_RESTRICTED
_POSIX_NO_TRUNC 路径名长于NAME_MAX是否出错 _PC_NO_TRUNC
_POSIX_VDISABLE 若定义,可用此值禁用终端特殊字符 _PC_VDISABLE
_POSIX_ASYNC_IO 对相关联的文件是否可以使用异步I/O _PC_ASYNC_IO
_POSIX_PRIO_IO 对相关联的文件是否可以使用优先I/O _PC_PRIO_IO
_POSIX_SYNC_IO 对相关联的文件是否可以使用同步I/O _PC_SYNC_IO
_POSIX2_SYMLINKS 目录中是否支持符号链接 _PC_2_SYMLINKS

sysconf的选项及name参数:

选项名 说明 name参数
_POSIX_ASYNCHRONOUS_TO 是否支持POSIX异步I/O _SC_ASYNCHRONOUS_TO
_POSIX_BARRIERS 是否支持屏障 _SC_BARRIERS
_POSIX_CLOCK_SELECTION 是否支持时钟选择 _SC_CLOCK_SELECTION
_POSIX_JOB_CONTROL 是否支持作业控制 _SC_JOB_CONTROL
_POSIX_MAPPED_FILES 是否支持存储映像文件 _SC_MAPPED_FILES
_POSIX_MEMORY_PROTECTION 是否支持存储保护 _SC_MEMORY_PROTECTION
_POSIX_READER_WRITER_LOCKS 是否支持读者写者保护 _SC_READER_WRITER_LOCKS
_POSIX_REALTIME_SIGNALS 是否支持实时信号 _SC_REALTIME_SIGNALS
_POSIX_SAVED_IDS 是否支持保存的设置用户ID的保存的的设置组ID _SC_SAVED_IDS
_POSIX_SEMAPHORES 是否支持POSIX信号量 _SC_SEMAPHORES
_POSIX_SHELL 是否支持POSIX shell _SC_SHELL
_POSIX_SPIN_LOCKS 是否支持旋转锁 _SC_SPIN_LOCKS
_POSIX_THREAD_SAFE_FUNCTIONS 是否支持线程安全函数 _SC_THREAD_SAFE_FUNCTIONS
_POSIX_THREADS 是否支持线程 _SC_THREADS
_POSIX_TIMEOUTS 是否支持基于超时的变量选择函数 _SC_TIMEOUTS
_POSIX_TIMERS 是否支持定时器 _SC_TIMERS
_POSIX_VERSION POSIX版本 _SC_VERSION
_XOPEN_CRYPT 是否支持XSI加密可靠组 _XOPEN_CRYPT
_XOPEN_REALTIME 是否支持实时选项组 _SC_XOPEN_REALTIME
_XOPEN_REALTIME_THREADS 是否支持实时线程选项组 _SC_XOPEN_REALTIME_THREADS
_XOPEN_SHM 是否支持XSI共享存储选项组 _SC_XOPEN_SHM
_XOPEN_VERSION XSI版本 _SC_XOPEN_VERSION

基本系统数据类型

基本系统数据类型,定义在<sys/types.h>中,用于将系统变量与C数据类型联系在一起。这样就不用考虑具体系统实现细节。包括:

类型 说明
clock_t 时钟滴答计数器
comp_t 压缩的时针滴答
dev_t 设备号
fd_set 文件描述符集
fpos_t 文件位置
gid_t 数值组ID
ino_t i节点编号
mode_t 文件类型,文件创建模式
nlink_t 目录项链接计数
off_t 文件长度的偏移量
pid_t 进程ID和进程组ID
pthread_t 线程ID
ptrdiff_t 两个指针相减的结果
rlim_t 资源限制
sig_atomic_t 能原子性地访问的数据类型
sigset_t 信号集
size_t 对象
ssize_t 返回字节计数的函数
time_t 日历时间的秒计数器
uid_t 数值用户ID
wchar_t 能表示所有不同的字符码