我的问题是关于初始化使用shm_open()
和mmap()
获得的内存。 我在几个地方看到的一个常见的build议是用标志O_CREAT|O_EXCL
调用shm_open()
:如果成功,那么我们是共享内存的第一个用户,并且可以初始化它,否则我们不是第一个共享内存已经被另一个进程初始化了。
然而,从我对shm_open
了解以及在Linux上所做的testing来看,这是行不通的:即使共享内存对象的最后一个用户没有被映射并closures,共享内存对象也会留在系统中。 一个简单的testing程序,用O_CREAT|O_EXCL
调用shm_open
,然后closures描述符并退出,在第一次运行时会成功,但在第二次运行时仍然会失败,即使当时没有人正在使用共享内存。
在我看来,(至less在我testing的系统上) shm_open
的行为与open()
是非常相似的:如果我修改我的简单testing程序来向共享内存写入内容(通过mmap
)并退出,然后共享内存对象将保持其内容持久(我可以运行另一个简单的程序来回读我以前写的数据)。
那么关于在O_CREAT|O_EXCL
使用shm_open
的build议是错的,还是我错过了一些东西?
我知道共享内存对象可以通过shm_unlink()
来移除,但似乎只会导致更多的问题:
如果一个进程在调用shm_unlink()
之前死亡,那么我们回到上面描述的问题。
如果一个进程调用shm_unlink()
而另一些进程仍然映射到同一个共享内存中,那么这些其他进程将照常继续使用。 现在,如果有另一个进程来调用shm_open()
具有相同的名称和指定的O_CREAT
,它实际上会成功地创build具有相同名称的新共享内存对象,这与其他进程仍在使用的旧共享内存对象完全无关。 现在我们有一个进程试图通过共享内存与其他进程通信,并且完全不知道它正在使用错误的通道。
我习惯了Windows语义,只要至less有一个句柄是开放的,共享内存对象就存在,所以这个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 | O_EXCL
在不必要时是错误的。 用上面的小例子,你可以看到master需要创建段,因此这些标志是必需的。 另一方面,从属进程都不需要创建它。 因此,你绝对禁止在相关调用中使用O_CREAT
。
现在,如果另一个进程在段已经被创建的时候调用shm_open(..., O_CREAT, ...)
,它只会检索与这个段相关的文件描述符。 这将是正确的渠道(如果它有权这样做,请参阅mode
参数)
您可以执行以下操作:int test = shmget(key_t key,size,0); 把这个放在每个进程的明星身上。 这里的零标志会尝试打开一个已经存在的共享内存,如果它没有被创建,测试将会等于-1,所以你可以在这个语句之后检查一下,如果test-1去创建一个共享内存,否则你只需要一个id到一个已经存在的共享内存…..我希望这个帮助