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函數來獲取值,具體調用哪個函數取決於該限制是否與文件或目錄相關。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章