UNIX 环境高级编程|UNIX 标准及实现
GitHub: https://github.com/storagezhang
Emai: debugzhang@163.com
华为云社区:https://bbs.huaweicloud.com/blogs/249303
本文为《UNIX 环境高级编程》第 2 章学习笔记
本章对 UNIX 环境编程的 3 个主要标准进行了说明:
- ISO C
- POSIX
- Single UNIX Specification
分析了这些标准对本书主要关注的 4 个实现所产生的影响:
- FreeBSD
- Linux
- Mac OS X
- Solaris
这些标准都试图定义一些可能随实现而更改的参数,但是这些限制并不完美。
2.2 UNIX 标准化
ISO C
ISO C 标准的意图是提供 C 程序的可移植性,使其能适合于大量的不同的操作系统,而不只是适合 UNIX 系统。
- 此标准不仅定义了 C 程序设计语言的语法和语义,还定义了标准库。
- 因为所有现金的 UNIX 系统都提供 C 标准中定义的库函数,所以该标准库非常重要。
头文件 | 说明 |
---|---|
<asert.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> | 宽字符分类和映射支持 |
IEEE POSIX
POSIX 指的是可移植操作系统接口。
- 该标准的目的是提升应用程序在各种 UNIX 系统环境之间的可移植性。它定义了“符合 POSIX 的” 操作系统必须提供的各种服务。
- POSIX 1003.1 说明了一个接口而不是一种实现,所以并不区分系统调用和库函数,所有在标准中的例程都被称为函数。
- 该标准包含了 ISO C 标准库函数。
- POSIX.1 没有包括超级用户的这样的概念,代之以规定某些操作要求“适当的优先权”。
Single UNIX Specification
Single UNIX Specification 是 POSIX.1 标准的一个超集,它定义了一些附加接口扩展了 POSIX.1 规范提供的功能。
2.3 UNIX 系统实现
标准只是接口的规范,这些标准由厂商采用,然后转变成具体实现。
目前 UNIX 主要有以下实现:
- SVR4
- 4.4BSD
- FreeBSD
- Linux
- Mac OS X
- Solaris
2.5 限制
UNIX 系统实现定义了很多幻数和常量,其中有很多已被硬编码到程序中,或用特定的技术确定。
以下两种类型的限制是必需的:
- 编译时限制(短整型的最大值是什么?)
- 编译时限制可在头文件中定义,程序在编译时可以包含这些头文件。
- 运行时限制(文件名有多少个字符?)
- 运行时现状要求进程调用一个函数获得限制值。
某些限制在一个给定的实现中可能是固定的(因此可以静态地在一个头文件中定义),而在另一个实现中则可能是变动的(需要有一个运行时函数调用),这种类型限制的一个例子是文件名的最大字符数。
为了解决这类问题,提供了以下 3 种限制:
- 编译时限制(头文件)
- 与文件或目录无关的运行时限制(
sysconf
函数) - 与文件或目录有关的运行时限制(
oathconf
和fpathconf
函数)
ISO C 限制
ISO C 定义的所有编译时限制都列在头文件 <limits.h> 中,这些限制常量在一个给定系统中并不会改变。
<stdio.h> 头文件还定义了 3 个编译时限制:
FOPEN_MAX
- 保证可同时打开的标准 I/O 流的最小个数,其最小值是 8.
TMP_MAX
- 由
tmpnam
函数产生的唯一文件名的最大个数。
- 由
FILENAME_MAX
- 避免使用该常量
- POSIX.1 提供了更好的替代常量(
NAME_MAX
和PATH_MAX
)
POSIX 限制
POSIX.1 定义了很多涉及操作系统实现限制的常量,这些限制和常量分成下列 7 类:
- 数值限制
- 最小值
- 最大值
- 运行时可以增加的值
- 运行时不变值(可能不确定)
- 其他不变值
- 路径名可变值
在这些限制和常量中,某些可能定义在 <limits.h> 中,其余的按具体条件可定义、可不定义。
- 一个给定进程的实际值可能依赖于系统的存储总量。
这些最小值是不变的,不随系统而改变,它们指定了这些特征最具约束性的值。
函数 sysconf
、pathconf
和 fpathconf
运行时限制可调用下面 3 个函数之一获得:
#include <unistd.h>
long sysconf(int name);
long pathconf(const char *pathname, int name);
long fpathconf(int fd, int name);
参数:
name
用于标识系统限制- 以
_SC_
开始的常量用作标识运行时限制的sysconf
函数所使用的name
参数 - 以
_PC_
开始的常量用作标识运行时限制的 ``pathconf和
fpathconf函数所使用的
name` 参数
- 以
pathname
:路径名fd
:文件描述符
返回值:
-
若成功,返回相应值
-
若出错,返回 -1
- 如果
name
参数并不是一个合适的常量,还要把errno
置为EINVAL
。 - 有些
name
会返回一个不确定的值,不确定的值通过返回 -1 来体现,而不改变errno
的值。
- 如果
守护进程
守护进程(daemon process)是指在后台运行且不与终端相连接的一种进程。
2.6 选项
如果我们要编写可移植的应用程序,而这些程序可能会依赖于这些可选的支持的功能,那么就需要一种可移植的方法来判断是否支持一个给定的选项。
POSIX.1 定义了 3 种处理选项的方法:
- 编译时选项定义在 <unistd.h> 中
- 与文件或目录无关的运行时选项用
sysconf
函数来判断 - 与文件或目录有关的运行时选项通过调用
pathconf
或fpathconf
函数来判断
如果符号常量未定义,则必须使用 sysconf
、pathconf
或 fpathconf
来判断是否支持该选项:
name
参数前缀_POSIX
必须替换为_SC
或_PC
- 对于以
_XOPEN
为前缀的常量,在构成name
参数时必须在其前放置_SC
或_PC
对于每一个选项,有以下 3 种可能的平台支持状态;
- 如果符号常量没有定义或者定义值为 -1,那么该平台在编译时并不支持相应选项。
- 如果符号常量的定义值大于 0,那么该平台支持相应选项。
- 如果符号常量的定义值为 0,则必须调用
sysconf
、pathconf
或fpathconf
来判断相应选项是否受到支持。
2.8 基本系统数据类型
头文件 <sys/types.h> 中定义了某些与实现有关的数据类型,它们被称为基本系统数据类型。在头文件中,这些数据类型都是用 C 的 typedef
来定义的,它们绝大多数都以 _t
结尾。用这种方式定义了这些数据类型后,就不再需要考虑因系统不同而变化的程序实现细节。
类型 | 说明 |
---|---|
clock_t | 时钟滴答计数器(进程时间) |
comp_t | 压缩的时钟滴答(POSIX.1 未定义) |
dev_t | 设备号(主和次) |
fd_set | 文件描述符集 |
fpos_t | 文件位置 |
gid_t | 数值组 ID |
ino_t | i 节点编号 |
mode_t | 文件类型,文件创建模式 |
nlink_t | 目录项的链接计数 |
off_t | 文件长度和偏移量(带符号的,如 lseek ) |
pid_t | 进程 ID 和进程组 ID(带符号的) |
pthread_t | 线程 ID |
ptrdiff_t | 两个指针相减的结果(带符号的) |
rlim_t | 资源限制 |
sig_atomic_t | 能原子性地访问的数据类型 |
sigset_t | 信号集 |
size_t | 对象(如字符串)长度(不带符号的) |
ssize_t | 返回字节计数的函数(带符号的,如 read 、write ) |
time_t | 日历时间的秒计数器 |
uid_t | 数值用户 ID |
wchar_t | 能表示所有不同的字符码 |