分类:
2008-04-07 18:37:59
过去,运行于 Solaris 操作系统上的 32 位应用程序只能通过 C 库中的标准 I/O 使用最多 255 个文件描述符。Solaris 10 及更高版本操作系统中的 FILE
扩展工具允许行为良好的 32 位应用程序通过标准的 I/O 功能使用任何有效的文件描述符。
行为良好的应用程序指满足以下所有三个要求的应用程序:
FILE
结构中任何由与任何标准 I/O 流相关的 FILE
指针指向的成员字段。
本文详细介绍了 FILE
扩展工具所引入的运行时和编程解决方案。以下讨论仅针对 32 位应用程序,因为 64 位应用程序不受 256 个文件描述符限制的影响。
在网上快速搜索 将会产生无数个 Solaris 操作系统限制 Stdio 最多只能使用 256 个指示符的结果。 解释了该问题:32 位标准输入输出例程应该支持大于 255 个文件指示符。错误报告链接到少数其他与标准输入输出的 256 个文件指示符限制相关的问题。
Solaris 操作系统上有此限制,是因为 unsigned char
用于存储与标准输入输出流相关的文件指示符的值。查看您可以在 Solaris 操作系统上的头部 /usr/include/stdio_impl.h
中发现的 FILE
结构的定义(该 Solaris 操作系统没有本文讨论的 解决方案):
struct __FILE_TAG |
名称 __FILE_TAG
只是 FILE
的别名。请参阅 。
成员字段 _file
保存了文件描述符,该文件描述符被声明为 unsigned char
类型。一个 unsigned char
在内存中占 8 位。因此 _file
可以保存的最大值为 2^8 = 256。换句话说,_file
对每个 32 位进程限制访问 256 个文件描述符。该限制被清楚地记录在 的手册页。
Sun 过去没有从 8 位 unsigned char
转变为 16 位 int
来适应更多的文件描述符。这样做会破坏与早期 Solaris 操作系统版本的双边兼容性,因为这会更改 FILE
结构的大??/p>
从2007年7月发布的Solaris 10开始,Sun将会以扩展的 FILE
工具的形式提供运行时和编程解决方案,以减轻 stdio
(标准输入输出)的256个文件描述符限制。运行 版本或任何 将会拥有这些功能。本文的 补丁和缺陷 一节包含了在任何运行从 Solaris 10 3/05 到 Solaris 10 11/06 版本的系统上安装扩展的 FILE
工具的说明。
像这一节的标题暗示的一样,运行时解决方案不需要更改任何源代码或重新编译对象,以克 服 C 库函数的 256 个文件描述符限制。但是,现有 32 位应用程序的默认行为将不会更改,除非您明确启动扩展的 FILE
工具。启用了此功能的应用程序将可以关联任何具有标准 I/O -- 或 stdio
-- 流的有效文件描述符。任何 3 以内的值以及由 ulimit -n
从用于启动应用程序的 shell 返回的值都是有效的文件描述符。文件描述符 0、1、和 2 被保留用作默认 stdin
、stdout
和 stderr
I/O流。
您可以增加 shell 中每个流程的最大文件描述符数目,从默认的 256 到任何少于或等于该命令的返回值的值均可。
echo 'rlim_fd_max/D' | mdb -k | awk '{ print $2 }' |
要调整 shell 中的文件描述符限制,请在 sh/ksh/bash
运行 ulimit -n
或者在 csh
运行 limit descriptors
,这里 max_file_descriptors
为您想要的文件描述符的最大数目。
每个进程任何时候可以打开的文件的数目的默认限制为 65,536。您可以使用系统可调节的参数 来调节此限制。尽管可以通过调节 rlim_fd_max
参数来打开大量文件,但是当打开成百上千的文件时,虚拟内存空间会成为32位进程的限制。当进程达到虚拟内存的限制时,stdio
会调用失败,出现 Not enough space
(没有足够空间)错误。
在运行 32 位应用程序之前,通过执行以下操作启用扩展的 FILE
工具:
FILE
工具,/usr/lib/extendedFILE.so.1
。 注意:extendedFILE.so.1
不是库,而是扩展的 FILE
工具的启用程序。
这里是如何从 ksh
启用扩展的 FILE
工具的方法:
% ulimit -n |
以下示例显示了启用或未启用扩展的 FILE
工具的简单 32 位进程的行为。用于测试的是一个简单的 C 程序,尝试使用 fopen()
接口打开 65,536 个文件。
% cat fopentestcase.c |
使用 shell 中文件描述符的默认最大数目重现失败:
% cc -o fopentestcase fopentestcase.c |
提高文件描述符限制,启用扩展的 FILE
工具,并运行再次运行测试,查看运行时解决方案的运行。
% ulimit -n 5000 |
看到缺少一个文件描述符 -- 不包括 stdin
、stdout
和 stderr
各自的 0、1 和 2 -- 在上述示例中。当启用扩展的 FILE
工具时,文件描述符 196 默认将变得不可分配,以最小化无记录的数据损坏。有关更多信息,请参阅下一节 环境变量。
这里是确认此陈述的 pfiles
输出:
% pfiles `pgrep fopentestcase` | egrep "log|:" |
两个环境变量控制扩展的 FILE
工具的行为:_STDIO_BADFD
和_STDIO_BADFD_SIGNAL
。
如您所知,在扩展的 FILE
工具出现之前,在 Solaris 操作系统上构建的对象代码将不会期望任何不符合 8 位 unsigned char
的文件描述符,并且将无法理解如何处理扩展的FILE
指针。因为这些原因,范围被限制在3 至 255,所以当实际的描述符其实是扩展的文件描述符时(即,任何大于255的值),通过重新引用 FILE -> _file
而不是 fileno(3C)
函数查询文件描述符值的代码将会收到不能分配或错误的文件描述符。
FILE
工具的使用期间检测到某个意外事件发生,则该变量使指定的信号被发送到应用程序。默认信号为 SIGABRT
。 如果应用程序执行以下操作,则不要启用扩展的 FILE
工具:
FILE
结构的 _file
成员。
fileno()
宏(已从 Solaris 2.7 版的头部去除)而不是 fileno(FILE)
函数来获取底层文件描述符的值。 当启用此功能后,大于 255 的文件描述符将被存储在一个应用程序未知的备用位置,而环境变量 _STDIO_BADFD
保存的不可分配或错误文件描述符将被存储在 FILE -> _file
成员字段中。当实际的底层文件描述符大于 255 时,应用程序不恰当地访问 FILE -> _file
成员字段将会产生不可分配的错误文件描述符,因此导致未记录的数据错误。
如果进程截断了由 fileno(FILE)
函数返回的值,也会发生数据损坏。例如,如果 fileno()
函数返回的 16 位或 32 位的 int
值被存储在一个 8 位的 unsigned char
变量中,则会进行截断。访问被截断的文件描述符可能会产生错误。
以下运行时的错误消息就清楚显示了应用程序修改 stdio
中的 FILE
结构的内部文件描述符成员字段。
Application violated extended FILE safety mechanism. |
当您收到这样的错误消息时,请停止对应用程序使用扩展的 FILE
工具。如果可能,通过将所有对 FILE -> _file
的引用替换为对 fileno(FILE)
的调用来修复资源。忽略此运行时错误将会导致数据损坏。
以下的小示例展示了两个环境变量 _STDIO_BADFD
和 _STDIO_BADFD_SIGNAL
的用法,并其显示了当代码违反FILE
安全机制时后续程序崩溃。
在任何运行于没有扩展的 FILE
解决方案的 Solaris 操作系统上编译以下代码并构建一个库。注意:运行 Solaris 10 3/05 到 Solaris 10 11/06 版本的未打补丁的系统不包含扩展的 FILE
解决方案。但是,通过应用最新的 kernel 和 libc
补丁,在这些系统上安装扩展的 FILE
工具是可以的。相关说明请参阅本文的 补丁和缺陷 一节。
% cat thirdpartysrc.c |
在任何运行具有扩展的 FILE
解决方案的 Solaris 操作系统的系统上,通过将对象代码和上一步中创建的库链接起来编译以下代码并建立一个可执行文件:
% cat enableextfile.c |
将每个进程最大文件描述符限制提高为任何大于 255 的数字,设置环境变量 _STDIO_BADFD
和 _STDIO_BADFD_SIGNAL
,通过预载 /usr/lib/extendedFILE.so.1
启动扩展的 FILE
工具,最后运行可执行文件:
% ulimit -n |
如果应用程序没有显示任何以前提到的参数,则它可以利用此运行时解决方案而不管其年纪 -- 即,即使在 Solaris 7 或更早版本上建立的应用程序也可能继续正常工作。
有关更多示例,请参阅 的手册页面。
此节用于新应用程序以及开发人员可以轻易修改的应用程序。
两个编程接口将允许访问大于 256 的文件描述符 FILE
池,提供的最大文件描述资源限制已经被提高了。注意默认的最大文件描述符限制仍为 256。
fopen(3C)
、fdopen(3C)
和popen(3C)
为了降低修改现有资源以利用扩展的 FILE
功能的工作量,stdio
打开调用(比如 fopen(3C)
、fdopen(3C)
和 popen(3C)
)的现有模式字符串增加了新标记:F
。例如:
FILE *fptr = fopen("dummy.txt", "rF"); |
如果该模式字符串的最后一个字符为 F
,则 32 位应用程序将被允许将流和通过值大于 255 的文件描述符访问的文件关联起来。在 64 位应用程序的情况下,应用程序将不加记录地忽略模式字符串中的字符F
。除了这个小的增强,stdio
打开调用的现有语法没有更改。
stdio
打开调用的模式字符串中的F
仅用于不执行以下操作的代码:
FILE
结构的成员字段。
FILE
指针给调用程序 如果应用程序展示了任何以前提到的模式,则字符 F
一定不能附加到模式字符串中以启动扩展的 FILE
功能。当模式的最后一个字符是F
时,如果 32 位的应用程序直接使用 FILE
结构中的成员字段,则会出现数据损坏。
FILE
指针被返回到用户未知的二进制代码(即意外代码)时也可能出现数据损坏,因为调用程序可能不知道怎么处理它。该接口没有对误操作 FILE
指针提供任何保护。如果 FILE
指针必须返回到任何意外代码,请考虑在代码中以高水平使用 启用扩展的_FILE_stdio(3C)
。
要用法实例,更改下行后重建测试:
fds[i] = fopen(filename, "w"); |
更改为:
fds[i] = fopen(filename, "wF"); |
现在提高 shell 中的文件描述符限制,然后再次运行测试查看结果:
% cc -o fopentestcaseF fopentestcase.c |
记下对任何特定库缺少的连接以使其工作。所有 stdio
例程仍是 libc
的一部分。
有关详细信息,请参阅手册页 、和。
enable_extended_FILE_stdio(3C)
如果 FILE
指针不是被限制在单个功能环境内,您可以使用新的编程接口 enable_extended_FILE_stdio(3C)
来启用扩展的 FILE
功能。此接口通过对行为未知的软件提供一些保护机制来最小化数据损坏,比如没有源代码的第三方库。例如,通过使用此接口,当应用程序不适当地取消对 FILE -> _file
的引用时,用户可以选择任何信号在运行时发送到进程。
该新接口在 /usr/include/stdio_ext.h
头部中定义如下:
int enable_extended_FILE_stdio(int, int); |
第一个参数是整数,指定了应用程序想要选择作为不可分配的文件描述符的文件描述符(从 3 至 255)。相反,将其设置为-1将会要求 enable_extended_FILE_stdio(3C)
选择一个合理的不可分配的文件描述符。这等同于当为扩展的 FILE
启用 runtime solution 时设置环境变量 _STDIO_BADFD
。
第二个参数是整数,指定当不可分配的文件描述符被用作所有系统调用(除了 close(2)
或 closefrom(3C)
)的文件描述符时,要发送给进程的信号。一些应用程序可能尝试关闭它们没有打开的文件描述符。该例外可防止应用程序因为这样无害的调用崩溃。
如果忽略 -1,则默认的信号 SIGABRT
将被发送到进程。值 0 会通过禁止发送信号忽略任何 FILE -> _file
取消引用。否则,指定的信号将被发送到进程。有关 Solaris 操作系统上完整的信号了列表,请参阅 手册页。这等同于当为扩展的FILE
启用运行时解决方案时设置环境变量 _STDIO_BADFD_SIGNAL
。
enable_extended_FILE_stdio(3C)
功能只在 32 位编译环境下可用。
要使扩展的 FILE
工具发挥作用,将进程的文件描述符默认最大限制从 256 提高到任何小于或等于进程在任何时间可以打开的文件数的硬性限制。有关详细信息,请查看 kernel 可调节的 。您这么做,您可以从 shell 中使用 ulimit/limit
命令,或者通过在 /usr/include/sys/resource.h
中定义的函数 getrlimit(2)/setrlimit(2)
才有编程来进行。
接下来的小编程实例展示了三点:
getrlimit(2)/setrlimit(2)
接口设置文件描述符限制。
FILE
工具的用法
FILE -> _file
的值滥用底层文件描述符时,应用程序会崩溃 编译以下代码,并通过链接对象代码和库 libthirdparty.so
(在本文的 运行时解决方案一节中创建)建立一个可执行文件。
% cat enableextfilestdio.c |
有关更多信息,请参阅 的手册页面。
_file
变成了 _magic
为了确保使用扩展的 FILE
机制的安全性,FILE
结构中的 _file
成员已经在 中被有意地重命名为 _magic
(正在开发的 Solaris 操作系统的下一个主要客户版本的每月快照),并且在 源代码基础中也是如此(建立39后)。这个更改将破坏包含任何对FILE -> _file
的引用的源代码的兼容性。注意:此警告不适用于 Solaris 10 操作系统,包括更新版本。
以下 diff
输出显示了在 FILE
结构的定义中引入以适应扩展的 FILE
工具的更改:
- unsigned char _file; /* UNIX system file descriptor */ |
因此,在运行 或任何 的系统上以及在 Solaris 10 以后版本上(当其可用时)编译具有对 FILE -> _file
的引用的代码会失败,错误代码如下。
"filename.c", line xx: undefined struct/union member: _file |
正式名称为 _file
的字段中的值可能不再包含 FILE
的文件描述符。如果该代码只是读取 _file
的值,使用更合适的 fileno(FILE)
函数代替所有这些引用。请参阅 的手册页。不应该再向 _file
指定新值。
当启用扩展的 FILE
工具后,访问小于或等于 255 的文件描述符时对性能没有影响。但是在访问大于或等于 256 的文件描述符时,由于在备用位置存储和检索文件描述符,性能会有轻微降低。
如果您的系统运行的是 Solaris 10 操作系统的任何现有版本 -- 即,Solaris 10 3/05 到 Solaris 10 11/06 -- 您可以在系统上使用以下三个用于硬件平台的补丁(或更高版本)安装扩展的 FILE
工具:
SPARC 平台:
x86/x64 平台:
如果应用程序代码与 链接,则 Sun Studio 软件编译器套件随附的 C++ 标准库,使用 -library=stlport4
编译器选项,确保在 SPARC 平台上为 Sun Studio 11 软件打上 121017-07 或更新补丁,在 x86/x64 平台上打上 121018-07 或更新补丁,以便利用扩展的 FILE
。
以上文章转自于 : http://developers.sun.com.cn/