在shm_open和mmap之间使用fstat

3
我正在将现有的Win32代码移植到Linux。在Windows上,我有一个“主”进程,“读取器”,它创建一个共享内存对象然后等待一些“从”进程,“编写器”将数据放入共享内存中进行处理。
主进程:Win32实现依赖于CreateFileMapping(INVALID_HANDLE_VALUE,[...]然后是MapViewOfFile。共享内存的大小在CreateFileMapping调用中指定。将0作为MapViewOfFile的最后一个参数传递可以确保映射所有共享内存。在Linux上,经过一些搜索后,我得出结论应该使用shm_open + ftruncate + mmap
从进程:Win32实现与主进程几乎相同,只是CreateFileMappingOpenFileMapping取代,并且可以使用VirtualQuery获取共享内存的大小。
在Linux上,我有一个问题:从进程必须以某种方式“等待”主进程中的ftruncate调用完成。它们不能自己执行ftruncate,因为它们还不知道共享内存的大小。
从进程在shm_openmmap之间是否轮询fstat是否可以呢?或者这是一种不好的做法,如果是,是否有另一种映射“正确”大小的方法?
编辑:
目前,我不想直接使用文件系统。我喜欢使用“名称”创建一个“命名共享内存对象”,该名称将在两个平台上运行,如"/MySharedMemName42",并且不想关心文件的位置。如果看起来不现实,我可能会改变主意。
我知道主进程和从进程在使用共享内存时必须互相配合。他们通过写入/读取内存来达到这一点。 “问题”在于shm_open / mmap可能导致从进程中的SIGBUS(ftruncate在主进程中晚)发生竞争。我测试了“fstat轮询”的效果,但想知道它是否被视为可怕的黑客行为,还是处理竞争的正确方法。

дҪ еҰӮдҪ•и§ЈеҶідё»зЁӢеәҸдёӯзҡ„CreateFileMappingе’Ңд»ҺзЁӢеәҸдёӯзҡ„OpenFileMappingд№Ӣй—ҙзҡ„з«һдәүе…ізі»пјҹ - chill
@chill 是哪个种族?如果OpenFileMapping失败,从属者们会想怎么做(重试?)。如果OpenFileMapping成功,他们可以毫无顾虑地进行MapViewOfFile。 - manuell
任何系统的关键在于协同进程。一些系统在每天的开始和结束时拥有单独的进程,以在应用程序(服务或客户端)运行之前创建和取消链接它们的共享内存。一些系统有机制(如文件锁、信号量等),以协调进程的有序加载,例如没有服务器则不会运行客户端。 - Duck
@duck 如果我能避免日初/日终的清理工作,我将非常感激。 - manuell
2个回答

2
如果我理解正确,Windows的CreateFileMapping调用是UNIX stanza“shm_open+ftruncate+maybe-mmap”的原子版本,因此在UNIX上存在客户端可以在共享内存被正确调整大小之前访问它的可能性。
因此,您提议使用fstat是可以的。它通过POSIX共享内存选项得到官方支持。您的设计似乎容忍轮询共享内存区域的存在,因此轮询其正确大小(或至少st_size > 0)也应该没问题。
另外,您可以在打开共享内存区域时尝试使用模式(O_CREAT|O_EXCL和0600)进行操作,并在准备好后使用fchown进行控制。根据UID和GID,您的下级进程将因缺少内存而失败并显示ENOENT或EACCES。这是一种"轮询"选项。您还可以在调整大小后重命名共享内存本身(通过Linux的/dev/shm/),但这是一种非POSIX便利方式。

(如果您的下级进程是主进程的子进程,则可以依靠那个古老的*NIX技术——进程继承并切换到未命名的共享内存...)


即使有权限,仍然存在竞态条件。关键是如果 size == 0(在新创建的段上),那么客户端必须退避(以某种方式)或退出。或者通过不在没有服务器的情况下运行客户端来完全避免这种情况。 - Duck
CreateFileMapping是"shm_open+ftruncate"的原子版本。mmap使用MapViewOfFile完成。谢谢。 - manuell
@Duck,你指的是哪个进程?如果从属进程在ftruncate()之后才能成功地shm_open()段,则不存在竞争关系。(主进程必须独占地创建shm对象。) - pilcrow
@pilcrow,我看了您的答案并在走出门时发表了评论,现在我很难重构我的思路。这似乎是我目前可以生成的所有情况的好解决方案。 - Duck

0

你在两个平台上都有相同的问题 - 你需要从属进程等待主进程来设置共享区域。

对于Windows情况,你需要从属进程尝试打开一个具有特定已知名称的文件映射,并且如果不存在带有该名称的映射,则重试。

虽然不是理想的解决方案,但你可以在Linux上执行相同操作。

请注意,你不一定需要POSIX共享内存对象。你可以使用普通文件。 首先,使用普通open(2)ftruncate(2)创建并设置文件大小,但使用一个临时唯一随机生成的名称。 然后,使用rename(2)将该文件从临时名称更改为已知名称。


我的Windows从机在与主机竞争打开对象时,无法因内存违规而崩溃。您的重命名想法适用于POSIX共享内存对象吗? - manuell
虽然这两个平台存在相同的竞态条件,但我不确定你对文件想法的看法是什么。 - Duck
@Duck 我在win32版本中没有看到导致崩溃的竞争情况。 - manuell
@manuel,我不明白为什么这一定会在posix上导致崩溃。最坏的情况下,您应该在尝试映射零长度时失败并返回EINVAL。 - Duck
@Duck 因为如果我不使用fstat轮询,我必须使用已知的非空大小(比如:某些“头”的大小)进行mmap,如果主进程尚未执行ftruncate,则从进程将会收到SIGBUS信号。 - manuell

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