c – Posix共享内存初始化

我的问题是关于初始化使用shm_open()和mmap()获得的内存.我在几个地方看到的一个常见建议是使用标志O_CREAT | O_EXCL调用shm_open():如果成功则我们是共享内存的第一个用户并且可以初始化它,否则我们不是第一个并且共享内存有已经被另一个进程初始化了.

但是,根据我对shm_open和我在Linux上进行的测试的理解,这不起作用:共享内存对象在系统中遗留下来,即使在共享内存对象的最后一个用户未映射和关闭之后也是如此.一个简单的测试程序使用O_CREAT | O_EXCL调用shm_open,然后关闭描述符并退出,将在第一次运行时成功,但在第二次运行时仍然会失败,即使当时没有其他人正在使用共享内存.

实际上在我看来(至少在我测试的系统上)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");
}

在这里,从属已准备好使用该段.由于主HAS要创建段,因此不需要在从站调用中使用O_CREAT标志.如果在尚未创建或已经销毁段的情况下执行从属调用,则只需处理可能的错误.

当您的任何进程完成该段时,它应该调用shm_unlink().在这种架构中,船长通常会为奴隶提供食物.当它没有什么可说的时候,它就会关闭.然后,奴隶有责任优雅地处理相应的错误.

如您所述,如果进程在调用shm_unlink过程之前死亡,则该段将在此后继续存在.为了在某​​些情况下避免这种情况,您可以定义自己的信号处理程序,以便在收到SIGINT等信号时执行操作.无论如何,如果将SIGKILL发送到您的流程,您将无法弥补这些混乱.

编辑:
更具体地说,使用O_CREAT | O_EXCL在不必要时是错误的.通过上面的小例子,您可以看到master需要创建段,因此需要这些标志.另一方面,任何从属进程都不必创建它.因此,您绝对禁止在相关调用中使用O_CREAT.

现在,如果另一个进程在已创建段时调用shm_open(…,O_CREAT,…),它将只检索与该段相关的文件描述符.因此它将位于正确的通道上(如果它有权这样做,请参阅mode参数)

点赞