APUE_Chapter02_UNIX实现标准_笔记总结

            ***PAY A TRIBUTE TO W.Richard Stevens***

Chapter02: Unix实现标准

2.1 简介

在十九世纪八十年代针对层出不穷的Unix版本导致很多用户和美国政府开始制定标准化.在这次笔记中我们将主要讨论在过去的三十年间各种各样的标准. 有人会说学这些没用,但是当你对Unix编程有一定的了解后,你会发现这章很重要,能帮助你理解为什么一些类型这样定义,为什么不转64位等等问题.所以跟着我的步伐开动吧~~~~

2.2 Unix标准

相比学过C的人都知道ANSI, 在1989年后期,ANSI 针对C语言的标准X3.159-1989成立了. 并且被国际标准组织ISO/IEC9899:1990所采纳. ANSI(American National Standards Institute)美国国家标准机构.
现在的C语言是由ISO/IEC的WG14来进行维护和开发, 所以也叫做了ISO C. 他们的宗旨是开发跨平台的C标准,而不仅仅针对于Unix.
自从1999年的时候,有三个技术性文章发表来更正ISO C标准, 分别在01, 04h和07年.
相比大家都听说过C99,但这到底是个什么,怎么来的?以下一段摘自百度:
C99是在C89/90的基础上发展起来的,增加了基本数据类型、关键字和一些系统函数等。C99有一部分是对于增加了宽字符集,还加入了一些库函数,是继C89标准之后的第二个C语言官方标准。第一个C++语言官方标准C++98标准,就是基于C89编写的,因此C99标准新增的语法特性在C++的编译器中就或多或少地支持了,而完全或几乎完全支持C99标准的主流编译器有:GCC[3] 、Clang[4] 、Intel C++ Compiler[5] 等。另外,Visual Studio2013也部分支持了C99语法特征.在ANSI标准化发布了C89标准以后,C语言的标准在一段相当的时间内都保持不变,尽管C++继续在改进。(实际上,Normative Amendment1在1995年已经开发了一个新的C语言版本(即C95)。但是这个版本很少为人所知。)标准在90年代才经历了改进,这就是ISO/IEC 9899:1999(1999年出版)。这个版本就是通常提及的C99。
ISO C库可以基于以下头文件分为了24块,POSX.1标准包含了这些头文件,当然还包含了其他的一些. 所有的这些头文件都在FreeBSD 8.0, Linux3.2.0, Mac OS X 10.6.8和Solaris10中实现.
这里写图片描述
ISO C头取决于不同操作系统上的不同C编译器版本. FreeBSD8.0用的是gcc4.2.1, Solaris 10用的是gcc3.4.3, Ubuntu(Linux 3.2.0)用的是gcc4.6.3, Mac OS X10.6.8用的是4.0.1和4.2.1的gcc.
那么POSIX是个什么鬼呢?它是标准大佬IEEE家族中的一员而已. 一开始就是IEEE1003.1-1988. POSIX(Protable Operating System Interface).起初是制定操作系统的标准化接口,到了后期也参与了shell和工具的标准化.
这本书中用到的就是IEEE的1003.1针对操作系统的接口标准,这个标准就是提升在Unix环境下应用程序的便携性. 尽管1003.1是针对Unix,但是绝对不仅限制于Unix和类Unix系统. 一些产商将POSIX也用在了其他系统方面.
因为1003.1只是制定了接口而没定义实现, 所以没有区分出系统调用和库函数.
标准都要与时俱进,1003.1也不例外, 当国家标准组织将1003.1接手后,并没有对接口和函数进行改变,而是对文档进行了修改. 这写文档刊登在了IEEE1003.1-1990上. 这也就是ISO/IEC9945-1:1990. 这就是大名鼎鼎的POSIX.1.
IEEE1003.1不断的在发展,在1996年,一个新修订的1003.1问世,其中包含了1003.1-1990的标准并且还加入了多线程编程, 对于POSIX线程就叫做pthreads.这一改变也被ISO/IEC9945-1:1996接纳.之后1999, 2000都增加了实时接口.
在2001年的时候,与之前的1003.1脱离出来一个新的1003.2标准,这个也是针对Unix的单独的标准(Single Unix Specificion)SUS. 这其中就包含了系统接口,命令和工具,系统接口和头文件.
在2004年的时候POSIX进行了更新,2008年被ISO/IEC采纳更新.
在这本书中我们讨论的就是2008版的POSIX, 这一版中的接口分为了必须的和可选择的. 可选择的部分又分为了40块.
POSX.1没有包含超级管理员的概念,然后,一些操作是需要”合适的权限”的.
这个图就是POSIX.1定义的XSI可选头文件:
这里写图片描述
很多人会说到底什么是XSI, 在接下来我们会介绍XSI.
单一UNIX规范(Single UNIX Specification)是描述标准UNIX操作系统的C语言程序和用户命令接口的 行业标准。遵循单一UNIX规范的UNIX操作系统被答应使用UNIX 商标。POSIX.1只是单一Unix规范中的子集,它提供了一些POSIX.1中没有的接口.
POSIX.1中的XSI(X/Open System Interface)系统接口选项描述了可选接口,也定义了遵循XSI的实现必须遵循POSIX.1中的哪些可选部分. 这些包括: 文件同步,线程栈地址和长度属性, 线程进程共享同步和_XOPEN_UNIX符号常量. 只有遵循XSI的实现才能称得上是UNIX.
有些接口在XSI的系统中是可选的, 这些被分在了可选组中. 具体如下:
加密: 由符号常量_XOPEN_CRYPT提供
实时: 由符号常量_XOPEN_REALTIME提供
高级实时
实时线程: _XOPEN_REALTIME_THREADS提供
高级实时线程.
单一Unix规范是Open Group的出版物,而Open Group是X/Open和开放软件基金会(OSF)合并的. X/Open过去出版了X/Open Protability Guide, 他采用了若干具体的标准填补了其他标准功能缺失的空白. 这些指南的目的是改善应用的可移植性,而不仅仅是遵循已出版的标准.
POSIX是一个最初由IEEE制定的标准族,POSIX指的是可移植操作系统结构(Protable Operating System Interface),该标准的目的是提升应用程序在各种UNIX系统环境之间的可移植性,
POSIX.1包含了ISO C标准函数库,同时结构分类两部分:必须部分和可选部分(X/Open系统接口(X/Open System Interface,XSI))。
POSIX.1中的X/Open系统接口(X/Open System Interface ,XSI)选项描述了可选的接口,也定义了遵循XSI的实现必须支持POSIX.1的哪些可选部分,只有遵循XSI实现的才能称为UNIX系统。
可以总结为 ISO C是POSIX的子集,POSIX是SUS的子集,SUS是POSIX的扩展,X/Open系统接口(XSI)是POSIX.1中的接口,描述了POSIX中的可选接口,只有遵循XSI的实现才能称为UNIX系统.

2.3 Unix系统实现

在上面我们简单讨论了ISO C, IEEE POSIX 和Single Unix Specification这三大原始的机构. 然而我们要知道标准只是接口的规范. 如何与现实接轨呢? 不同的产商就利用这些标准进行了他们不同的实现,这本书我们就是了解这些标准和一些不同的实现. Unix的各种版本和变体都是起源于PDP-11系统上运行的Unix分时系统第6版和第7版. 这两个版本是在贝尔实验室意外首先得到引用的Unix系统. 从这里可以演进出三个分治:
1. ATT分支, 从此引起了System 3和System V, 这也叫做商业版本的Unix.
2. 加州伯克利的4.xBSD
3. 由ATT贝尔实验室计算机科学研究中心研发的Unix研究版本, 引起了Unix分时系统.
SVR4是ATT的Unix系统实验室的产品,它将下列系统的功能合并到了一个一致的操作系统中: ATT的SVR3.2, Sun Microsystem的SunOS, 加州伯克利的4.3BSD以及微软的Xenix系统. ATT也出版了系统V接口的定义(SVID). 第三版说明了Unix系统要达到SVR4质量要求必须提供的功能. 如同POSIX.1一样,SVID定义了一个接口,而不是一种实现. SVID并不区分system call和库函数.
BSD(Berkeley Software Distribution)是加州伯克利计算机系统研究组(CSRG)研发的. 4.2BSD是在1983年问世,4.3是1986年问世. 其后的4.3BSD Reno版支持很多POSIX.1的功能. 最初的BSD系统包含了ATT专有的源码,它们需要ATT许可证. 为了获得BSD的源码,必须有ATT许可证. 这几年越来越多的ATT源码被替换成非ATT源码, 并且添加了很多非ATT方面的新功能. 在1989年,4.3BSD Tahoe中有很多非ATT源码包装成BSD网络软件1.0. 并开源. 1991年发布的BSD网络软件2.0, 是从4.3BSD Reno的派生物. 这样大部分的BSD代码不熟ATT限制. 并公开源码.4.4BSD-lite是CSRG计划开发的最后一个发行版, 由于USL产生法律纠纷,导致该版本发型一度推迟, 在1994年正式发布. 并且完全不需要Unix源代码许可证就可以使用. 1995年CSRG发布了bug修复版. BSD系统由一开始的PDP-11上运行到VAX小型机到工作站. 之后又广泛引用在80386个人计算机上.
FreeBSD是基于4.4BSD-Lite操作系统. CSRG决定终止UNIX操作系统的研发后,386BSD项目被忽视了很长时间, 为了坚持BSD系列,形成了FreeBSD. 由FreeBSD项目产生的所有软件,二进制代码和源码都是免费使用的. 针对BSD的NetBSD是注重不同硬件之间的可移植性,OpenBSD是注重安全方面.
Linux是一种提供类似于Unix的丰富的编程环境的操作系统. 在GNU公用许可证的指导下, Linux也是免费的. Linux也是通常第一个支持新硬件的系统. 1991年由Torvalds经MINIX改编而成. 一个名不见经传(grass-roots)的努力掀起了全球志愿者的风潮. 我们这本书中也会对Linux3.2.0的Ubuntu12.04进行试验.
MaxOS X与以前的版本相比,是用了完全不同的技术,其操作系统核心是Darwin, 它是基于Mach内核,FreeBSD以及面向对象框架的驱动和其他内核扩展的结合. Mac OS X10.5de Intel部分已经被验证为是一个Unix系统.
Solaris是Sun Microsystems开发的Unix系统版本. 它基于SVR4, 在超过15年的时间里, Sun Microsystems对其不断改进. 它是唯一在商业上取得成功的SVR4后裔, 也被正式验证为Unix系统.
其他的Unix系统有: AIX, IBM的Unix。 HP-UX. IRIX. UnixWare.

2.4 实现和标准的关系

在之后我们主要关注于四种Unix系统: FreeBSD 8.0, Linux 3.2.0, Mac OS X 10.6.8 和Solaris 10. 尽管只有MacOS和Solaris能真正称为是Unix,但是它们的程序环境都是相似的. 因为他们都是POSIX规则的不同实现.所以我们也会将重点放在POSIX的实现规则上. 这些实现都对他们的早期系统进行向后兼容(SVR3.2和4.3BSD), 比如, Solaris支持POSIX.1的非阻塞IO(O_NONBLOCK)和传统System V的(O_NDELAY). 包括后面讲到的singal也是,我们会主要管组POSIX的实现标准.

2.5 限制

为了更有助于Unix程序的可移植性, 进行了两种必要的限制:
1. 编译时限制(比如, 在短整型中最大的值是多少)
2. 运行时限制(比如, 在文件名中最大有多少字节)
编译型的限制是能够定义在头文件中供任何一个文件使用,但是运行时的限制是需要进行调用函数来获取相应的限制值.
此外,如果在指定的实现中一些限制可以固定,那么在头文件中定义成静态的,但是还是可以在其他的实现中变化的.比如说文件名的最大长度, 在SVR4允许14字节,但是在BSD中允许255字节. 针对不同的文件系统也是有着不同的定义.
为了解决这样的问题,定义了三个限制:
1. 编译时限制(headers).
2. 运行时限制但是不关联文件和目录(sysconf函数)
3. 运行时限制关联问价和目录(pathconf和fpathconf函数)
如果在头文件中定义,那么就是不变静态的,如果没有在头文件中定义,那么就必须使用三个函数中的一个来获取值.
所有ISO C定义的编译时限制都在limits.h中,这些常量在指定系统中是不改变的. 在ISO C中定义的一些常量比如整形的最小值但是在POSIX中不一致,为了兼容POSIX的值,ISO C进行了改变. 还有些例如我们之后会碰到的FOPEN_MAX, 表示IO流的最大打开数量, 这个在stdio.h中定义,在POSIX中也有这样的宏,叫做STREAM_NAX. 还有是在ISO C中定义了FILENAME_MAX,表示文件名最大的字节数,但是我们更多使用的是POSIX中的NAME_MAX和PATH_MAX.
这里写图片描述
POSIX.1中定义了大量的常量来进行操作系统中的实现的限制. 这也是POSIX.1的混乱之处. 但是我们只是考虑POSIX.1接口的基本部分:
1. 数字限制: LONG_BIT, SSIZE_MAX 和WORD_BIT
2. 最小值
3. 最大值: _POSIX_CLOCKERES_MIN
4. 运行时可增加的值: CHARCLASS_NAME_MAX, COLL_WEIGHTS_MAX, LINE_MAX, NGROUPS_MAX, RE_DUP_MAX.
5. 运行不变的值.
6. 其他不变的值: NL_ARGMAX, NL_MSGMAX, NL_SETMAX, NL_TEXTMAX
7. 路径名变化的值: FILESIZEBITS, LINK_MAX, MAX_CANON, MAX_INPUT, NAME_MAX, PATH_MAX, PIPE_BUF, SYMLINK_MAX.
针对这些有的可能定义在了limits.h中,但是有的是没有定义在其中的. 一下是定义的最小值:
这里写图片描述
这些值从一个系统到另一个系统是不变化的. 也就是说遵循POSIX.1的实现都要大于等些值.这就是为什么叫最小值,尽管它们的名字中包含的MAX.
在这里需要说明的一点是,严格遵循POSIX的应用和仅仅遵循POSIX的应用是不一样的. 后者只是用IEEE1003.1-2008中定义的接口,但是严格遵循POSIX的会有更进一步的限制. 比如不能依赖没有定义的一些行为属性, 常量值要大于对应的最小值等.
但是有些POSIX的最小值实在是太小了,已经无法满足当今的需求了,比如对于大多数的Unix能够允许在一个进程中同时打开20个文件, 还有路径限制 _POSIX_PATH_MAX值有256字节. 这就意味着我们不能用这两个_POSIX_OPEN_MAX和_POSIX_PATH_MAX. 在这里我们重新定义了一些并且去掉了_POSIX的前缀就是表示不满于POSIX的一些限制了,进行了改进,但是这25个定义也不一定都出现在了limits.h中. 比如说某些值就不会出现在头文件中如果这个值是根据指定的进程中内存的大小而定的. 为了决定的真正在运行时的实现值,POSIX.1大哥又出手了, 提供了sysconf, pathconf, fpathconf函数. 但是还有一个问题就是在POSIX.1中定义的某些值是没有上限的值,例如在Solaris进程结束的时候时可运行atexit这个函数是受系统总存储量限制的. 所以在Solaris中ATEXIT_MAX是不确定的.
这里写图片描述
以上这些是POSIX的限制,那么我们再讨论讨论XSI的限制.
1. 最小值
2. 运行时的不变值: IOV_MAX和PAGE_SIZE
这里列出了最小值:
这里写图片描述
最后的两个值是对POSIX的值定义太小的抗议, 所以Single Unix Specification 在遵循XSI的系统中对这两个值进行了扩大.
在我们之前也提到了各种最小值的限制,但是我们在真实的系统中到底怎么获得这些值呢?有些值是在编译时可获得的,有些是在运行时决定的,我们也提到有些值在系统中是不变的,但是有些值会关联文件和目录而改变. 运行时的限制的值我们通过以下三个函数就能获取到:
这里写图片描述
针对sysconf能过获取到的是:
这里写图片描述
在这里最后一列的名字参数就是syscong(3)中的参数值,都是以SC开头的.
针对pathconf(3)和fpathconf(3)能获取到的是:
这里写图片描述
参数常量以PC开头的都是pathconf和fpathconf中的.
这对以上三个函数:
1. 如果name不是一个合适的常量,那么函数都会返回-1并且errno设置为EINVAL.
2. 有些name会返回变量值或提醒这个值是不确定的, 不确定的值通过-1返回但是errno不会设置.
3. _SC_CLK_TCK的返回值是每秒钟的始终滴答数,是times的返回值.
对于pathconf和fpathconf函数的pathname参数和fd是有些限制存在:
1. _PC_MAX_CANON和_PC_MAX_INPUT引用的文件必须是终端文件.
2. _PC_LINK_MAX和_PC_TIMESTAMP_RESOLUTION引用的文件可以是文件也可以是目录. 如果是目录,则返回值用于目录本身而不用于目录内的文件名项.
3. _PC_FILESIZBITS和_PC_NAME_MAX引用文件必须是目录,返回值用于目录中文件.
4. _PC_PATH_MAX引用文件必须是目录, 当所指定的目录是工作目录,返回的值是相对路径的最大文件名长度.
5. _PC_PIPE_BUF引用的文件必须是pipe, FIFO, 或者目录. 在pipe和FIFO下, 返回值是对所引用的pipe和FIFO的限制. 在目录下,返回值是对该目录下创建的任意FIFO的限制.
6. _PC_SYMLINK_MAX所引用的文件必须是目录. 返回值是对该目录下创建的符号链接的最大长度的限制.
在这里我们awk写了一个有趣的程序用于打印出pathconf和sysconf符号在系统上支持情况. 具体有想了解awk的同学可以参照awk通俗易懂的讲解:

#! /usr/bin/awk -f
BEGIN   {
    printf("#include \"apue.h\"\n")
    printf("#include <errno.h>\n")
    printf("#include <limits.h>\n")
    printf("\n")
    printf("static void pr_sysconf(char *, int);\n")
    printf("static void pr_pathconf(char *, char *, int);\n")
    printf("\n")
    printf("int\n")
    printf("main (int argc, char **argv)\n")
    printf("{\n")
    printf("\tif(argc != 2)\n")
    printf("\t\terr_quit(\"usage: a.out <dirname>\");\n\n")
    FS="\t+"
    while(getline < "sysconf.sym" > 0){
        printf("#ifdef %s\n", $1)
        printf("\tprintf(\"%s defined to be %%ld\\n\", 
            (long)%s+0);\n,$1, $1")
        printf("#else\n")
        printf("\tprintf("\"not symbol for %s\\n\");\n", $1)
        printf("#endif\n")
        printf("#ifdef %s\n", $2)
        printf("\tpr_sysconf(\"%s = \", %s); \n", $1, $2)
        printf("#else\n")
        printf("\tprintf(\"no symbol for %s\\n\");\n", $2)
        printf("#endif\n")
    }
    close("sysconf.sym")
    while (getline <"pathconf.sym" > 0) {
        printf("#ifdef %s\n", $1)
        printf("\tprintf(\"%s defined to be %%ld\\n\",
            (long)%s+0);\n",$1, $1)
        printf("#else\n")
        printf("\tprintf(\"no symbol for %s\\n\");\n", $1)
        printf("#endif\n")
        printf("#ifdef %s\n", $2)
        printf("\tpr_pathconf(\"%s =\", argv[1], %s);\n", 
            $1, $2)
        printf("#else\n")
        printf("\tprintf(\"no symbol for %s\\n\");\n", $2)
        printf("#endif\n")
    }
    close("pathconf.sym")
    exit
}
END {
    printf("\texit(0);\n")
    printf("}\n\n")
    printf("static void\n")
    printf("pr_sysconf(char *mesg, int name)\n")
    printf("{\n")
    printf("\tlong  val;\n\n")
    printf("\tfputs(mesg, stdout);\n")
    printf("\terrno = 0;\n")
    printf("\tif ((val = sysconf(name)) < 0) {\n")
    printf("\t\tif (errno != 0) {\n")
    printf("\t\t\tif (errno == EINVAL)\n")
    printf("\t\t\t\tfputs(\" (not supported)\\n\", stdout);
        \n")
    printf("\t\t\telse\n")
    printf("\t\t\t\terr_sys(\"sysconf error\");\n")
    printf("\t\t} else {\n")
    printf("\t\t\tfputs(\" (no limit)\\n\", stdout);\n")
    printf("\t\t}\n")
    printf("\t} else {\n")
    printf("\t\tprintf(\" %%ld\\n\", val);\n")
    printf("\t}\n")
    printf("}\n\n")
    printf("static void\n")
    printf("pr_pathconf(char *mesg, char *path, int name)\n")
    printf("{\n")
    printf("\tlong  val;\n")
    printf("\n")
    printf("\tfputs(mesg, stdout);\n")
    printf("\terrno = 0;\n")
    printf("\tif ((val = pathconf(path, name)) < 0) {\n")
    printf("\t\tif (errno != 0) {\n")
    printf("\t\t\tif (errno == EINVAL)\n")
    printf("\t\t\t\tfputs(\" (not supported)\\n\", stdout);
        \n")
    printf("\t\t\telse\n")

这个程序读入两个文件pathconf.sym和sysconf.sym,在这两个文件中包含limit名字和symbol,是用table键进行分割的. 在这里通过测试我们总结出了四个系统上的这些参数. no symbol是说这个系统上不存在_SC和_PC符号. unsupported是说符号在系统上定义但是并没有被sysconf和pathconf认定. no limit并不是表示无限,只是说具体值不确定.
这里写图片描述
接下来我们针对在运行时的限制之不确定的值进行举例:
1. 路径名
许多的程序中都要对路径名分配内存,内存在编译时分配, 这是就出现各种各样的magic number,但是鲜有正确的. 比如有256, 512, 1024或者标准IO的BUFSIZ. 在4.3BSD中有MAXPATHLEN在头文件sys/param.h中,这就是个正确的值但是许多4.3BSD的应用中用的还是错的.POSIX试着用PATH_MAX来解决这个问题,但是如果这个值是不确定的,那么也是没有什么卵用的. 并且在POSIX.1-2001或者之前版本中对字符串最后到底有没有给定null的空间位置没有明确的指定. 如果PATH_MAX在limits.h中有设定,那么我们就不考虑了,但是如果没有设定,我们就得考虑清楚是不是不定的,如果是不定的我们应该怎么分配. 鉴于这些考虑因素,我们进行了方法的重写来为pathname分配内存.

#include <errno.h>
#include <errno.h>
#include <limits.h>


#ifdef PATH_MAX
static long pathmax = PATH_MAX;
#else
static long pathmax = 0;
#endif

static long posix_version = 0;
static long xsi_version = 0;

/*If PATH_MAX is indeterminate, This value no guarantee*/
#define PATH_MAX_GUESS 1024

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

    if(posix_version == 0)
    {
        posix_version = sysconf(_SC_VERSION);
    }
    if(xsi_version == 0)
    {
        xsi_version = sysconf(_SC_XOPEN_VERSION);
    }
    if(pathmax == 0)
    {
        errno = 0;//clear
        if((pathmax = pathconf("/", _PC_PATH_MAX) < 0))
        {
            /*
            If the return value less than the 0
            and the errno is set, means that the
            arguments of _PC_PATH_MAX is wrong or
            not included.
            if the return value less than the 0
            and the errno not set, it means indeterminated
            value.
            */
            if(errno == 0)
            {
                pathmax = PATH_MAX_GUESS;
            }
            else{
                err_sys("pathconf error for _PC_PATH_MAX");
            }
        }
        else{
            /*add one since it's relative to root*/
            pathmax++;
        }
    }

    if((posix_version < 200112L) && (xsi_version < 4))
    {
        size = pathmax + 1;
    }
    else{
        size = pathmax;
    }
    if((ptr = malloc(size)) == NULL)
    {
        err_sys("malloc error for pathname");
    }
    if(sizep != NULL)
    {
        *sizep = size;
    }
    return(ptr);
}
  1. 文件打开最大数量
    在守护进程中我们有着一段这样的代码(由于守护进程的所有IO都不能指向文件或终端,所以要关闭所有打开的文件描述符). 一些程序就是用在sys/param.h中的NOFILE来获取到打开文件描述符的最大值然后用for循环将他们遍历都关了. 还有些程序使用stdio.h中_NFILE提供上限,设置有些程序直接指定20. 但是这些都是不合适的方式. 我们的POSIX大哥又出动了,我们要用POSIX.1中定义的OPEN_MAX来决定合适的值,但是如果这个值又是未决定的,那么循环就永远不会执行,因为sysconf函数会返回-1(for(i = 0; i < sysconf(_SC_OPEN_MAX); i++));所以我们也进行了重写:
#include <errno.h>
#include "apue.h"
#include <limits.h>

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

#define OPEN_MAX_GUESS 256

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

尽管在我们的bash中有工具像ulimit进行打开文件个数大小等的设置,但是当我们把打开数量设置到无穷大的时候,sysconf会将LONG_MAX换到OPEN_MAX. 这样关闭文件描述符都是个浪费资源的活, 更关键的是我们根本没用到那么多. 在遵循Single Unix的XSI扩展的标准中, 会有一个方法是getrlimit(2)能够得到最大文件描述打开数在一个进程中. 这样我们就能直接进行判断的.
在POSIX中定义OPEN_MAX是运行时常量,也就是在在进程中的生命周期中是不变的,但是在支持XSI的系统中,可以通过setrlimit(2)函数来进行修改,在Cshell中有limit进行修改,在Bash中, Debian Almquist, 和KShell中有ulimit进行修改. 所以说基本上我们可以改变sysconf的这个值在任何时候.

2.6 可选择的

同样的,我们在写可移植的程序的时候, 有可能会依赖这些可选的功能. 所以POSIX.1同样的对其进行了三种方式的限制,与之前不同的就是如果定义了那么就应该在unistd.h中定义了.
这个是sysconf所支持的:
这里写图片描述
这个是pathconf和fpathconf支持的:
这里写图片描述

2.7 功能测试宏

我们也可以发现在头问价中定义了大量的POSIX.1和XSI符号. 但是当我们想要加入自己的定义. 如果在编译一个程序的时候, 希望它只与POSIX的定义相关, 并且不与其他的定义冲突的话, 我们就需要定义常量_POSIX_C_SOURCE. 所有的POSIX.1头都是用这个常量排除其他实现定义的冲突的. _POSIX_C_SOURCE和_XOPEN_SOURCE叫做功能测试宏. 所有的功能测试红都是以下划线开头的.当使用的时候通常在cc命令中定义:
cc -D_POSIX_C_SOURCE=200809L file.c
这使得C程序在包含头文件之前定义了功能测试宏, 如果我们只想使用POSIX.1定义,那么我们也可以将源文件的第一行:
#define _POSIX_C_SOURCE 200809L
为使SUSv4中的XSI在程序中使用,我们就需要将_XOPEN_SOURCE设置为700,这样就与上面同样的功能了, SUS定义了c99作为C编译环境的接口:
c99 -D_XOPEN_SOURCE=700 file.c -o file
可以使用-std=c99在gcc的c编译器中实现ISO C
gcc -D_XOPEN_SOURCE=700 -std=c99 file.c -o file

2.7 基本系统数据类型

纵观历史,许多C的数据类型和Unix仅仅关联. 比如说major和minor设备号,以前就是存储在16bit的短整型中的, 8位minor和8位major. 但是许多大型的系统都需要更多的256. 所以不同的技巧应用而生. 头文件sys/types.h中定义了实现独立的数据类型叫做基本系统数据类型. 这些也会在其他的头文件中有定义. 这些定义的方式是C的typedef.
这里写图片描述
用了这些数据类型我们就不需要考虑系统不同而导致数据类型不同了.

2.8 标准间的不同

总的来说这些不同的标准协调的很不错,因为SUS基本规则和POSIX.1是同一个规则,所以我们不讨论他俩,我们主要就是讨论ISO C和POSIX.1的差别. 一旦差别出现,POSIX.1服从ISO C. 但是还是有些不同的:
ISO C中定义了clock函数返回进程中的CPU时间. 返回的值是clock_t, 但是ISO C并没有指定它的单位. 为了将这个值转换为秒,我们将其除以time.h中定义的CLOCKS_PER_SEC. 但是在POSIX.1中定义了times函数, 返回了CPU时间和时钟时间. 返回的也是clock_t. ISO C和POSIX.1用同一种类型保存对时间的测量,但是定义了不同的单位. 这种差别可以再Solaris中看到. clock返回了微秒, 然而sysconf为每秒滴答数返回的是100. 因此在使用clock_t的时候我们一定要小心.


联系方式: reyren179@gmail.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值