通过文件描述符(fd)而不是文件名来比较/比较两个文件

时间:2017-02-21 20:35:46

标签: c linux diff patch mmap

Linux中是否有任何方法使用生成存储在内存中的两个文件的diff / patch,使用通用格式(即:统一差异,与命令行{{1}一样)实用工具)?

我正在开发一个系统,我在内存中生成两个文本文件,没有可用的外部存储空间。我需要创建两个文件的逐行差异,因为它们是diff,所以它们没有文件名,这使我无法简单地调用mmap

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

谢谢。

2 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:2)

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

以下是如何通过管道使用/usr/bin/diff命令获取存储在内存中的两个字符串之间的统一差异的概述:

  1. 创建三个管道: I1 I2 O

  2. 分叉子进程。

  3. 在子进程中:

    1. 将管道 I1 I2 的读取端移动到描述符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二进制文件。它将读取两个管道 I1 I2 的输入,并将差异输出到管道 O

  4. 父进程关闭 I1 I2 管道的读取端,以及 O 管道的写入端。

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

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

    当完全写入输入数据时,父进程必须关闭管道的相应写端,以便子进程检测到输入结束。 (除非发生错误,否则必须在子进程关闭 O 管道末尾之前关闭写入结束。)

    当父进程注意到 O 管道中没有更多数据可用时(read()返回0),它已经关闭了< em> I1 和 I2 管道,或者出现错误。如果没有错误,则数据传输完成,并且可以获得子进程。

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

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

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

    总之,完整的方法(记录标准错误,并检测exec错误)使用10个描述符。这不应该是正常应用程序中的问题,但可能很重要 - 例如,考虑面向Internet的服务器,其中包含传入连接使用的描述符。