Skip to main content

第二章: 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_MAXPATH_MAX 共同决定.

POSIX 限制

有如下分组:

  1. 数值的限制: 如 SSIZE_MAX, WORD_BIT 等.
  2. 最小值的限制: 如 _POSIX_ARG_MAX
  3. 最大值: 如 _POSIX_CLOCKRES_MIN
  4. 运行时可增长的值: 如 CHARCLASS_NAME_MAX
  5. 运行时可变的值: 如 ARG_MAX
  6. 其他不变的值: 如 NL_ARGMAX
  7. Pathname 变量值: 如 FILESIZEBITS, LINK_MAX, NAME_MAX

需要注意, 最小值并非一成不变, 最小值限制在不同的操作系统下, 可能有变化, POSIX 只是定义了最小值应 "至少这么小".

XSI 限制

在 POSIX.1 的"可选部分", 即 XSI 中也有限制的定义, 包括:

  1. 最小值: 有 5 个定义
  2. 运行时不变值

获取运行时限制的三个 conf 函数

包括: sysconf, pathconf, fpathconf.

选项

由于 XSI 的可选接口存在, 我们需要一个途径来确认某个系统是否支持某些可选特性.

  1. 编译时选项定义在 <unistd.h> 中.
  2. 运行时选项(和文件/目录无关的)可以通过 sysconf 获取
  3. 运行时选项(和文件/目录相关的)可以通过 pathconffpathconf 获取.

功能检测宏(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.