我有两个过程: 进程A将大文件(~170 GB - 内容不断变化)映射到内存中,使用标记 MAP_NONBLOCK 和 MAP_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()
小块(每次约4个字节)从文件中读取1000次。
该过程正在阅读的大部分内容彼此接近。
这个问题的原因是什么?它与页面分配有关吗?我该如何解决?
顺便说一句,当我在流程B中使用mmap()
而不是简单fread()
时,会出现同样的问题。
答案 0 :(得分:4)
msync()
可能是问题所在。它强制系统写入磁盘,在写入狂热中阻止内核。
通常在Linux上(在Solaris BTW上它是相同的),经常使用msync()
是个坏主意。没有必要调用msync()
来实现内存映射和read()/write()
I / O操作之间的数据同步,这是一种来自过时的HOWTO的误解。实际上,mmap()
仅使文件系统缓存"可见"一个过程。这意味着内存块阻止进程更改仍在内核控制之下。即使您的进程崩溃,更改也会最终落在磁盘上。其他进程仍然可以由相同的缓冲区提供服务。
这里有关于这个问题的另一个答案mmap, msync and linux process termination 有趣的部分是关于realworldtech讨论的链接,其中Linus Torvalds自己解释了缓冲区缓存和内存映射的工作原理。
PS:fseek()/fread()
对也可能更好地被pread()
取代。 1系统调用始终优于2.同时fseek()/fread()
读取总是4K并在缓冲区中复制,因此如果您有几个没有fseek()
的小读取,它将从其本地缓冲区读取并可能错过更新过程A.
答案 1 :(得分:0)
这听起来你正在遭受IO-Starvation,这与你选择的方法(mmap或fread)无关。你必须改进你的(预)缓存策略和/或尝试另一个IO调度程序(cfq是默认的,也许截止日期为你提供更好的整体结果)
您可以通过写入/ sys:
来更改调度程序SELECT a.country_name, s.state_name, c.city_id,
LEAST (c.next_1, c.next_2, c.next_3) AS next_visit,
MAX(v.visit_time) AS last_visit
FROM city c
INNER JOIN country a ON a.id = c.country
INNER JOIN state s ON s.id = c.state
INNER JOIN visit_log v ON CONCAT(c.country, c.state, c.city_id) = CONCAT(v.country, v.state, v.city_id)
GROUP BY CONCAT(v.country, v.state, v.city_id)
ORDER BY a.id ASC, s.id ASC, c.city_id
答案 2 :(得分:0)
也许你应该尝试分析甚至使用strace来确定进程花费时间的位置。 20秒在msync()
中被io解释似乎非常漫长。
当你说A没有回应时,你究竟是什么意思?