C 文件描述符复制而不共享偏移量或标志
C File descriptor duplication without sharing offset or flags
我需要使用 C 从不同偏移量的文件中并发读取。
dup
不幸地创建了一个与原始文件共享偏移量和标志的文件描述符。
是否有像dup
这样不共享偏移量和标志的函数?
编辑我只能访问文件指针FILE* fp;
我没有文件路径
EDIT 除了 mac 和 linux
的多种风格之外,该程序还针对 windows 编译
解决方案
我们可以在 posix 系统上使用 pread,我为 windows 写了一个 pread 函数来解决这个问题
https://github.com/Storj/libstorj/blob/master/src/utils.c#L227
不,C 和 POSIX(因为你提到 dup()
)都没有基于现有文件句柄打开新的独立文件句柄的功能。如您所见,您可以 dup()
一个文件描述符,但结果引用相同的基础打开文件描述。
要获得一个独立的句柄,你需要open()
或fopen()
相同的路径(只有当FILE
指的是一个可以通过文件系统访问的对象时才有可能) .如果您不知道那是什么路径,或者一开始就没有路径,那么您将需要一种不同的方法。
一些可供考虑的备选方案:
- 在内存中缓冲部分或全部文件内容,并根据需要从缓冲区中读取以满足您对独立文件偏移量的需求;
- 构建
tee
命令的内部等价物;这可能需要第二个线程,并且您可能无法先于另一个文件读取一个文件,或者无法在其中一个文件中查找;
- 将文件内容复制到已知名称的临时文件中,并根据需要多次打开该文件;
- 如果 FILE 对应于一个常规文件,则将其映射到内存并在那里访问其内容。 POSIX 函数
fmemopen()
在这种情况下可能很有用,可以使内存映射适应您现有的基于流的使用。
在Linux上,可以从/proc/self/fd/N
恢复文件名,其中N
是文件描述符的整数值:
sprintf( linkname, "/proc/self/fd/%d", fd );
然后在生成的 link 名称上使用 readlink()
。
如果文件已被重命名或删除,您可能会倒霉。
但是为什么您需要另一个文件描述符?您可以在原始文件描述符上使用 pread()
and/or pwrite()
到 read/write from/to 文件而不影响当前偏移量。 (警告:在 Linux 上,pwrite()
到以追加模式打开的文件是错误的 - POSIX 指出 pwrite()
到以追加模式打开的文件将写入指定的偏移量在 pwrite()
调用中,但 Linux pwrite()
实现被破坏,将忽略偏移量并将数据附加到文件末尾 - see the BUGS section of the Linux man page)
在 windows(假设是 VisualStudio)上,您可以从 stdio FILE 句柄访问 OS 文件句柄。
从那里重新打开它并转换回新的文件句柄。
这只是 windows,但我认为 Andrews 的答案适用于 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;
}
我能够在 POSIX 系统上使用 pread 和 pwrite,并且我将 ReadFile/WriteFile 在 Windows 系统上包装成 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
我需要使用 C 从不同偏移量的文件中并发读取。
dup
不幸地创建了一个与原始文件共享偏移量和标志的文件描述符。
是否有像dup
这样不共享偏移量和标志的函数?
编辑我只能访问文件指针FILE* fp;
我没有文件路径
EDIT 除了 mac 和 linux
的多种风格之外,该程序还针对 windows 编译解决方案 我们可以在 posix 系统上使用 pread,我为 windows 写了一个 pread 函数来解决这个问题 https://github.com/Storj/libstorj/blob/master/src/utils.c#L227
不,C 和 POSIX(因为你提到 dup()
)都没有基于现有文件句柄打开新的独立文件句柄的功能。如您所见,您可以 dup()
一个文件描述符,但结果引用相同的基础打开文件描述。
要获得一个独立的句柄,你需要open()
或fopen()
相同的路径(只有当FILE
指的是一个可以通过文件系统访问的对象时才有可能) .如果您不知道那是什么路径,或者一开始就没有路径,那么您将需要一种不同的方法。
一些可供考虑的备选方案:
- 在内存中缓冲部分或全部文件内容,并根据需要从缓冲区中读取以满足您对独立文件偏移量的需求;
- 构建
tee
命令的内部等价物;这可能需要第二个线程,并且您可能无法先于另一个文件读取一个文件,或者无法在其中一个文件中查找; - 将文件内容复制到已知名称的临时文件中,并根据需要多次打开该文件;
- 如果 FILE 对应于一个常规文件,则将其映射到内存并在那里访问其内容。 POSIX 函数
fmemopen()
在这种情况下可能很有用,可以使内存映射适应您现有的基于流的使用。
在Linux上,可以从/proc/self/fd/N
恢复文件名,其中N
是文件描述符的整数值:
sprintf( linkname, "/proc/self/fd/%d", fd );
然后在生成的 link 名称上使用 readlink()
。
如果文件已被重命名或删除,您可能会倒霉。
但是为什么您需要另一个文件描述符?您可以在原始文件描述符上使用 pread()
and/or pwrite()
到 read/write from/to 文件而不影响当前偏移量。 (警告:在 Linux 上,pwrite()
到以追加模式打开的文件是错误的 - POSIX 指出 pwrite()
到以追加模式打开的文件将写入指定的偏移量在 pwrite()
调用中,但 Linux pwrite()
实现被破坏,将忽略偏移量并将数据附加到文件末尾 - see the BUGS section of the Linux man page)
在 windows(假设是 VisualStudio)上,您可以从 stdio FILE 句柄访问 OS 文件句柄。 从那里重新打开它并转换回新的文件句柄。
这只是 windows,但我认为 Andrews 的答案适用于 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;
}
我能够在 POSIX 系统上使用 pread 和 pwrite,并且我将 ReadFile/WriteFile 在 Windows 系统上包装成 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