UNIX環境高級編程(4):UNIX標準化及實現(2)

不確定的運行時限制:

如果某些限制值沒有在<limits.h>中定義,則在編譯時不能使用這些限制;而且即使對於運行時限制,如果它們的值是不確定的,那麼它們也是未定義的。

如下的程序用來爲路徑名動態分配存儲區(一般來說,很多程序在編譯時就爲其分配了存儲區,而且不同的程序使用不同的幻數,例如256,512,1024或標準I/O常量BUFSIZ,但很少是正確的)。

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


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


#define SUSV3 200112L
#define PATH_MAX_GUESS 1024


#ifdef PATH_MAX
	static int path_max = PATH_MAX;
#else 
	static int path_max = 0;
#endif

static long posix_version = 0;

char *path_alloc(int *sizep);

char *
path_alloc(int *sizep)
{
	
	int size;
	char *ptr;

	if (posix_version == 0) {
		posix_version = sysconf(_SC_VERSION);	
	}

	if (path_max == 0) {
		errno = 0;
		if ( (path_max = pathconf("/", _PC_PATH_MAX) < 0)) {
			if (errno == 0) {
				path_max = PATH_MAX_GUESS;
			} else {
				printf("pathconf errnor for _PC_PATH_MAX\n");
			}
		} else {
			path_max++;
		}
	}
	if (posix_version < SUSV3) {
		size = path_max + 1;
	} else {
		size = path_max;
	}

	if ( (ptr = malloc(size)) == NULL) {
		printf("malloc error\n");
	}

	if (sizep != NULL) {
		*sizep = size;	
	}

	return ptr;
}


int
main()
{
	int size;
	char *ptr;

	ptr = path_alloc(&size);
	printf("PATH_MAX = %d\n", size);
	if (ptr != NULL) {
		free(ptr);
	}
	exit (0);
}
關於該程序有如下幾點說明:

(1)pathconf函數返回的基於工作目錄的相對路徑名的最大長度,而工作目錄是其第一個參數。所以使用根目錄作爲參數進行調用,並將得到的返回值加1,即可得到路徑名的最大長度。

(2)SUS v3之前,對於PATH_MAX是否在路徑名末尾已經包含了null字符這一點表述的不清楚,如果操作系統遵循先前的標準版本,則需在路徑名分配的存儲數量上加1。

(3)如果PATH_MAX是不確定的,我們只能猜一個值了。

最大打開文件數:

守護進程(daemon,是指在後臺運行且不與終端相連接的一種進程,也稱爲精靈進程或後臺進程)中一個常見的代碼序列是關閉所有打開的文件。我們可以使用POSIX.1中的OPEN_MAX來確定最大打開文件數,以提高代碼的可移植性。

下列程序獲取最大的打開文件數,如果OPEN_MAX是未確定的,則只能猜測一個限制值。儘管這並不能保證在所有情況下都正確動作,但這卻是我們所能選的最好的方法。

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


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


#ifdef OPEN_MAX
	static long openmax = OPEN_MAX;
#else
	static long openmax = 0;
#endif

#define OPEN_MAX_GUESS 256

long open_max(void);

long 
open_max(void)
{
	if (openmax == 0) {
		errno = 0;
		if ( (openmax = sysconf(_SC_OPEN_MAX)) < 0) {
			if (errno == 0) {
				openmax = OPEN_MAX_GUESS;
			} else {
				printf("sysconf error for _SC_OPEN_MAX\n");
			}
		}
	}
	return openmax;
}


int 
main()
{
	printf("oepnmax = %d\n", open_max());
	exit(0);
}

程序的運行結果如下:

這與ulimt -n的結果一致:

上述程序還有一個問題,由於Linux中可以調用ulimit命令隨時更改可打開的最大文件數,而上述程序只在第一次調用open_max函數時調用sysconf函數,所以爲了應對這種情況,應該修改上述程序,使得每次調用open_max時都會調用sysconf函數。

支持Single UNIX Specification 的XSI擴展的系統提供了getrlimit(2)函數,它可用來返回一個進程可以同時打開的最大描述符數。

選項:

如果我們要先寫一些可移植的應用程序而這些程序與所得到支持的選項有關,那麼就需要一種可移植的方法已決定是否支持一個給定的選項。

如同對限制的處理一樣,SUS定義了三種處理方法:

  • 編譯時選項定義在<unistd.h>中;
  • 與文件或目錄無關的選項可用sysconf函數來確定;
  • 與文件或目錄相關的選項可用pathconf或fpathconf函數來確定;
如果符號常量未定義,則必須使用sysconf、pathconf或fpathconf以決定該選項是否受到支持。如果該平臺定義了符號常量,則有以下三種可能:
  • 如果符號常量的定義值爲-1,那麼該平臺不支持相應的選項;
  • 如果符號常量的定義值大於0,那麼該平臺支持相應的選項;
  • 如果符號常量的定義值爲0,則必須調用sysconf、pathconf以確定相應的選項是否受到支持;

功能測試宏:

大多數實現在頭文件中除了定義很多POSIX符號和XSI符號,也在這些頭文件中加上了它們自己的定義。如果編譯一個程序時,只希望使用POSIX的定義而不使用任何實現定義的限制,那麼就需要定義常量_POSIX_C_SOURCE,所有的POSIX頭文件都使用此常量。當定義此常量時,就能排除任何實現專有的定義。

常量_POSIX_C_SOURCE以及_XOPEN_SOURCE被稱爲功能測試宏。

另一個功能測試宏是:__STDC__,它由符合ISO C標準的C編譯器自動定義。這就允許我們編寫ISO C編譯器和非ISO C編譯器都能編譯的程序。儘管大多數編譯器都支持ISO C標準,但是很多頭文件中仍舊使用__STDC__功能測試宏。

基本系統數據類型:

頭文件<sys/types.h>中定義了某些與實現有關的數據類型,他們被稱爲基本系統數據類型(primitive system data type)。還有很多這種數據類型定義在其它頭文件中。在頭文件中,這些數據類型都是用C的typedef功能來定義的,它們絕大多數都以_t結尾。

用這種方法定義了這些數據類型後,就不再需要考慮因系統而異的程序實現細節了。

標準之間的衝突:

就整體而言,這些不同的標準之間配合得相當好。但它們之間也有一些差別,例如ISO C和POSIX.1標準之間就存在一些差別,而SUS v3本身就是POSIX.1的超集。



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