跳转至主要内容

VOS 流文件的最大大小限制在约 2 GB。这是因为诸如 s$seq_position、s$get_port_status、s$lock_region 等接口的定义使用了带符号的 32 位变量来表示文件中的位置,而 2**31 是该变量所能表示的最大值。在流文件中,位置表示的是字节偏移量,而非其他 VOS 文件组织中使用的记录编号。

自17.2版本起,64位流文件已正式支持,其增长空间仅受VOS文件系统限制(单个文件最大容量为512 GB)。除扩展增长潜力外,此类文件还具备其他优势,尤其在访问二进制数据时更为显著。与常规流文件不同,包含全二进制零的块在磁盘上不占用空间,读取此类数据时无需进行磁盘访问。

符合 POSIX 标准的应用程序无需修改源代码,即可轻松实现对大文件的处理:在运行 17.2 及以上版本模块的磁盘上创建的新文件将是 64 位文件(通常称为 stream64 文件),而在 17.2 之前版本的模块上创建的新文件则为普通流文件。 使用 VOS s$ 接口或 VOS 语言 I/O 的应用程序被称为原生应用程序;而使用流文件的应用程序,只要它们不使用字节定位操作(定位到 BOF 或 EOF 不被视为字节定位),或者文件本身支持 stream64 格式,便能够访问 stream64 文件。不过,若要创建 stream64 文件,则需要对这些应用程序进行修改。

以下是一些信息,如果您计划使用 64 位流文件,这些信息可能会对您有所帮助。内容安排如下:

现有应用程序与兼容性
– 现有 Posix 应用程序
– 现有本机应用程序
– 兼容性
– 开源产品
稀疏分配
文件转换
物理特性
– 扩展区
– 扩展区如何影响稀疏分配
– 灵活扩展区
工具
– 在模块上定位 64 位流文件
– 块比较
– 比较稀疏文件

现有应用程序与兼容性
– 现有 POSIX 应用程序

从 17.2 版本开始,Posix 应用程序可以构建为支持大文件处理,从而使其能够访问 64 位文件,并在目标位于运行 17.2 或更高版本的磁盘上时创建此类文件。 如果应用程序符合 POSIX 标准(例如,在需要时使用 off_t 类型而非 int 类型进行编码),则无需修改源代码;只需按照《OpenVOS POSIX.1 参考手册》(R502)中“将现有应用程序移植到 64 位流文件环境”部分所述进行构建即可。 如果要创建的文件位于运行 17.2 之前版本的模块上的磁盘中,则会创建一个普通流文件,此时一切正常,只有在尝试将文件扩展至 2 GB 以上时才会发生错误。因此,让您的 Posix 应用程序支持大文件在互操作性方面无需任何额外成本。自 17.2 版本起,大多数受 VOS 支持的开源产品均可正确生成并处理 64 位流文件。

符合 POSIX 标准的应用程序无需修改源代码,即可轻松实现对大文件的处理:在运行 17.2 及以上版本模块的磁盘上创建的新文件将作为 64 位流文件(通常称为 stream64 文件),而在 17.2 之前版本的模块上创建的新文件则为普通流文件。 使用 VOS s$ 接口或 VOS 语言 I/O 的应用程序被称为原生应用程序;而使用流文件的应用程序,只要满足以下任一条件,即可访问 stream64 文件:不使用字节定位操作(定位到文件开头或文件结尾不被视为字节定位),或者文件大小小于 2 GB。

– 现有的原生应用程序

许多原生应用程序只需将create_file命令(或s$create_file)的调用方式修改为组织类型为STREAM64而非STREAM。若应用程序使用定位操作且文件增长至>2GB,则s$seq_position将引发错误:为支持更大文件,需将s$seq_position调用替换为s$seq_position_x。 同样,使用 _x 接口不会影响应用程序引用位于可能不支持 _x 接口的模块上的普通流文件的能力,只要定位参数在支持的范围内即可。因此,进行此更改既能访问任何类型的流文件,又不会对互操作性造成任何影响。

现有应用程序若仅使用不传递字节位置信息的接口(如 s$seq_read、s$seq_write、s$seq_position BOF/EOF),则无需修改即可处理 stream64 文件。许多应用程序都是为处理 ASCII 文件而编写的,无论这些文件是顺序文件还是流文件,因此无论文件大小如何,处理 64 位流文件时也无需进行任何修改。

64 位流文件不支持索引,因此带索引的流文件无法转换为 stream64。由于现有索引实现的限制,带索引的流文件大小无法超过 2 GB。

– 兼容性

由于 64 位流文件具有诸多优势,因此在不需要索引的情况下,应尽可能采用它们。

只要文件大小不超过 2 GB,就可以将其复制或移动到运行旧版本 VOS 的模块中,在那里它将显示为一个普通的流文件。只要文件未被“限制”,您甚至可以将包含 stream64 文件的磁盘移动到旧版本的 VOS 上。 当文件大小超过2GB或采用稀疏分配时即被限制;若将包含此类文件的磁盘移至17.2之前的VOS系统,这些文件将无法打开(若计划执行此类迁移,可使用专用工具识别受限文件)。 不过,运行旧版 VOS 的应用程序仍可通过网络打开、读取和写入任何类型的 stream64 文件;发生的任何错误都与应用程序在 17.2 及以上版本上运行时相同(例如,当文件超过 2 GB 时,使用 s$seq_position 进行字节定位)。

– 开源产品

VOS支持的大多数开源软件已实现对大文件的处理能力,例如FTP、SFTP、Samba等。这带来了一些影响。 从 17.2 版本开始,您现在可以通过 FTP 将流文件传输到 VOS,且无需担心文件过大,因为 FTP 守护进程会自动生成一个 stream64 文件。FTP 会写入所有字节,因此生成的文件不会是稀疏文件(参见下一节),即使其中大部分内容是二进制零。只需复制传输后的文件,即可大幅减少其占用的磁盘空间。 如果您计划为通过 FTP 传输的文件添加索引,必须先将其转换为普通流文件(当然,其长度不得超过 2 GB)。

稀疏分配

对于 stream64 文件,包含全二进制零的块并不总是被分配,因此不需要占用磁盘空间。可以使用 gnu 的“truncate”命令(位于 >system>gnu_library>bin 目录下)来截断或扩展流文件的大小。在 18.0 版本中,VOS 提供了一个名为 reset_eof 的类似命令。 EOF 之后的数据行为未定义,但当 EOF 被扩展时,从当前 EOF 位置开始的文件内容始终会被设置为二进制零。

假设有一个普通的流文件和一个 stream64 文件:

create_file stm -organization stream
create_file s64 -organization stream64

假设每个文件内容均为“abc”。我们希望扩展这些文件,使每个文件占用20亿字节:

bash
bash-4.2$ truncate -s 2000000000 s64
bash-4.2$ truncate -s 2000000000 stm

第一个请求立即完成,而第二个则需要几分钟。文件显示逻辑等价性:

bash-4.2$ ls -l
总计 244382
-rwxrwxrwx 1 nobody nobody 2000000000 2月 25日 14:08 s64
-rwxrwxrwx 1 nobody nobody 2000000000 2月 25日 14:11 stm

然而,每个文件所占用的磁盘块数量却大不相同,这可通过VOS的list命令显示出来:

列表

文件数:2,区块数:488764

w 4 s64
w 488760 stm

在版本 18.0 中,等效操作可通过 VOS 的 reset_eof 命令实现:

reset_eof s64 2000000000

如果要求将普通流文件扩展超过几千个块,VOS 命令会发出警告,因为这是一项非常耗费资源的操作

reset_eof stm 2000000000
将文件末尾(eof)扩展至 2000000000 将向文件中添加 488281 个区块。是否要将 stm 扩展 1999999996 字节?(是,否)

以下是扩展后的 64 位流文件。它仅包含两个数据块:

dump_file s64
区块编号 1

000 6162630A 00000000 00000000 00000000 |abc………….|
=
FF0 00000000 00000000 00000000 00000000 |…………….|

区块编号 488282

000 00000000 00000000 00000000 00000000 |…………….|
=
FF0 00000000 00000000 00000000 00000000 |…………….|

导出普通流文件 stm 后,将显示所有 488282 个已分配的块,其中大多数块包含二进制零。

dump_file stm
块号 1
000 6162630A 00000000 00000000 00000000 |abc………….|
=
FF0 00000000 00000000 00000000 00000000 |…………….|

第2块
000 00000000 00000000 00000000 00000000 |…………….|
=
FF0 00000000 00000000 00000000 00000000 |…………….|

第3街区
……

区块编号 488282
000 00000000 00000000 00000000 00000000 |…………….|
=
400 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |…………….|
=
FF0 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |…………….|

尽管如此,compare_files 和 diff 仍将这两个文件视为等同,但需要读取普通流文件中的所有数据块,而在这种情况下,这可能需要几分钟时间:

准备就绪;compare_files s64 stm
准备就绪 14:29:47
准备就绪 14:31:24 45.877 46

文件转换

convert_stream_file 命令可用于在任何顺序文件和流文件类型(流、64 位流、顺序和扩展顺序)之间进行转换,并可更改扩展区。 通常,转换过程涉及复制文件内容,因此类似于创建一个空的目标文件并使用带 -truncate 选项的 copy_file 命令。convert_stream_file 命令可用于就地修改文件,其优势在于能在尝试转换之前确保现有内容能够以所需的新格式表示。

There are cases where a stream file can be reset to stream64 and vice versa without conversion, i.e., without copying contents. This is done by resetting the directory entry and is possible only if the file is not sparse, is less that 2 GB and extent size is not changed. This can be useful when migrating a disk containing stream64 files to a pre-17.2 module and again if the disk is moved back. The set_stream_files command is available for this purpose. It is a privileged command intended for use by a System Administrator: it takes a directory as input and resets files in that directory, and optionally in all sub-directories. It affects only those stream64 files which can be reset without conversion (not sparse and otto>stm
file organization: stream file
last used at: 15-02-25 14:34:45 est

extent size: 1
next byte: 2000000000
blocks used: 488760 <<<<<

然后:
convert_stream_file stm -to stream64 -extent_size 8

display_file_status stm
file organization: stream file (64-bit/rstr)
last used at: 15-02-25 14:39:16 est

extent size: 8
next byte: 2000000000
blocks used: 18 <<<<
sparse: yes

由于该文件现为稀疏文件,因此显示为受限文件(64-bit/rstr)。copy_file命令同样会生成稀疏文件。将stream64文件转换为普通流文件时,所有数据块都会被实例化,因此生成的文件可能远大于原始文件。

在 stream64 文件中,无法保证某个恰好包含二进制零的块是否会被分配到磁盘上。例如,如果你向某个块中写入一些零,导致该块最终全是零,该块仍会被分配;或者,如果某个程序显式地写入零数据来填满块。只有从未被写入过的块才会保持未分配状态;copy_file 和 convert_stream_file 函数绝不会写入全是二进制零的块。

物理特性
– 范围

任何 VOS 文件的实际增长上限由其扩展区大小决定,因此了解什么是扩展区非常重要。 VOS文件映射表包含文件中每个4KB数据块的磁盘地址;该表最多可容纳523792条条目,因此任何不使用扩展区的文件大小上限为2145452032(523792 × 4096)字节。这一数值略低于2³¹,因此也是普通流文件增长的实际上限。

扩展区是磁盘上由N个连续块组成的集合,其中N即为扩展区大小。通过扩展区分配的文件的文件映射包含扩展区中第一个块的地址,因此该文件映射可代表显著更大的文件。 动态分配的扩展区允许扩展区大小达到256,从而支持存储512 GB的文件(注:静态分配的扩展区允许扩展区大小超过256,但已弃用,仅限于分页文件使用;此类扩展区存在诸多限制和性能问题,不适用于常规场景)。

要突破2 GB的容量限制,stream64文件必须启用扩展区,其数值将决定文件的实际增长上限。例如:

create_file stm64 -organization steam64 -extent_size 32

将创建一个最大可达 64 GB 的文件。

在 17.2 版本中,Posix Runtime 创建的文件的默认扩展区大小为 8,允许文件增长至 16 GB。此类文件每次增长 8 个块,因此任何文件的最小大小均为 8 个块。系统管理员可以按模块逐个更改此默认设置,但增长潜力越大,文件的最小大小也就越大。 对于会生成大量极小文件的应用程序而言,较大的扩展区可能会造成问题。

– 扩展如何影响稀疏分配

通常情况下,对于 stream64 文件,从未被写入的块不会被分配;但是,如果一个扩展区中的某个块被写入,那么该扩展区中的所有块都会被分配,即使其中有些块全是二进制零。

假设将包含字符串 abc 的非扩展文件(stm1)转换为具有 8 个扩展的文件(stm8):

dump_file stm1
块编号 1
000 6162630A 00000000 00000000 00000000 |abc………….|
010 00000000 00000000 00000000 00000000 |…………….|
=
FF0 00000000 00000000 00000000 00000000 |…………….|

convert_stream_file stm1 stm8 -extent_size 8

dump_file stm8
块编号 1
000 6162630A 00000000 00000000 00000000 |abc………….|
010 00000000 00000000 00000000 00000000 |…………….|
=
FF0 00000000 00000000 00000000 00000000 |…………….|

第2块
000 00000000 00000000 00000000 00000000 |…………….|
=
FF0 00000000 00000000 00000000 00000000 |…………….|

第3街区
……

第8块
000 00000000 00000000 00000000 00000000 |…………….|
=
FF0 00000000 00000000 00000000 00000000 |…………….|

在 18.0 版本中,dump_file 命令新增了 -brief 选项,当您不想查看那些物理上存在但代表“空”(可能不存在的)区块时,该选项会很有用。下文展示了如何在 DAE-8 文件中使用该选项:

dump_file stm8 -brief
块编号 1
000 6162630A 00000000 00000000 00000000 |abc………….|
010 00000000 00000000 00000000 00000000 |…………….|
=
FF0 00000000 00000000 00000000 00000000 |…………….|

此方法对于隐藏固定文件的“空”区块(即所有字节均为-1的区块)同样有效。

– 灵活扩展

弹性扩展区(Flexible extents)在 18.0 版本中引入,允许小文件每次以一个数据块为单位增长;随着文件变大,扩展区大小也会随之变化。这是 18.0 版本中所有由 Posix 应用程序创建的文件的默认设置,也是在使用 create_file 命令时,若未指定具体扩展区大小所获得的结果:

create_file flex -organization stream64 -extent_size

具有灵活扩展区的文件称为 flex 文件;只有 stream64 文件可以拥有灵活扩展区。display_file_status 命令会将 flex 文件的扩展区大小显示为“flex”。

display_file_status flex
名称: %swsle#Raid4>flex
文件组织方式: 流文件 (64位)
最后修改时间: 15-02-25 12:06:25 est

动态扩展区: 是
扩展区大小: flex

如果在 18.0 之前的模块上运行 display_file_status 命令,将显示:
扩展大小:-1

如果将 flex 文件复制到 17.2 模块的磁盘上,该文件的扩展区将采用该模块的默认设置。如果复制到 17.1 或更早版本,结果将是一个普通的流文件。

在 17.2 及更高版本中,compare_files 无法识别流文件之间的扩展区大小差异,无论扩展区如何,文件看起来都完全相同。例如,假设 %swsle#m109_mas 是一个运行 17.2 版本的模块:

copy_file flex %swsle#m109_mas>Stratus
compare_files flex %swsle#m109_mas>Stratus>flex
完成 14:42:38 0.001 6

在17.2之前的模块上运行compare_files时,会显示以下差异:

compare_files %swsle#Raid>flex %swsle#m109_mas>Stratus>flex
A (%swsle#Raid4>flex) 与 B (%swsle#m109_mas>Stratus>flex) 不匹配。
– 两个文件的某些属性不匹配:
扩展区大小 -1 8

Flex 文件的最大容量略低于 512 GB,具体而言为 540,142,534,656 字节,而 DAE-256 则为 549,235,720,192 字节。但 Flex 文件的优势在于:小文件会先以 1 个数据块为单位增长,随后依次扩展为 8、32 和 256 个数据块,而非始终以 256 个数据块为单位进行扩展。

请勿将包含 flex 文件的磁盘挂载到 18.0 之前的模块上;已有工具可帮助您轻松检查磁盘中是否存在 flex 文件。 若不慎挂载,这些文件将不可见且无法访问。此类文件或其所在的任何目录均无法删除,因此若将磁盘移回运行 18.0 及以上版本的模块,这些文件将保持完整。但若在 18.0 之前的模块上进行数据恢复,所有 flex 文件都将被清除,且空间将被回收。

工具
– 在模块中定位 64 位流文件

当您需要将磁盘迁移到旧版本的 VOS 时,需要找出与旧版本不兼容的文件。有一个命令可以协助完成此操作:

locate_stream_files -form

—————————– locate_stream_files ————————-
directory_names:
-depth:
-type: 64-bit
-brief: no
-long: no

-type 还可以是 flex、sparse 或 large(用于查找具有该特定特征的 64 位流文件),或者 all(用于查找所有流文件)。

根据深度参数,系统将搜索子目录。若文件具备任何未通过类型参数指定的属性,则会同时显示该属性信息及文件大小。若未指定目录名称,则从根目录开始搜索模块上的所有磁盘。例如:

locate_stream_files - 64 位类型

正在检查磁盘上的目录 %swsle#raid0-1…
%swsle#raid0-1:
smb_test.stm64 (FLEX/large)
smb_test.stmb
共计 2 个 64 位流文件。

检查磁盘上的目录 %swsle#raid0-2…
%swsle#raid0-2:
big_stream (FLEX)
smb_test.stm (FLEX)
smb_test.stm64a (FLEX/large)
共计 3 个 64 位流文件。

检查磁盘上的目录 %swsle#Raid4…
%swsle#Raid4>otto:
b3 (DAE-8)
big (DAE-256/large/sparse)

– 块比较

在 17.2 和 18.0 版本中,`compare_files` 命令新增了若干选项,这些选项对于流文件非常有用。对 VOS 结构化文件类型进行逐块比较通常没有意义,因为两个值不同的块往往代表相同的逻辑记录。例如,相对文件中超出当前长度的记录内未使用的数据是无法预测的。 此外,顺序文件包含未定义的填充字节。扩展顺序文件中,即使记录大小不同但代表相同逻辑记录时,其块内容也可能完全不同。

然而,块比较有时对固定文件和流文件仍具实用价值。问题在于compare_files采用记录导向设计,旨在通过记录编号(或行号)显示差异,这些差异可通过编辑器定位。这给非记录结构的二进制流文件带来了处理难题。 由于任何文件的记录大小均限制为 32767 字节,因此在流文件中,当出现超过 32k 字节的序列且中间没有 NL 记录分隔符时,使用 s$seq_read 会返回接下来的 32767 个字符,而无法区分下一个字节是否为 NL。 这使得对于存储二进制值(而非由 NL 字符分隔的字符序列)的流文件而言,基于记录的比较结果变得模棱两可。

此问题已在 17.2 版本中得到解决;compare_files 现可检测此前可能出现的虚假成功情况——具体而言,即 32767 个字符后紧跟一个换行符(NL),而 32767 个字符后未紧跟换行符的情况。若出现此情况,系统将报告错误,指出流文件中包含长度超过 32767 的记录。若从未发生此情况,则基于记录的比较结果即为有效且可信。 但无论如何,若您仅需确保文件内容完全一致,对非结构化文件进行块级比较的速度远快于基于记录的方法。块级比较的弊端在于:由于缺乏记录作为参照基准,一旦出现首个差异,后续的比较往往就失去了意义。

在 17.2 版本中,为实现这一目的,compare_files 命令新增了 -compare_blocks 选项。该选项可快速检测非结构化文件是否逐块完全一致,并定位出差异所在的块。在极少数情况下,若差异是由覆盖操作(即修改特定字节而非插入或删除)引起的,且文件其余部分完全一致,则可使用 -continue 选项。 这将显示文件剩余部分中存在差异的块范围。例如,如果插入了一个字节(而非覆盖),则所有剩余块都会产生差异;也就是说,块比较无法像记录比较那样进行重新同步。但在上述情况下,这些额外信息可以确认唯一的差异仅在于少数几个块中的某些覆盖字节,这些字节可能代表类似时间戳的内容。

以下是 -compare_blocks 配合 -continue 选项的示例:

compare_files flex dae256 -compare_blocks -continue
A (%swsle#m111_mas>Stratus>Newman>flex) 与 B (%swsle#m111_mas>Stratus>Newman>dae256) 不匹配。
– 数据块 2 至 24 存在差异。
– 数据块 33 至 488 存在差异。
– 文件 B 中有 272 个额外数据块。

– 比较稀疏文件

`compare_files` 命令会检查文件中所有实际存在的块。这在稀疏文件中可能会造成问题,因为一个由二进制零组成的块可能在某个文件中显示为已分配的块,但在另一个文件中却不存在。这些文件在逻辑上可能完全相同,但在块与块之间却未必完全一致。事实上,使用 `copy_file` 命令会消除目标文件中的二进制零块;如果存在此类块,这将确保源文件和目标文件在块与块之间不会完全一致。

在基于扩展区的流文件中也存在类似问题,因为扩展区内的所有块都会被实例化,即使它们仅包含二进制零。这意味着稀疏扩展区文件中的块可能与相同数据的非扩展区文件块外观不同,尽管它们存储的是完全相同的数据。执行copy_file操作无法消除扩展区中的零块。

上文展示 -continue 用法的那个示例实际上涉及两个完全相同的文件,唯一的区别在于一个使用 DAE-256 扩展区,另一个使用灵活扩展区。

因此,一个内容为“abc”的 DAE-8 文件将包含一个内容为“abc”的块,后面紧跟 7 个包含二进制零的块。一个非扩展文件(或 flex 文件)将仅包含一个内容为“abc”的块,而一个 DAE-16 文件将包含一个“abc”块,后面紧跟 15 个包含二进制零的块。

使用带 -compare_blocks 选项的 compare_files 命令时,尽管这些文件都表示相同的文件数据,但系统会将它们全部显示为不同。在 18.0 版本中,针对这种情况提供了 -compare_all_blocks 选项。该选项会使 copy_file 在比较的文件中逻辑性地实例化缺失的块,从而实现真正的逐块比较,并消除因稀疏性导致的差异。 在 stream64 文件中,缺失的块被视为全二进制零;而在固定长度文件(或其他所有文件类型)中,缺失的块则被视为二进制 -1(全 FFFF)。

对于非常稀疏的文件,这可能会导致比较速度远低于使用 -compare_blocks 选项的情况,但仍比使用默认的基于记录的比较方式快得多。通常只有在比较具有不同扩展区的文件块时才需要此选项,尽管如前所述,还存在其他稀疏程度可能不同的情况。对于具有不同扩展区的固定文件,此选项同样有用。