当前位置:嗨网首页>书籍在线阅读

11-大文件IO

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

5.10 大文件I/O

通常将存放文件偏移量的数据类型 off_t 实现为一个有符号的长整型。(之所以采用有符号数据类型,是要以−1来表示错误情况。)在32位体系架构中(比如x86-32),这将文件大小置于231−1个字节(即2GB)的限制之下。

然而,磁盘驱动器的容量早已超出这一限制,因此32位UNIX实现有处理超过2GB大小文件的需求,这也在情理之中。由于问题较为普遍,UNIX厂商联盟在大型文件峰会(Large File Summit)上就此进行了协商,并针对必需的大文件访问功能,形成了对SUSv2规范的扩展。本节将概述LFS的增强特性。(完整的LFS规范定稿于1996年,可通过http://opengroup.org/platform/lfs.html访问。)

始于内核版本2.4,32位Linux系统开始提供对LFS的支持(glibc版本必须为2.2或更高)。另一个前提是,相应的文件系统也必须支持大文件操作。大多数“原生”Linux文件系统提供了LFS支持,但一些“非原生”文件系统则未提供该功能(微软的 VFAT 和NFSv2系统是其中较为知名的范例,无论系统是否启用了LFS扩展功能,2GB的文件大小限制都是硬杠杠)。

由于64位系统架构(例如,Alpha、IA-64)的长整型类型长度为64位,故而LFS增强特性所要突破的限制对其而言并不是问题。然而,即便在64位系统中,一些“原生”Linux文件系统的实现细节还是将文件大小的理论值默认为不会超过263−1个字节。在大多数情况下,此限额远远超出了目前的磁盘容量,故而这一对文件大小的限制并无实际意义。

应用程序可使用如下两种方式之一以获得LFS功能。

  • 使用支持大文件操作的备选API。该API由LFS设计,意在作为SUS规范的“过渡型扩展”。因此,尽管大部分系统都支持这一API,但这对于符合SUSv2或SUSv3规范的系统其实并非必须。这一方法现已过时。
  • 在编译应用程序时,将宏_FILE_OFFSET_BITS的值定义为64。这一方法更为可取,因为符合SUS规范的应用程序无需修改任何源码即可获得LFS功能。

过渡型LFS API

要使用过渡型的LFS API,必须在编译程序时定义_LARGEFILE64_SOURCE功能测试宏,该定义可以通过命令行指定,也可以定义于源文件中包含所有头文件之前的位置。该API所属函数具有处理64位文件大小和文件偏移量的能力。这些函数与其32位版本命名相同,只是尾部缀以64以示区别。其中包括:fopen64()、open64()、lseek64()、truncate64()、stat64()、mmap64()和setrlimit64()。(针对这些函数的32位版本,本书前面已然讨论了一部分,还有一些将在后续章节中描述。)

要访问大文件,可以使用这些函数的64位版本。例如,打开大文件的编码示例如下:

95.png

调用open64(),相当于在调用open()时指定O_LARGEFILE标志。若调用open()时未指定此标志,且欲打开的文件大小大于2GB,那么调用将返回错误。

另外,除去上述提及的函数之外,过渡型LFS API还增加了一些新的数据类型,如下所示。

  • struct stat64:类似于stat结构(参见15.1节),支持大文件尺寸。
  • off64_t:64位类型,用于表示文件偏移量。

如程序清单5-3所示,除去使用了该API中的其他64位函数之外,lseek64()就用到了数据类型off64_t。该程序接受两个命令行参数:欲打开的文件名称和给定的文件偏移量(整型)值。程序首先打开指定的文件,然后检索至给定的文件偏移量处,随即写入一串字符。如下所示的shell会话中,程序检索到一个超大的文件偏移量处(超过10GB),再写入一些字节:

96.png 程序清单5-3:访问大文件

_FILE_OFFSET_BITS宏

要获取LFS功能,推荐的作法是:在编译程序时,将宏_FILE_OFFSET_BITS的值定义为64。做法之一是利用C语言编译器的命令行选项:

99.png 另外一种方法,是在C语言的源文件中,在包含所有头文件之前添加如下宏定义:

100.png 所有相关的32位函数和数据类型将自动转换为64位版本。因而,例如,实际会将open()转换为open64(),数据类型off_t的长度也将转而定义为64位。换言之,无需对源码进行任何修改,只要对已有程序进行重新编译,就能够实现大文件操作。

显然,使用宏_FILE_OFFSET_BITS要比采用过渡型的LFS API更为简单,但这也要求应用程序的代码编写必须规范(例如,声明用于放置文件偏移量的变量,应正确地使用off_t,而不能使用“原生”的C语言整型)。

LFS 规范对于支持_FILE_OFFSET_BITS 宏未作硬性规定,仅仅提及将该宏作为指定数据类型 off_t 大小的可选方案。一些 UNIX 实现使用不同的特性测试宏来获取此功能。

若试图使用32位函数访问大文件(即在编译程序时,未将宏_FILE_OFFSET_BITS的值设置为64),调用可能会返回EOVERFLOW错误。例如,为获取大小超过2G文件的信息,若使用stat的32位版本时就会遇到这一错误。

向printf()调用传递off_t值

LFS扩展功能没有解决的问题之一是,如何向printf()调用传递off_t值。3.6.2节曾特别指出,对于预定义的系统数据类型(诸如pid_t、uid_t),展示其值的可移植方法是将该值强制转换为long型,并在printf()中使用限定符%ld。然而,一旦使用了LFS扩展功能,%ld将不足以处理off_t数据类型,因为对该数据类型的定义可能会超出long类型的范围,一般为long long类型。据此,若要显示off_t类型的值,则先要将其强制转换为long long类型,然后使用printf()函数的%lld限定符显示,如下所示:

101.png 在处理stat结构所使用的blkcnt_t 数据类型时,也应予以类似关注(参见15.1节的描述)。

如需在独立的编译模块之间传递off_t或stat类型的参数值,则需确保在所有模块中,这些数据类型的大小相同(即编译这些模块时,要么将宏_FILE_OFFSET_BITS的值都定义为64,要么都不做定义)。