第二章: UNIX 标准和实现
UNIX 以及 C 编程标准化工作经过多年努力已卓有成效.
UNIX 标准
ISO C
ISO C 标准从 89 年一版, 99 一版, 01, 04, 07 各一版, 现在实际上已经到了 23 版了.
ISO C 库可以分为 24 个部分, POSIX.1 标准已完整包含这些部分中的头文件, 其他的标准也是如此.
IEEE POSIX
POSIX 标准最初是由 IEEE 发布的. 我们讨论的主要是 POSIX 的 1003.1 "操作系统接口标准". 这个标准的目的在于提升应用程序在不同 UNIX 系统中的兼容性.
这个标准中定义了操作系统必须提供的服务.
POSIX 1003.1 标准在许多年份都有发布和更新. 我们主要看的是2008 年的 POSIX.1 版本.
Single UNIX Specification
SUS 是 POSIX.1 的扩展. POSIX.1 标准中有一个可选的接口部分, 被称为 XSI, 实现了这些接口的系统被称为 XSI Conforming. 而只有 XSI Conforming 的系统可以称为 "UNIX 系统".
系统限制
操作系统中定义了许多的魔法数和常量. 大部分都是被硬编码到或通过 ad-hoc 的方式进入到程序中. 操作系统提供获取大部分这些魔法数和常量的函数, 以便提高程序在不同系统的兼容性. 限制可以分为两大类:
- 编译时限制: 比如 short integer 的位数
- 运行时限制: 比如 filename 的最大字节数
编译时限制的配置常数通常都在头文件中定义了. 运行时限制则需要程序在运行的时候调用函 数去获取, 因此, 限制可以通过如下三个方式取得:
- 编译时限制从头文件中取得: (limits.h)
- 与文件或目录无关的运行时限制: 通过函数取得(sysconf)
- 与文件或目录有关的运行时限制: 通过函数取得(pathconf, fpathconf)
但实际上, 有些运行时限制在不同 UNIX 实现下都是固定的, 则它也可能会被定义在头文件中, 但程序仍然需要通过上述三个 conf 组函数去获取它们.
ISO C 限制
由 ISO C 定义的所有编译时限制都在 limits.h
头文件中, 且在所有的 UNIX 系统下保持一致. 但需要注意的是这样的情况: 比如某些操作系统可能不会提供 signed/unsigned char 的区分(有些只使用 signed char).
ISO C 中也可能提供和 POSIX.1 类似语义的限制, 这种情况下, 我们需要选择 POSIX.1 提供的(因起更贴近系统), 比如 ISO C 的 FILENAME_MAX
, 在 POSIX.1 中有 NAME_MAX
和 PATH_MAX
共同决定.
POSIX 限制
有如下分组:
- 数值的限制: 如 SSIZE_MAX, WORD_BIT 等.
- 最小值的限制: 如 _POSIX_ARG_MAX
- 最大值: 如 _POSIX_CLOCKRES_MIN
- 运行时可增长的值: 如 CHARCLASS_NAME_MAX
- 运行时可变的值: 如
ARG_MAX
- 其他不变的值: 如 NL_ARGMAX
- Pathname 变量值: 如 FILESIZEBITS, LINK_MAX, NAME_MAX
需要注意, 最小值并非一成不变, 最小值限制在不同的操作系统下, 可能有变化, POSIX 只是定义了最小值应 "至少这么小".
XSI 限制
在 POSIX.1 的"可选部分", 即 XSI 中也有限制的定义, 包括:
- 最小值: 有 5 个定义
- 运行时不变值
获取运行时限制的三个 conf 函数
包括: sysconf
, pathconf
, fpathconf
.
选项
由于 XSI 的可选接口存在, 我们需要一个途径来确认某个系统是否支持某些可选特性.
- 编译时选项定义在
<unistd.h>
中. - 运行时选项(和文件/目录无关的)可以通过
sysconf
获取 - 运行时选项(和文件/目录相关的)可以通过
pathconf
和fpathconf
获取.
功能检测宏(Feature Test Macros)
常量 _POSIX_C_SOURCE
和 _XOPEN_SOURCE
被称为功能检测宏. 比如如果我们想只使用 POSIX.1 的定义, 则可以在源码文件中包含如下:
#define _POSIX_C_SOURCE 200809L
并且可以在 cc 命令中通过类似 -D_POSIX_C_SOURCE file.c -o file
这种方式使用.
系统数据类型(原子类型)
早期的 UNIX 中 C 的数据类型根据系统类型有不同. 比如 device major 和 device minor 被存放在一个 16 bit 类型中, 高 8 bit 为 major, 低 8 bit 为 minor. 但这样就有一个问题, 有些系统需要支持超过 256 个不同值(设备个数), 此时就无法达到目的了.
在头文件 <sys/types.h>
中定义的就是和系统特定的数据类型, 大部分情况下都以 _t
结尾, 比如 size_t
, mode_t
等.
标准之间的差异
我们主要想讨论的是 ISO C 标准和 POSIX.1 标准之间的差异. 比如 ISO C 中通过 clock
获取进程使用的 CPU time, 而 POSIX.1 中则是通过 times
同时获取 CPU time 和 clock time.