在 Linux 上快速连接多个文件
Posted
技术标签:
【中文标题】在 Linux 上快速连接多个文件【英文标题】:Fast concatenate multiple files on Linux 【发布时间】:2011-08-19 02:36:56 【问题描述】:我正在使用 Python 多处理为每个进程生成一个临时输出文件。它们的大小可以是几 GB,我做了几十个。需要连接这些临时文件以形成所需的输出,这是被证明是瓶颈(和并行性杀手)的步骤。是否有一个 Linux 工具可以通过修改文件系统元数据而不是实际复制内容来创建连接文件?只要它适用于我可以接受的任何 Linux 系统。但是文件系统特定的解决方案不会有太大帮助。
我没有受过操作系统或 CS 培训,但从理论上讲,似乎应该可以创建一个新的 inode 并从我要复制的文件的 inode 中复制 inode 指针结构,然后取消链接这些 inode。是否有任何实用程序可以做到这一点?考虑到经过深思熟虑的 unix 实用程序过多,我完全期望它会如此,但找不到任何东西。因此,我对 SO 的问题。文件系统位于块设备上,实际上是硬盘,以防此信息很重要。我没有信心自己写这篇文章,因为我以前从未做过任何系统级编程,所以任何指针(指向 C/Python 代码片段)都会很有帮助。
【问题讨论】:
@san:作为背景,请您谈谈为什么最终输出必须是单个文件。 @aix 它输入到我无法控制的另一段代码。 @san:你事先知道每个临时文件的大小吗? @san: 你不能只在下一个进程的标准输入上提供cat file1 ... fileN |
而不是常规文件吗?
@aix 不,我不知道。它也不是每个进程的固定数字。我可能愿意冒险猜测上限。但是,如果没有它有办法做到这一点,那就太好了。
【参考方案1】:
即使有这样的工具,这也只能在文件除了最后一个 保证大小是文件系统块的倍数 大小。
如果您控制数据写入临时文件的方式,并且您知道 每个有多大,您可以改为执行以下操作
在开始多处理之前,创建最终输出文件,并增长
它的最终大小
fseek()
ing
最后,这将创建一个
sparse file.
启动多处理,将 FD 和偏移量交给每个进程到它的 文件的特定部分。
这样,进程将协作填充单个输出文件, 无需稍后将它们放在一起。
编辑
如果您无法预测单个文件的大小,但
最终文件可以使用顺序(而不是随机访问)输入,您可以
在标准输入上将cat tmpfile1 .. tmpfileN
提供给消费者
cat tmpfile1 ... tmpfileN | consumer
或通过命名管道(使用 bash 的进程替换):
consumer <(cat tmpfile1 ... tmpfileN)
【讨论】:
+1 自从我看到有人引用稀疏文件以来已经有一段时间了。 刚刚添加了关于此的评论。我的主要问题是(i)我没有保证每个进程将写入多少的上限,并且(ii)不知道该文件的使用者将如何处理这些漏洞。如果有办法检测和消除孔洞,它可能仍然值得一试。【参考方案2】:您表示事先不知道每个临时文件的大小。考虑到这一点,我认为最好的办法是编写一个FUSE 文件系统,将块显示为单个大文件,同时将它们作为单独的文件保存在底层文件系统中。
在此解决方案中,您的生产和消费应用保持不变。生产者写出一堆文件,FUSE 层使它们显示为单个文件。然后将该虚拟文件呈现给消费者。
FUSE 具有多种语言的绑定,including Python。如果您查看一些示例 here 或 here(这些用于不同的绑定),这需要的代码非常少。
【讨论】:
谢谢。我以前不知道保险丝。希望开销不要太高。如果它可以在 3 分钟左右读取 200Gb,我会很高兴。 @san: FUSE 或没有 FUSE,我希望读取是磁盘绑定的,所以我怀疑您会发现读取性能有什么不同。 谢谢(实际上我所做的串联是 I/O 绑定的)。希望有一种方法可以接受两个答案,因为我喜欢 FUSE 和命名管道解决方案。我都投了赞成票,但我只能接受一个。【参考方案3】:对于 4 个文件; xaa、xab、xac、xad bash 中的快速串联(以 root 身份):
losetup -v -f xaa; losetup -v -f xab; losetup -v -f xac; losetup -v -f xad
(假设loop0、loop1、loop2、loop3是新设备文件的名称。)
将http://pastebin.com/PtEDQH7G 放入“join_us”脚本文件中。然后你可以这样使用它:
./join_us /dev/loop0..3
然后(如果这个大文件是电影)您可以将其所有权授予普通用户(chown itsme /dev/mapper/joined),然后他/她可以通过以下方式播放它:mplayer /dev/mapper/joined
这些之后的清理(以root身份):
dmsetup remove joined; losetup -d /dev/loop[0123]
【讨论】:
【参考方案4】:我不这么认为,inode 可能是对齐的,因此只有在您可以在一个文件的页脚和另一个文件的页眉之间留下一些零(或未知字节)的情况下才有可能。
我建议不要连接这些文件,而是重新设计分析工具以支持从多个文件中进行采购。以日志文件为例,很多日志分析器都支持每天读取日志文件。
编辑
@san:正如您所说,您无法控制正在使用的代码,您可以使用命名管道即时连接单独的文件:
$ mkfifo /tmp/cat
$ cat file1 file2 ... >/tmp/cat &
$ user_program /tmp/cat
...
$ rm /tmp/cat
【讨论】:
您能否详细说明一下 inode 对齐问题。 一个文件的长度可能是12345字节,如果inode的大小(可以通过sudo tune2fs -l /dev/sda1
得到)是4096字节,那么文件占用4个inode。 4 个 inode 总共是 16384 字节,所以最后一个 inode 没有填满,所以最后留下了 4096*4 - 12345 = 4039
未知字节。
命名的 fifo 似乎是我们正在融合的最方便的解决方案。 @mmutz 在这个线程上建议它。但是感谢您对 inode 的解释。但你的意思是阻止对吗?因为iirc(其中r=read)每个文件对应一个inode,但是inode指针结构指的是磁盘上的多个block。
是的,我的错误,我的意思是块,而不是 inode! O.O【参考方案5】:
不,没有这样的工具或系统调用。
您可能会调查每个进程是否可以直接写入最终文件。假设进程 1 写入字节 0-X,进程 2 写入 X-2X 等等。
【讨论】:
是的,我确实考虑过这一点,即我打开一个文件并寻找不同的偏移量,就在那里。我不确定该文件的使用者将如何处理创建的漏洞。此外,我没有保证每个进程将写入多少的上限。【参考方案6】:一种可能的替代方法是将所有临时文件分类到一个命名管道中,然后将该命名管道用作单输入程序的输入。只要你的单输入程序只是按顺序读取输入而不是寻找。
【讨论】:
是的!我们刚刚在问题的 cmets 部分讨论了这个问题。以上是关于在 Linux 上快速连接多个文件的主要内容,如果未能解决你的问题,请参考以下文章