UNIX环境高级编程(3):UNIX标准化及实现(1)

虽然UNIX应用程序在不同的UNIX操作系统版本之间进行移植相同容易,但是20世纪80年代UNIX版本的剧增以及它们之间的差别扩大,导致很多用户呼吁对其进行标准化。标准化工作的一个重要部分是对每种实现必须定义的各种限制进行说明。

UNIX标准化:

ISO C:

1989年,C程序设计语言的ANSI标准X3.159-1989得到批准,随后该标准被采纳为国际标准ISO/IEC 9899:1990。ANSI是美国国家标准学会(American National Standards Institute)的简称,而ISO是国际标准化组织(International Organization for Standardization)的简称。ISO C标准现在由ISO/IEC的C程序设计语言国际标准化工作组维护和开发。

ISO C标准的意图是提供C程序的可移植性,使其能适合于大量的不同的操作系统,而不只是UNIX系统。该标准不仅定义了C语言的语法和语义,还定义了其标准库。1999年,ISO C标准被更新为ISO/IEC 9899:1999。

IEEE POSIX:

POSIX是一系列由IEEE(Institute of Electrical and Electronics Engineers,电气与电子工程师协会)制定的标准。POSIX指的是可移植的操作系统接口(Portable Operation System Interface)。它原来指得是IEEE标准1003.1-1998(操作系统接口),后来则扩展成包括很多标记为1003的标准及标准草案,包括shell和实用程序(1003.2)。

与本书相关的是1003.1操作系统接口标准,该标准的目的是提高应用程序在各种UNIX系统环境之间的可移植性,它定义了“依从POSIX”的操作系统必须提供的各种服务。而且1003.1标准定义的是一个接口,而不是 一种实现,所以不区分系统调用和库函数,标准中的所有例程都被称为函数。

IEEE1003.1-1988标准经修改后提交给ISO,最终的文档为IEEE std.1003.1 1990,这也就是国际标准ISO/IEC 9945-1:1990,该标准通常被称为POSIX.1。POSIX.1标准包括了ISO C标准库函数。 POSIX.1标准现由称为Austin Group的开放工作组维护。

Single UNIX Specification:

Single UNIX Specification(单一UNIX规范)是POSIX.1标准的一个超集,定义了一些附加的接口,这些接口扩展了基本的POSIX.1规范提供的功能。相应的系统接口全集被称为X/Open系统接口(XSI,X/Open System Interface)。XSI还定义了实现必须支持POSIX.1的哪些可选部分才能认为是遵循XSI的。

只有遵循XSI的实现才能称为UNIX系统。Open Group拥有UNIX商标,并且使用Single UNIX Specification来定义一个实现必须支持的接口,这样的实现才能称为UNIX系统。

Single UNIX Specification(SUS)由Open Group发布,Open Grop由两个业界社团X/Open和Open Software Foundation(OSF)合并而成。

FIPS:

FIPS:联邦信息处理标准(Federal Information Processing Standard),它由美国政府出版,用于计算机系统的采购。该标准的影响是:它要求任何希望向美国政府销售POSIX.1兼容的计算机系统的厂商应支持POSIX.1的某些可选功能,但FIPS的影响正在减退,这里不再考虑。

UNIX系统实现:

标准只是接口的规范,标准由制造商采用,然后转变成具体实现。

UNIX的各种版本和变体都起源于在PDP-11系统上运行的UNIX分时系统第6版和第7版(通常称为V6和V7)。这两个版本是在贝尔实验室以外首先得到广泛应用的UNIX系统,之后演变出三个分支:

  • AT&T分支,从此导出了系统III和系统V(被称为UNIX的商用版本);
  • 加州大学伯克利分校分支,从此导出了4.xBSD实现;
  • AT&T贝尔实验室的计算科学研究中心开发的UNIX研究版本。从此导出UNIX分时系统的第8,9版以及1990年的最后一版第10版;

SVR4:SVR4(UNIX System V Release 4,UNIX系统V第4版)是AT&T的UNIX实验室的产品。

4.4BSD:BSD(Berkeley Software Distribution)版由加州大学伯克利分校的计算机科学研究小组(CSRG)研究和发布的。4.4BSD-Lite第2版是CSRG的最后一个BSD版本。

FreeBSD:FreeBSD的基础是4.4BSD-Lite操作系统,在加州大学伯克利分校CSRG决定终止其在UNIX操作系统的BSD版本的研发工作后,为了继续坚持BSD系列,设立了FreeBSD项目。由FreeBSD项目产生的所有软件(包括二进制程序和源代码)都是免费使用的。还有其他几个基于BSD的免费操作系统:NetBSD项目,OpenBSD项目。

Linux:Linux是一种提供丰富的UNIX编程环境的操作系统。在GNU通用公共许可证的指导下,Linux是免费使用的。Linux的普及是计算机产业中一道亮丽的风景线。

Mac OS x:和以前的版本相比,Mac OS X采用了完全不同的技术,其核心操作系统被称为Darwin,它基于Mach内核和FreeBSD操作系统的组合。Darwin是一个开放源码项目。

Solaris:Solaris是由Sun公司开发的UNIX系统版本,它基于SVR4,它是唯一在商业上取得成功的SVR4后裔,并被正式验证为UNIX系统。

其他UNIX系统:已经通过验证的其它UNIX系统的版本包括: AIX,IBM版的UNIX; HP-UX,HP版的UNIX系统等。

标准和实现的关系:

各个标准定义了任一实际系统的子集。在《APUE》中主要关注4种实际的UNIX系统:FreeBSD,Linux,Mac OS以及Soloaries。在这4种操作系统中,只有Solaries能够称自己是一种UNIX系统,但是所有这4种系统都提供了UNIX编程环境。因为这4种操作系统都在不同程度上依从POSIX。

限制:

UNIX系统实现定义了很多幻数和常量,由于大量标准化工作的努力,已有若干种可移植的方法用以确定这些幻数和实现定义的限制,这非常有助于软件的可移植性。

以下两种类型的限制是必需的:编译时限制,运行时限制。编译时限制可在头文件中定义,在程序编译时可以包含这些头文件。但是,运行时限制则要求进程调用一个函数以获取此限制值。而且,某些限制在一个给定的实现中可能是固定的(因此可以静态地在一个头文件中定义),而在另一个实现中可能是变化的(需要调用一个运行时函数)。

为了解决这类问题,提供了以下三种限制:

  • 编译时限制(头文件);
  • 不与文件或目录相关联的运行时限制(sysconf函数);
  • 与文件或目录相关联的运行时限制(pathconf和fpathconf函数);

ISO C限制:ISO C定义的下限值都是编译时限制,这些常量总是定义在头文件在中,而且在一个给定的系统中不会改变。

POSIX限制:POSIX.1标准定义了很多涉及操作系统实现限制的常量,但是我们只关心与基本POSIX.1接口有关的部分。这些限制和常量被分成下列5类:

(1)不变的最小值:他们并不随系统而改变,一个符合POSIX.1的实现应当提供至少这样大的值。尽管一个具体实现可能提供比这个最小值更大的值,但是一个严格遵守POSIX应用程序不应要求更大的值。

每一个不变最小值都有一个相关的实现值,其名字是相应的不变最小值的名字去掉_POSIX_后构成的。这些实现值就是下面列出的2-5项:不变值,运行时可增加的值,运行时不变的值,以及路径名可变值。问题是并不能确保所有的这些实现值都在<limits.h>头文件中定义。所以POSIX.1提供了三个运行时函数以供调用:sysconf、pathconf以及fpathconf。使用这三个函数可以在运行时得到实际的实现值。而且其中某些值被POSIX.1定义为“可能不确定的”(逻辑上无上限),这就意味着该值没有实际上限。

(2)不变值:SSIZE_MAX;

(3)运行时可以增加的值;

(4)运行时不变的值(可能不确定);

(5)路径名可变值(可能不确定);

在这些限制和常量中,有一些可定义在<limits.h>中,其余的则可按具体条件可定义或不定义。

XSI限制:XSI还定义了处理实现限制的下面几个常量:

(1)不变的最小值;

(2)数值限制;

(3)运行时不变值(可能不确定);

sysconf、pathconf 与fpathconf函数:

POSIX.1标准定义了实现必须支持的各种最小值,但是如何才能获取一个特定系统实际支持的限制值呢?某些限制值在编译时可用,而另外一些则必须在运行时确定。运行时限制可调用下面三个函数中的一个而取得:

#include <unistd.h>

long sysconf(int name)

long pathconf(const char *pathname, int name)

long fpathconf(int fileds, int name)

后两个函数之间的差别是一个用路径名作为参数,另一个则用文件描述符做参数。关于上述函数的返回值,需要注意以下几点:

(1)如果函数参数不是合适的常量,则所有函数都返回-1,并将errno设置为EINVAL;

(2)有些name可返回变量的值,或者返回-1,表示该值是不确定的,此时并不改变errno的值。

下列程序打印了各项限制的实际值,并处理未定义限制的情况:

/*
 * Copyright (C) [email protected]
 */


#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>


static void pr_sysconf(char *mesg, int name);
static void pr_pathconf(char *mes, char *path, int name);

int
main(int argc, char *argv[])
{
	if (argc != 2) {
		printf("usage : ./limit dirname\n");
		exit(1);
	}

#ifdef ARG_MAX
	printf("ARG_MAX defined to be %d\n", ARG_MAX);
#else
	printf("no symbol for ARG_MAX\n");
#endif
#ifdef _SC_ARG_MAX
	pr_sysconf("ARG_MAX =", _SC_ARG_MAX);
#else
	printf("no symbol for _SC_ARG_MAX\n");
#endif

/* similar processing for all the rest of sysconf symbols ...... */
#ifdef _POSIX_NAME_MAX
	printf("_POSIX_NAME_MAX defined to be %d\n", _POSIX_NAME_MAX);
#else
	printf("no symbol ro _POSIX_NAME_MAX\n");
#endif
#ifdef NAME_MAX
	printf("NAME_MAX defined to be %d\n", NAME_MAX);
#else
	printf("no symbol for NAME_MAX\n");
#endif
#ifdef _PC_NMAE_MAX
	pr_path_conf("NAME_MAX =", argv[1], _PC_NMAE_MAX);
#else
	printf("no symbol for _PC_NAME_MAX\n");
#endif

/* similar processing for all the rest of pathconf symbols ...... */
	exit(0);
}

static void 
pr_sysconf(char *mesg, int name)
{
	long val;
	fputs(mesg, stdout);

	errno = 0;
	if ( (val = sysconf(name)) < 0) {
		if (errno != 0) {
			if (errno == EINVAL) {
				fputs(" (not supported)\n", stdout);
			} else {
				fputs(" sysconf error\n", stdout);
			}
		} else {
			fputs(" (no limit)\n", stdout);
		}
	} else {
		printf(" %d\n", val);
	}
}


static void 
pr_pathconf(char *mesg, char *path, int name)
{
	long val;
	fputs(mesg, stdout);

	errno = 0;
	if ( (val = pathconf(path, name)) < 0) {
		if (errno != 0) {
			if (errno = EINVAL) {
				fputs(" (not supported)\n", stdout);
			} else {
				fputs(" sysconf error\n", stdout);
			}
		} else {
			fputs(" (no limit)\n", stdout);
		}
	} else {
		printf(" %d\n", val);
	}
}


关于该程序有以下几点值得说明:

(1)并非每种平台都会定义所有的符号,所以对于获取每个限制值,都使用必要的#ifdef语句(例如对于编译时限制,在头文件中就会定义该限制值,但是对于运行时限制,头文件中不会有该限制对应的标示符常量

(2)如果该限制值在头文件中定义了,则直接获取该限制值;

(3)如果该限制值在头文件中没有定义,则需要调用sysconf或pathconf函数来获取值,具体调用哪个函数取决于该限制是否与文件或目录相关。


你可能感兴趣的:(apue)