处理进程间大文件的mmap和fread

4
我有两个进程: 进程A正在将一个大文件(~170 GB - 内容不停变动)通过 MAP_NONBLOCKMAP_SHARED 标记映射到内存中进行写入。
MyDataType *myDataType; = (MyDataType*)mmap(NULL, sizeof(MyDataType), PROT_WRITE, MAP_NONBLOCK | MAP_SHARED , fileDescriptor, 0);

每秒钟我都调用msync:

msync((void *)myDataType, sizeof(MyDataType), MS_ASYNC); 

这个部分工作正常。当进程B试图从进程A映射的同一文件中读取时,进程A不会响应约20秒钟。进程B正在尝试使用fread()fseek()读取文件大约1000次,每次使用小块(每次大约4字节)。该进程正在读取的大部分内容都相互靠近。
这个问题的原因是什么?与页面分配有关吗?如何解决?
顺便说一下,当我在进程B中使用mmap()而不是简单的fread()时,会出现同样的问题。

当进程A未响应时,vmstat是否显示任何异常情况? - Andrew Henle
1
它没有显示任何东西 - 我的服务器有800GB的RAM。 - ItayB
1
这是相当多的东西,只需将文件移动到内存磁盘中就可以了;)) - Ctx
3个回答

4
msync()可能是问题所在。它会强制系统写入磁盘,导致内核陷入写入狂潮的阻塞状态。 通常在Linux上(顺便说一下,在Solaris上也是一样),过度使用msync()是不明智的。没有必要为了在内存映射和read()/write() I/O操作之间同步数据而调用msync(),这是一个来自过时HOWTO的误解。实际上,mmap()只是使文件系统缓存对进程“可见”。这意味着进程更改的内存块仍然受内核控制。即使您的进程崩溃,更改最终也会落到磁盘上。其他进程也将继续由同一缓冲区提供服务。
在这里有关于该主题的另一个答案mmap, msync and linux process termination。有趣的部分是链接到一个讨论,Linus Torvalds本人在其中解释了缓冲高速缓存和内存映射的工作原理。
PS:fseek()/fread()配对也可能更好地被pread()替换。1个系统调用总是比2个更好。此外,fseek()/fread()读取始终为4K并将其复制到缓冲区中,因此如果您有多个小读取而没有fseek(),它将从本地缓冲区读取,可能会错过进程A中的更新。

优秀的回答。您还可以添加dat MAP_NONBLOCK在这里没有意义。 - joop
1
我尝试从代码中删除msync()行,但问题仍然存在。感谢提示,我将尝试使用pread()。 - ItayB

0

这听起来像是您遇到了IO饥饿问题,与您选择的方法(mmap还是fread)无关。您需要改善您的(预)缓存策略和/或尝试其他IO调度程序(默认情况下为cfq,也许deadline对您来说能提供更好的整体结果)

您可以通过写入/sys来更改调度程序:

echo deadline > /sys/block/<device>/queue/scheduler

解决这种问题确实不容易;可能需要采取一些措施(例如使用带有BBU的RAID控制器进行写回和更快的磁盘),以进一步提高性能。 - Ctx

0
也许您可以尝试使用 Profiling 或者使用 strace 工具来确定进程占用时间的确切位置。在 msync() 中,20 秒似乎非常长,无法仅仅通过 io 来解释。
当您说 A 没有响应时,您具体指的是什么?

1
我有额外的进程向进程A发送保持活动消息。当进程B尝试从文件中读取(即使我只调用fseek),进程B停止响应保持活动,尽管我可以看到它仍在运行(进程的主循环继续打印日志)。 - ItayB
在您的OP中,您说“进程A在大约20秒内没有响应”。这里您指的是进程B。看起来B是多线程的(来自主循环的日志)。设置如何以响应保持连接的形式运行?您是否在进程(不响应的那个)上运行了strace?我会为停顿期间获得跟踪,然后为停顿+不响应保持活动状态获得跟踪。 - Ziffusion

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