【问题标题】:Diff/compare two files by file descriptor (fd) instead of file name通过文件描述符(fd)而不是文件名来比较/比较两个文件
【发布时间】:2017-07-11 16:09:27
【问题描述】:

在 Linux 中有什么方法可以使用 生成存储在内存中的两个文件的差异/补丁,使用通用格式(即:统一差异,如命令行 diff 实用程序)?

我正在开发一个系统,我在内存中生成两个文本文件,但没有可用的或不需要的外部存储。我需要创建两个文件的逐行差异,因为它们是mmap'ed,它们没有文件名,阻止我简单地调用system("diff file1.txt file2.txt")

我有可用的文件描述符 (fds),这是我访问数据的唯一入口点。有没有办法通过比较两个打开的文件来生成差异/补丁?如果实现是 MIT/BSD 许可的(即:非 GPL),那就更好了。

谢谢。

【问题讨论】:

  • 我找不到使用 2 个标准输入参数作为文件调用 diff 的方法,但这是一种方法。
  • 另一种方法是使用comm - - 和馈线交替,但它仅在文件同步时才有效。
  • 我知道您说过“没有可用的或不需要的外部存储”。但是您是否考虑过使用像 ramfs 这样的简单内存文件系统?它基本上出现在所有 linux 发行版和 AFAIK 上,它的主要用途是在早期启动期间抛出一个临时文件系统。
  • @MitchellGouzenko 我的部分要求是没有其他人能够访问这些文件,除了 root 用户通过 GDB 附加到进程以强制访问数据。跨度>

标签: c c linux diff patch mmap


【解决方案1】:

在 Linux 上,您可以使用 /dev/fd/ 伪文件系统(到 /proc/self/fd 的符号链接)。使用 snprintf() 为两个文件描述符(如 snprintf(path1, PATH_MAX, "/dev/fd/%d", fd1); fd2 同上)构造路径并在它们上运行 diff

【讨论】:

    【解决方案2】:

    考虑到需求,最好的选择是实现您自己的内存中diff -au。您或许可以根据您的需要调整 OpenBSD 的 diff 的相关部分。


    这里概述了如何通过管道使用/usr/bin/diff 命令来获得存储在内存中的两个字符串之间的统一差异:

    1. 创建三个管道:I1I2O

    2. fork 一个子进程。

    3. 在子进程中:

      1. 将管道 I1I2 的读取端移动到描述符 3 和 4,将管道 O 的写入端移动到描述符 1.

      2. 在子进程中关闭这些管道的另一端。打开描述符 0 用于读取 /dev/null,打开描述符 2 用于写入 /dev/null。

      3. 执行execl("/usr/bin/diff", "diff", "-au", "/proc/self/fd/3", "/proc/self/fd/4", NULL);

        这会在子进程中执行diff 二进制文件。它将读取来自两个管道 I1I2 的输入,并将差异输出到管道 O

    4. 父进程关闭I1I2管道的读端,以及O管道的写端。

    5. 父进程将比较数据写入I1I2管道的写端,从O的读端读取差异em> 管道。

      注意,父进程必须使用select()poll()或类似的方法(最好使用非阻塞描述符)以避免死锁。 (如果父进程和子进程同时尝试读取或写入,则会发生死锁。)通常,父进程必须不惜一切代价避免阻塞,因为这很可能导致死锁。

      当输入数据已经完全写入后,父进程必须关闭管道各自的写入端,以便子进程检测到输入结束。 (除非发生错误,写端必须在子进程关闭它的 O 管道结束之前关闭。)

      当父进程注意到 O 管道中没有更多可用数据时(read() 返回0),要么它已经关闭了 I1 和 I2 管道,否则出现错误。如果没有错误,则数据传输完成,可以收割子进程了。

    6. 父进程使用例如waitpid()。请注意,如果有任何差异,diff 将返回退出状态 1。

    您可以使用第四个管道从子进程接收标准错误流; diff 通常不会向标准错误输出任何内容。

    您可以使用第五个管道,将标记为O_CLOEXEC 的结尾写为子级中的fcntl(),以检测execl() 错误。 O_CLOEXEC标志表示描述符在执行另一个二进制文件时关闭,因此父进程可以通过检测读取端的数据结束(read()返回0)来检测diff命令的成功启动。如果execl() 失败,孩子可以例如将errno 值(作为十进制数,或作为int)写入此管道,以便父进程可以读取失败的确切原因。

    总的来说,完整的方法(既记录标准错误,又检测执行错误)使用 10 个描述符。这在普通应用程序中应该不是问题,但可能很重要——例如,考虑一个面向 Internet 的服务器,其中包含传入连接使用的描述符。

    【讨论】:

      猜你喜欢
      • 2014-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-25
      相关资源
      最近更新 更多