不同平台上是否有pread的等效函数?

6
我正在使用C++编写一个并发持久化消息队列,需要对文件进行并发读取,但不能使用内存映射io。简而言之,多个线程将需要从文件的不同偏移量处进行读取。
最初,我有一个文件对象,其中包含典型的读/写方法,线程将获取互斥锁以调用这些方法。然而,某个地方我没有正确获取互斥锁,导致一个线程在读/写过程中移动了文件偏移量,另一个线程开始读/写文件的错误部分。
因此,谨慎的解决方案是为每个线程拥有一个打开的文件句柄。现在,我有很多文件句柄指向同一个文件,我认为这可能不太好。
我想使用类似于 pread 的东西,它允许将当前偏移量传递给读/写函数。
然而,该函数仅在Linux上可用,我需要在Windows、AIX、Solaris和HP-UX上实现相当的功能,有什么建议吗?

为什么你不能为其他平台编写自己的pread()函数呢?使用fseek()类型的函数似乎可以完成所有操作。 - Duck
2
@Duck:Snazzer想要的是一种可以在一个原子操作中查找和读取的东西。 - C. K. Young
手册没有说明pread()是否支持原子性的seek/read操作。是否有任何系统可以在常规文件上提供原子性的seek/read操作?即使使用pread(),他在调用之前仍需要提供自己的锁。 - Duck
2
pread函数将偏移量作为参数,因此我认为它是为了允许在共享文件句柄上进行原子寻址-读/写操作。搜索一番似乎表明是这样的...或者我有什么遗漏吗?否则,如果不能与多个线程一起使用,则pread几乎没有用处。 - Snazzer
@Snazzer:非常抱歉。这是原子操作,实际上非常酷。我需要再多玩一下。 - Duck
3个回答

8

看起来这个会起作用,似乎即使进行同步文件访问,重叠参数仍然被使用。谢谢jpalecek! - Snazzer

4

使用 NIO,java.nio.channels.FileChannel 类有一个 read(ByteBuffer dst, long position) 方法,它在内部使用 pread

哦等等,你的问题是关于 C++,而不是 Java。好吧,我刚刚查看了 JDK 源代码以查看它在 Windows 上是如何做到的,但是很遗憾,在 Windows 上它并不是原子性的:它只是先寻找,然后读取,然后再回到原来的位置。

对于 Unix 平台,重点是:pread 是任何支持 XSI(X/Open 系统接口)操作系统的标准函数:http://www.opengroup.org/onlinepubs/009695399/functions/pread.html


实际上,至少在2023年,FileChannel.read(dst, position)的Windows实现似乎只是保存文件指针,然后使用带有OVERLAPPED参数指示位置的ReadFile,最后恢复文件指针。但是带有位置的ReadFile是一个调用。因此它可以是原子的。失败可能会导致坏的文件指针,但如果没有使用它,则不清楚是否会有影响。 - squarewav

2

根据另一个答案,我能想到的最接近的答案是这个。然而,存在一个bug:ReadFile会改变文件偏移量,而pread保证不会改变文件偏移量。没有真正的方法来解决这个问题,因为代码可以在没有锁的情况下同时执行普通的read()和write()。有人找到了一个不会改变偏移量的调用吗?

unsigned int FakePRead(int fd, void *to, std::size_t size, uint64_offset) {
  // size_t might be 64-bit.  DWORD is always 32.
  const std::size_t kMax = static_cast<std::size_t>(1UL << 31);
  DWORD reading = static_cast<DWORD>(std::min<std::size_t>(kMax, size));
  DWORD ret;
  OVERLAPPED overlapped;
  memset(&overlapped, 0, sizeof(OVERLAPPED));
  overlapped.Offset = static_cast<DWORD>(off);
  overlapped.OffsetHigh = static_cast<DWORD>(off >> 32);
  if (!ReadFile((HANDLE)_get_osfhandle(fd), to, reading, &ret, &overlapped)) {
    // TODO: set errno to something?
    return -1;
  }
  // Note the limit to 1 << 31 before.
  return static_cast<unsigned int>(ret);
}

1
就此而言,我能够确认ReadFile()确实会改变文件偏移量,至少在Windows 7上是这样。我使用了SetFilePointerEx()来读取偏移量。 - kainjow

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