Posix共享内存初始化

我的问题是关于初始化使用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()来移除,但似乎只会导致更多的问题:

  1. 如果一个进程在调用shm_unlink()之前死亡,那么我们回到上面描述的问题。

  2. 如果一个进程调用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到一个已经存在的共享内存…..我希望这个帮助