不共享偏移量或标志的C文件描述符复制

4
我需要使用C语言同时从文件不同的偏移量中读取内容。不幸的是,dup创建的文件描述符与原始文件共享偏移量和标志。
是否有类似于dup但不共享偏移量和标志的函数? 编辑 我只能访问文件指针FILE* fp;,我没有文件路径 编辑 该程序编译为Windows以及Mac和许多Linux版本 解决方案 我们可以在posix系统上使用pread,并且我为Windows编写了一个pread函数来解决这个问题。 https://github.com/Storj/libstorj/blob/master/src/utils.c#L227

1
文件要 open() 两次? - EOF
我只能访问文件指针。 - Alexander Leitner
4个回答

3
在Linux上,您可以从/proc/self/fd/N中恢复文件名,其中N是文件描述符的整数值:
sprintf( linkname, "/proc/self/fd/%d", fd );

然后在结果链接名称上使用readlink()

如果文件已被重命名或删除,你可能就没戏了。

但是,为什么需要另一个文件描述符呢?您可以使用原始文件描述符上的pread()和/或pwrite()从/向文件读取/写入数据而不影响当前偏移量。(注意:在Linux上,对于以附加模式打开的文件进行pwrite()是有问题的 - POSIX规定对于以附加模式打开的文件进行pwrite()将写入pwrite()调用中指定的偏移量,但是Linux的pwrite()实现存在缺陷,将忽略偏移量并将数据追加到文件末尾 - 请参阅Linux手册的BUGS部分)


这个有没有 Windows 的相应版本?我需要为许多不同的操作系统编译程序。 - Alexander Leitner
1
@AlexanderLeitner 这可能是相关的(我没有足够深入的Windows编程经验来确定):GetFinalPathNameByHandle - Andrew Henle

2
不,既没有C语言也没有POSIX(由于提到了dup()函数)有一个基于现有文件句柄打开新的独立文件句柄的函数。正如你所观察到的,你可以使用dup()复制一个文件描述符,但是结果仍然指向相同的底层打开文件描述符。
要获得独立的句柄,需要使用相同的路径来open()或者fopen()(只有FILE对象可以通过文件系统访问才可能)。如果你不知道该路径是什么,或者根本不存在这样的路径,那么你将需要另一种方法。
考虑一些替代方案:
  • 缓冲所有或部分文件内容到内存中,并根据需要从缓冲区读取以满足您对独立文件偏移量的需求;
  • 构建内部等效的tee命令;这可能需要第二个线程,并且很可能不能太快地读取其中一个文件,或在任何一个文件中进行搜索;
  • 将文件内容复制到具有已知名称的临时文件中,并多次打开它;
  • 如果FILE对应于常规文件,则将其映射到内存中并在那里访问其内容。在这种情况下,POSIX函数fmemopen()可能对于将内存映射适应于您现有的基于流的使用方法很有用。

一个临时的仅适用于Linux的解决方案似乎是这样的sprintf(path, "/proc/self/fd/%d", fileno(state->original_file));我这样做的主要原因是我停止了向磁盘写入任何文件,这些都是太多千兆字节数据的上传文件,所以我似乎需要研究内存映射文件。 - Alexander Leitner

1

在Windows上(假设使用VisualStudio),您可以从stdio FILE句柄访问操作系统文件句柄。从那里,重新打开它并转换为新的FILE句柄。

这仅适用于Windows,但我认为Andrew的答案也适用于Linux和可能的Mac - 不幸的是,没有可移植的方法使其在所有系统上工作。

#include <Windows.h>
#include <fcntl.h>
#include <io.h>  
#include <stdio.h>

FILE *jreopen(FILE* f)
{
    int n = _fileno(f);
    HANDLE h = (HANDLE)_get_osfhandle(n);
    HANDLE h2 = ReOpenFile(h, GENERIC_READ, FILE_SHARE_READ, 0);
    int n2 = _open_osfhandle((intptr_t)h2, _O_RDONLY);
    FILE* g = _fdopen(n2, "r");

    return g;
}

0

我能够在POSIX系统上使用pread和pwrite函数,并将Windows系统上的ReadFile/WriteFile函数封装成pread和pwrite函数。

#ifdef _WIN32
ssize_t pread(int fd, void *buf, size_t count, uint64_t offset)
{
    long unsigned int read_bytes = 0;

    OVERLAPPED overlapped;
    memset(&overlapped, 0, sizeof(OVERLAPPED));

    overlapped.OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32);
    overlapped.Offset = (uint32_t)(offset & 0xFFFFFFFFLL);

    HANDLE file = (HANDLE)_get_osfhandle(fd);
    SetLastError(0);
    bool RF = ReadFile(file, buf, count, &read_bytes, &overlapped);

     // For some reason it errors when it hits end of file so we don't want to check that
    if ((RF == 0) && GetLastError() != ERROR_HANDLE_EOF) {
        errno = GetLastError();
        // printf ("Error reading file : %d\n", GetLastError());
        return -1;
    }

    return read_bytes;
}

ssize_t pwrite(int fd, const void *buf, size_t count, uint64_t offset)
{
    long unsigned int written_bytes = 0;

    OVERLAPPED overlapped;
    memset(&overlapped, 0, sizeof(OVERLAPPED));

    overlapped.OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32);
    overlapped.Offset = (uint32_t)(offset & 0xFFFFFFFFLL);

    HANDLE file = (HANDLE)_get_osfhandle(fd);
    SetLastError(0);
    bool RF = WriteFile(file, buf, count, &written_bytes, &overlapped);
    if ((RF == 0)) {
        errno = GetLastError();
        // printf ("Error reading file :%d\n", GetLastError());
        return -1;
    }

    return written_bytes;
}
#endif

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