POSIX共享存储器初始化
我的问题是关于初始化使用shm_open()
和mmap()
获得存储器。我在几个地方都看到一个共同的建议是呼吁shm_open()
与标志O_CREAT|O_EXCL
:如果成功那么我们是共享内存的第一个用户,并可以初始化它,否则我们不是第一,共享内存已经被初始化另一个过程。POSIX共享存储器初始化
但是,根据我对shm_open
的了解以及我在Linux上所做的测试,这样做不起作用:即使在共享内存对象的最后一个用户拥有共享内存对象后,共享内存对象仍留在系统中未映射并关闭。这就要求shm_open
与O_CREAT|O_EXCL
一个简单的测试程序,然后关闭描述符和退出,将在第一次运行成功,但仍然会失败的第二次运行,即使没有其他人使用当时的共享内存。
在我看来,(至少在我测试的系统上)shm_open
的行为与open()
的行为几乎完全相同:如果我修改我的简单测试程序来向共享内存写入内容(通过获得的指针由mmap
)并退出,然后共享内存对象将保持其内容持久(我可以运行另一个简单的程序来回读我以前写的数据)。
所以是关于使用shm_open
与O_CREAT|O_EXCL
只是错了,还是我失去了一些东西的建议吗?
我知道,共享内存对象可以用shm_unlink()
被删除,但似乎只会造成更多的问题:
如果一个进程调用
shm_unlink()
前去世,然后我们又回到了所描述的问题以上。如果一个进程调用
shm_unlink()
而其他一些进程仍然映射到同一个共享内存中,这些其他进程仍然照常继续使用它。现在,如果另一个进程来并调用shm_open()
具有相同的名称,并指定O_CREAT
,它实际上将具有相同的名称,这是完全无关的旧共享内存的其他进程仍在使用对象创建新的共享内存对象成功。现在我们有一个进程试图通过共享内存与其他进程通信,并且完全不知道它正在使用错误的通道。
我习惯了其*享内存对象只存在只要至少一个手柄处于打开位置,以它的Windows语义,所以这Posix的东西是非常混乱。
由于您使用的O_EXCL
标志,我会假设你有一组围绕一个主站(段的创建者)收集的过程。
然后,你的主进程将创建使用调用共享内存段shm_open
:
shmid = shm_open("/insert/name/here", O_CREAT|O_EXCL, 0644);
if (-1 == shmid) {
printf("Oops ..\n");
}
这里,奴隶们就可以使用该段。由于主设备要创建段,因此不需要在从设备调用中使用O_CREAT
标志。如果在段尚未创建或已被销毁时执行从设备调用,则只需处理可能的错误。
当您的任何流程都完成了该分段时,应该调用shm_unlink()
。在这种体系结构中,主设备通常会提供从设备。当它无话可说时,它就会关闭。那么奴隶就有责任优雅地处理相应的错误。
如您所述,如果一个进程在之前死亡程序调用shm_unlink
过程,那么该段将在此后继续存在。为了在某些情况下避免出现这种情况,您可以定义自己的信号处理程序,以便在收到诸如SIGINT
的信号时执行操作。无论如何,如果SIGKILL
已发送到您的流程,您将无法覆盖这些混乱情况。
编辑: 更具体地说,当不必要时使用O_CREAT | O_EXCL
是错误的。通过上面的小例子,您可以看到主设备需要创建段,因此需要这些标志。另一方面,任何从属进程都不必创建它。因此,您绝对禁止在相关调用中使用O_CREAT
。
现在,如果另一个进程在段已被创建时调用shm_open(..., O_CREAT, ...)
,它将只检索与该段相关的文件描述符。因此,这将是右声道(如果它有权利这样做,看到mode
参数)
感谢您的回复。然而,就我而言,这个过程并不知道它是一个“主”还是“奴隶”。这里的全部问题是找出它是哪一个。基本上,一旦进程映射到共享内存,它就需要回答“我是第一次?”这个问题。如果是,则调用初始化例程。因为一个进程可能会被终止(如果是SIGKILL,我们不能拦截它),但我们不能假设文件系统*享内存段的存在意味着什么。 –
我有一个解决方案,使用文件锁来确保正确的共享内存初始化(它实际上使用2个文件锁来避免某些竞争条件)。然而有一些疑虑,这又与异常进程终止有关。我在这里发布了相关问题:http://*.com/questions/19674214/resource-cleanup-on-abnormal-process-termination –
@YevgeniyP通常,信号量与共享内存一起使用以确保正确使用。但是,再一次,如果你的进程集中的一些被杀死(使用SIGKILL),你无法阻止它的良好行为。无论如何,我现在找不到任何解决方案... – Rerito
你可以做到以下几点: INT测试= shmget的(关键的key_t,大小,0);把它放在每个过程的明星身上。这里的零标志尝试打开一个现有的共享内存,如果它的未创建的测试将等于-1,所以你可以在这个语句后检查一下,如果test -1去创建一个共享内存,否则你只是得到一个ID到一个现有的共享内存.....我希望这可以帮助
它看起来像你没有设置你的shm段删除,当你完成它。因此,它需要你的过程。您可以在终端中使用'ipcs'命令来查看系统中当前存在哪种_IPC_(信号量,shm段等)。 – Rerito
但我如何设置删除?是否有一些标志传递给shm_open()?我在manpage上找不到任何东西。 –
@Rerito:关于评论评论的任何想法? – alk