复制文件描述符并独立地在两个描述符中寻找

4

我有一个打开的文件描述符,我想复制它以便可以独立地执行读写和查找操作。 我查看了

int dup(int old_fd)

syscall。问题在于它并不真正适合这里。手册页说明如下http://man7.org/linux/man-pages/man2/dup.2.html

成功返回后,旧的和新的文件描述符可以互换使用。它们指向同一个打开的文件描述符(参见open(2)),因此共享文件偏移量和文件状态标志;例如,如果通过使用lseek(2)在其中一个文件描述符上修改文件偏移量,则对另一个文件描述符也进行更改。

有没有一种方法可以复制文件描述符,使它们完全独立?


pread()pwrite()可以在您选择的位置使用。但是,您必须在操作系统之外跟踪它。 - Robert Siemer
2个回答

5
在Linux中,打开/proc/<pid>/fd/<n>会打开当前在文件描述符N处打开的文件,但这是一个新副本,而不是像使用dup()和相关函数时获得的链接副本。
这将创建一个包含bar、一堆零字节,然后是foo的文件。与使用dup()的版本相比较。
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(void)
{
    int fd1, fd2;
    char buffer[50];

    fd1 = open("testfile", O_CREAT | O_TRUNC | O_RDWR, 0600);
    sprintf(buffer, "/proc/self/fd/%d", fd1);
#ifndef USE_DUP
    fd2 = open(buffer, O_RDWR);
    if (fd2 == -1) {
        perror("open");
    }
#else
    fd2 = dup(fd1);
#endif
    if (lseek(fd1, 16, SEEK_SET) == -1) {
        perror("lseek");
    }
    if (write(fd1, "foo", 3) == -1) {
        perror("write(fd1)");
    }
    if (write(fd2, "bar", 3) == -1) {
        perror("write(fd2)");
    }
}

哇,非常有趣。有文档记录吗? - Some Name
1
@SomeName,我不确定。proc(5) man page记录了/proc/<pid>/fd目录,但它只是说readlink()会给出有用的信息,并且你可以打开它们(/dev/stdin特别指向/proc/self/fd/0,所以打开必须有效)。我不知道是否有官方文档描述它们与dup() 的区别。尽管它们必须存在差异,因为您可以从_另一个_进程的/proc/<pid>/fd中打开文件。 - ilkkachu
无论如何,只要Linux开发人员不认真改变他们不干扰用户空间可见行为的立场,我认为当前的行为不会改变。 - ilkkachu
但是如果我们移动原始文件,符号链接会变成悬空的吗? - Some Name
4
@SomeName,实际上并不是这样的,它遵循重命名。打开它会直接访问_inode_,然而在符号链接中显示的名称只是为了展示。它们不是普通的符号链接。(如果你有硬链接,即使文件仍然存在,符号链接中也可能显示“(已删除)”,你仍然可以通过符号链接打开它)。 - ilkkachu
1
打开 /proc/self/fd/FD 仍然受到 inode 权限的限制,因此这不是一种可靠的方式来深度复制 fd -- fd 可能被一个特权进程打开并通过继承或 SCM_RIGHTS 传递给另一个权限较低的进程。当尝试按进程或调用基础设置非阻塞标志时,同样会出现这个问题。请参见这里,特别是 lkml [讨论] (https://lkml.org/lkml/2007/8/14/135) 的链接。 - user10678532

3

不可以,在POSIX定义的机制中至少不能这样做。

如果您想完全独立于文件描述符,您需要避免共享打开的文件描述符,这意味着需要使用独立的open()或等效函数。

有可能存在一种我没有听说过的Linux特定机制可以实现这个目标。然而,在http://man7.org/linux/man-pages/man2/中查看Linux系统调用并没有提供启示。


使用/proc/<pid>/fd/NN应该可以工作。这确实涉及到第二个open(),正如我所规定的那样。/proc文件系统并不适用于所有其他操作系统。 - Jonathan Leffler
我不确定你在问什么情况。移动文件通常涉及rename()函数,或者可能是link()unlink()。如果文件已打开,则即使在打开时重命名(甚至在文件权限更改时),也可以读取该文件。因此,可能没有问题,但我不确定我知道上下文是什么,所以这更像是一个猜测,而不是我想要的那样。 - Jonathan Leffler
1
实际上,/proc/<pid>/fd/NN 中的条目始终是最新的。可能在线程1读取了 fd 5 的条目后(例如),在其可以在任何其他系统调用中使用它之前,线程2已更改了 fd 5 指向的内容,但这本质上是不可避免的。 - Jonathan Leffler
1
/prod/<pid>/fd/NN 中的条目与我们在目录中创建的常规符号链接不同。它位于特殊的 /proc 文件系统上,基本上提供了对内核内存中每个进程信息的文件系统样式访问 - 一切都是“伪文件”或“伪目录”,而不是在常规的“磁盘支持”文件系统上找到的常规文件或目录(其中“磁盘支持”越来越不准确,因为它更像是 SSD 或内存支持)。 - Jonathan Leffler
1
@SomeName 在 /proc/<pid>/fd 中符号链接的视觉方面可能是虚假的,如果支持文件描述符的文件在文件系统中没有名称(即已删除)。open() 调用仍然会打开该文件。请注意,符号链接中提到的文件名,例如 /tmp/#4849919 (deleted) 是有效的文件名,但与此无关。 - Robert Siemer
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接