通过偏移引用分割/引用大文件

Mr.*_*Mr. 10 linux filesystems

split将文件分割成多个块,这些块总共消耗相同的存储空间(消耗的磁盘空间加倍)。

ln可以创建到其他(目标)文件的符号链接(symlink),同时不复制文件,因此不会消耗目标文件双倍的空间。

由于缺乏存储空间,是否可以通过指向大文件中特定偏移量的引用/符号方式(即虚拟地分割文件)来分割文件?

例如,给定一个 2MB 的文件,将其分成 2 个部分,其中每个部分引用大文件的 1MB(与符号链接工作原理相同),这样每个部分:

  • 不与其他片段重叠(片段不会引用大文件中的相同数据)
  • 不消耗与其引用的大文件部分相同的存储大小
piece_1.file -> 2mb.file 1st MB
piece_2.file -> 2mb.file 2nd MB
Run Code Online (Sandbox Code Playgroud)

并且每块的存储大小远小于1MB

Mar*_*ler 15

\n

由于缺乏存储空间,是否可以通过指向大文件中特定偏移量的引用/符号方式(即虚拟地分割文件)来分割文件?

\n
\n

不直接,不。在 POSIX 的思维中,文件并不是这样工作的。它们是更独立的原子数据单元。

\n

两种选择:

\n

环回设备

\n

这是一个运行时解决方案,这意味着它不是磁盘上解决方案,而是需要手动设置。这可能是优点,也可能是缺点!

\n

你可以很容易地设置一个环回设备;如果您使用的是与 freedesktop 系统消息总线兼容的会话管理器(即,您以图形方式登录到计算机并运行 gnome、xfce4、kde、\xe2\x80\xa6),则 udisks 是您的朋友:

\n
blksize=$((2**20))\nudisksctl loop-setup -s $blksize -f /your/large/file\nudisksctl loop-setup -s $blksize -o $blksize -f /your/large/file\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • 第一个命令为您提供/dev/loop从 0. 字节开始并持续 2\xc2\xb2\xe2\x81\xb0 字节(即 1 兆字节)的 a 。
  • \n
  • 第二个命令为您提供/dev/loop+从 2\xc2\xb2\xe2\x81\xb0 开始的 a。字节并持续 2\xc2\xb2\xe2\x81\xb0 字节(即 1 兆字节)。(注意我们如何从 0 开始计数,因此这实际上正好在第一个块之后。)
  • \n
\n

然后您可以使用这两个环回设备,例如/dev/loop0/dev/loop1。它们从字面上描述了文件的“视图”。您在这些块设备中更改某些内容,在大文件中更改它。

\n

如果您不以图形方式登录,也可以实现完全相同的效果,但您需要 root 权限:

\n
blksize=$((2**20))\nsudo losetup --sizelimit $blksize -f /your/large/file\nsudo losetup --sizelimit $blksize -o $blksize -f /your/large/file\n
Run Code Online (Sandbox Code Playgroud)\n

重新链接存储块

\n

这是磁盘上的解决方案。它还将制作一个“逻辑”副本,即,如果您更改小文件中的某些内容,它将不会反映在大文件中。

\n

必须使用支持的文件系统reflink(据我所知,这些是 XFS、Btrfs 和一些网络文件系统)。(需要复制分割所经过的文件系统块,但对于大多数文件系统,我们这里讨论的是小于 4 kB。)

\n

在这种情况下,只要副本或原始文件没有更改(即使更改了,也只会复制受影响的部分),您的文件系统就可以在没有副本的情况下使用其自己的任何空间来复制文件。

\n

所以,在这样的文件系统上,我们有两个选择:

\n
    \n
  1. “正常”地分成前半部分和后半部分,然后要求实用程序 ( duperemove) 比较三个文件并进行重复数据删除。
  2. \n
  3. 以提示文件系统直接避免使用两倍空间的方式复制文件的两半。
  4. \n
\n

由于第一个选项暂时需要两倍的空间,所以我们立即执行第二个选项。我编写了一个小程序来为您执行此拆分(完整源代码)(注意:发现了一个错误,我不会修复该错误。这只是一个示例)。相关摘录是这样的:

\n
// this is file mysplit.c , a C99 program\n// SPDX-License-Identifier: Linux-man-pages-copyleft\n// (heavily based on the copy_file_range(2) man page example)\n// compile with `cc -o mysplit mysplit.c`\n// [\xe2\x80\xa6]\nint main(int argc, char* argv[])\n{\n[\xe2\x80\xa6]\n        ret = copy_file_range(fd_in /*input file descriptor*/,\n                              &in_offset /*address of input offset*/,\n                              fd_out /*output file descriptor*/,\n                              NULL /*address of output offset*/,\n                              len /*amount of bytes to copy*/,\n                              0 /*flags (reserved, must be ==0)*/);\n[\xe2\x80\xa6]\n}\n
Run Code Online (Sandbox Code Playgroud)\n

让我们尝试一下。我们必须:

\n
    \n
  • 获取并编译我的程序
  • \n
  • 首先创建一个 XFS 文件系统,因为它是支持引用链接的文件系统之一
  • \n
  • 安装它
  • \n
  • 将一个充满随机数据的大文件放入其中
  • \n
  • 检查该文件系统上的可用空间
  • \n
  • 添加文件的分割(使用我的程序)
  • \n
  • 再次检查可用空间。
  • \n
\n

下面的脚本就是这样做的。

\n
# replace "apt" with "dnf" if you\'re on some kind of redhat/fedora\n# replace "apt install" with "pacman" followed by a randomly guessed amount of options involving the letters {y, s, S, u} if you\'re on arch or manjaro\napt install curl gcc xfsprogs\n\n# Download the C program from above\ncurl https://gist.githubusercontent.com/marcusmueller/d1e0235f9a484cb44626e35460a5c0ac/raw/6295f9f6371b916b87a5d0a5a6edad65f9ea8627/mysplit.c > mysplit.c\n\n# Comile\ncc -o mysplit mysplit.c\n\n# Demo:  (doesn\'t need root privileges if you got udisks)\n# Make file system in a file system image,\n# Mount that image\n# create a 300 MB file in there,\n# split it,\n# show there\'s still nearly the same amount of free space\n\n\n# Make file system in a file system image\nfallocate -l 1G filesystemimage # 1 GB in size\n## this is a bit confusing, but to make the root of the file system\n## world-writable, we do:\necho "thislinejustforbackwardscompatibility/samefornextline\n1337 42\nd--777 ${UID} ${GID}" > /tmp/protofile\nmkfs.xfs -p /tmp/protofile -L testfs filesystemimage\n\n# Mount that image\nloopdev=$(LANG=C udisksctl loop-setup -f filesystemimage | sed \'s/.* as \\(.*\\).$/\\1/\')\n## on most systems, that new device is automatically mounted.\nsleep 3\n## udisksctl mount -b "${loopdev}"\n\n# create a 300 MB file in there,\ntarget="/run/media/$(whoami)/testfs"\nrndfile="${target}/largefile"\ndd if=/dev/urandom "of=${rndfile}" bs=1M count=300\n## Check free space\necho "free space with large file"\ndf -h "${target}"\n\n# split it,\n## (copy the first 100 MB)\n./mysplit "${rndfile}" "${target}/split_1" 0 $((2**20 * 100))\n## (copy the next 120 MB, just showing off that splits don\'t need to be of uniform size)\n./mysplit "${rndfile}" "${target}/split_2" "$((2**20 * 100))" "$((2**20 * 120))"\n# show there\'s still nearly the same amount of free space\n## Check free space\necho "free space with large file + splits"\ndf -h "${target}"\n
Run Code Online (Sandbox Code Playgroud)\n


Sté*_*las 7

在 Linux 上,您可以使用设备执行此类操作loop

例如:

losetup --find --show             --sizelimit=2M file
losetup --find --show --offset=2M --sizelimit=2M file
losetup --find --show --offset=4M --sizelimit=2M file
Run Code Online (Sandbox Code Playgroud)

将输出引用 3 个 2MiB 大部分的 3 个循环设备的路径file

  • 啊,我没有意识到 `losetup` 支持 `M` 作为偏移量(尽管手册页确实提到了它)。 (2认同)