16

是否有任何方法,无论是否特定于 linux,shm_open()在没有进程使用它们时删除 posix 共享内存段(使用 获得)。即对它们进行引用计数,并在引用变为 0 时让系统删除它们

几点注意事项:

  • 如果程序崩溃,建立一个 atexit 处理程序来删除它们不起作用。

  • 目前,linux特有的方式,我将pid嵌入到段名中,并尝试通过在外部程序中走/dev/shm来找到未使用的段。其缺点是必须定期以一种相当骇人的方式从外部清理它们。

  • 由于程序可以运行多个副本,因此为程序在启动时重用的段使用定义明确的名称是不可行的。

4

7 回答 7

7

如果您的程序执行中有一个众所周知的点,即所有需要打开共享内存段的进程都已经这样做了,您可以安全地取消链接它。取消链接会将对象从全局命名空间中移除,但只要至少有一个进程保持其文件描述符处于打开状态,它就会一直存在。如果在那之后发生崩溃,文件描述符会自动关闭,并且引用计数会减少。一旦没有打开未链接共享内存块的描述符,它就会被删除。

这在以下场景中很有用:进程创建一个共享内存块,取消链接,然后分叉。子进程继承文件描述符,可以使用共享内存块与父进程通信。一旦两个进程都终止,该块将在两个文件描述符都关闭时自动删除。

未链接时,共享内存块无法供其他进程打开。同时,如果使用shm_open()与未链接块相同的名称,则会创建一个新的完全不同的共享内存块。

于 2012-11-18T22:17:10.547 回答
5

不——至少在 Linux 上,内核不包含任何可以做到这一点的东西。某些应用程序需要在某个时候调用 shm_unlink() 以摆脱共享内存段。

于 2012-11-14T17:25:21.610 回答
4

我找到了一种使用系统命令和 Linux 命令“fuser”的方法,它允许列出打开文件的进程。这样,你可以检查共享内存文件(位于/dev/shm")是否还在使用,如果没有就删除它。注意检查/删除/创建操作必须包含在进程间临界区中使用命名互斥锁或命名信号量或文件锁。

        std::string shm_file = "/dev/shm/" + service_name + "Shm";
        std::string cmd_line = "if [ -f " + shm_file + " ] ; then if ! fuser -s " + shm_file + " ; then rm -f " + shm_file + " ; else exit 2 ; fi else exit 3 ; fi";
        int res = system(cmd_line.c_str());
        switch (WEXITSTATUS(res)) {
        case 0: _logger.warning ("The shared memory file " + shm_file + " was found orphan and is deleted");         break;
        case 1: _logger.critical("The shared memory file " + shm_file + " was found orphan and cannot be deleted");  break;
        case 2: _logger.trace   ("The shared memory file " + shm_file + " is linked to alive processes");            break;
        case 3: _logger.trace   ("The shared memory file " + shm_file + " is not found");                            break;
        }
于 2013-05-12T13:53:34.100 回答
3

对于使用 sysV API 创建的共享内存,可能会有这样的行为。仅在 Linux 上。它不是 POSIX 共享内存,但可能对您有用。

The Linux Programming Interface一书中,shmctl() 的可能参数之一描述如下。

IPC_RMID 将共享内存段及其关联的 shmid_ds 数据结构标记为删除。如果当前没有进程附加该段,则立即删除;否则,在所有进程都与它分离后(即,当 shmid_ds 数据结构中的 shm_nattch 字段的值降至 0 时),该段被删除。在某些应用程序中,我们可以确保在应用程序终止时整齐地清除共享内存段,方法是在所有进程都使用 shmat() 将其附加到其虚拟地址空间后立即将其标记为删除。这类似于打开文件后取消链接。在 Linux 上,如果已使用 IPC_RMID 将共享段标记为删除,但尚未删除,因为某些进程仍然附加它,那么另一个进程可以附加该段。然而,这种行为是不可移植的:大多数 UNIX 实现阻止新的附加到标记为删除的段。(SUSv3 对在这种情况下应该发生什么行为保持沉默。)一些 Linux 应用程序已经开始依赖这种行为,这就是为什么 Linux 没有被更改以匹配其他 UNIX 实现的原因。

于 2016-02-18T15:06:38.407 回答
3

让我们假设最复杂的情​​况:

  • 您有多个进程通过共享内存进行通信
  • 他们可以随时开始和结束,甚至可以多次。这意味着没有主进程,也没有可以初始化共享内存的专用“第一个”进程。
  • 这意味着,例如,您无法安全地取消链接共享内存,因此SergeyHristo 的答案都不起作用。

我看到了两种可能的解决方案,并欢迎对它们提供反馈,因为互联网对这个问题非常沉默:

  1. 将写入共享内存中的共享内存的最后一个进程的 pid(或更具体的进程标识符,如果有的话)存储为锁。然后你可以做某事。像下面的伪代码:

     int* pshmem = open shared memory()
    
     while(true) 
         nPid = atomic_read(pshmem)
         if nPid = 0 
            // your shared memory is in a valid state
            break
         else 
            // process nPid holds a lock to your shared memory
            // or it might have crashed while holding the lock
            if process nPid still exists 
              // shared memory is valid
              break
            else 
              // shared memory is corrupt
              // try acquire lock 
              if atomic_compare_exchange(pshmem, nPid, my pid) 
                 // we have the lock
                 reinitialize shared memory
                 atomic_write(pshem, 0) // release lock
              else 
                 // somebody else got the lock in the meantime
                 // continue loop
    

    这验证了最后一位作家在写作时没有死。共享内存的持续时间仍然比您的任何进程长。

  2. 使用读取器/写入器文件锁来确定是否有任何进程是第一个打开共享内存对象的进程。然后第一个进程可能会重新初始化共享内存:

     // try to get exclusive lock on lockfile
     int fd = open(lockfile, O_RDONLY | O_CREAT | O_EXLOCK | O_NONBLOCK, ...)
     if fd == -1
         // didn't work, somebody else has it and is initializing shared memory
         // acquire shared lock and wait for it
         fd = open(lockfile, O_RDONLY | O_SHLOCK)
         // open shared memory
     else 
         // we are the first
         // delete shared memory object
         // possibly delete named mutex/semaphore as well
    
         // create shared memory object (& semaphore)
         // degrade exclusive lock to a shared lock
         flock(fd, LOCK_SH)
    

    文件锁似乎是 POSIX 系统上唯一(?)在进程终止时自动清除的机制。不幸的是,使用它们的注意事项列表非常非常长。该算法假设flock至少在本地机器上支持底层文件系统。该算法不关心锁是否对 NFS 文件系统上的其他进程实际可见。它们只需对访问共享内存对象的所有进程可见。

    此解决方案已在 boost.interprocess 之上实施。

于 2016-09-23T11:37:29.230 回答
0

你不能只使用全局计数信号量来引用计数吗?包装附加和分离调用,以便信号量在附加到内存时递增,在分离时递减。当分离将信号量减少到零时释放段。

于 2012-11-14T12:05:51.573 回答
0

不确定,如果以下工作,或可行。但我的尝试。

为什么不执行每次程序崩溃时都会执行的帮助程序。

IE:

/proc/sys/kernel/core_pattern  to  /path/to/Myprogram %p

Myprogram 在进程崩溃时执行,可能您可以进一步探索。

man 5 core.  for more information. 

希望这在一定程度上有所帮助。

于 2012-11-14T12:37:18.360 回答