user964970
user964970

Reputation:

Remove posix shared memory when not in use?

Is there any way, linux specific or not, to have posix shared memory segments (obtained with shm_open()) removed when no process is using them. i.e. have them reference counted and have the system remove them when the reference becomes 0

A few notes:

Upvotes: 16

Views: 13255

Answers (7)

Sebastian
Sebastian

Reputation: 4950

Let's assume the most complicated case:

  • You have several processes communicating via shared memory
  • They can start and finish at any time, even multiple times. That means there is no master process, nor is there a dedicated "first" process that can initialize the shared memory.
  • That means, e.g., there is no point where you can safely unlink the shared memory, so neither Sergey's nor Hristo's answers work.

I see two possible solutions and would welcome feedback on them because the internet is horribly silent on this question:

  1. Store the pid (or a more specific process identifier if you have one) of the last process that wrote to the shared memory inside the shared memory as a lock. Then you could do sth. like the following pseudo code:

     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
    

    This verifies that the last writer didn't die while writing. The shared memory is still persisting longer than any of your processes.

  2. Use a reader/writer file lock to find out if any process is the first process opening the shared memory object. The first process may then reinitialize the shared memory:

     // 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)
    

    File locks seem to be the only (?) mechanism on POSIX systems that is cleared up automatically when the process dies. Unfortunately, the list of caveats to use them is very, very long. The algorithm assumes flock is supported on the underlying filesystem at least on the local machine. The algorithm doesn't care if the locks are actually visible to other processes on NFS filesystems or not. They only have to be visible for all processes accessing the shared memory object.

    This solution has been implemented on top of boost.interprocess.

Upvotes: 3

Sergey
Sergey

Reputation: 31

For the shared memory, created using sysV API, it is possible to have such a behaviour. On Linux only. It is not POSIX shared memory, but may work for you.

In the book The Linux Programming Interface one of the possible parameters for shmctl() is described the following way.

IPC_RMID Mark the shared memory segment and its associated shmid_ds data structure for deletion. If no processes currently have the segment attached, deletion is immediate; otherwise, the segment is removed after all processes have detached from it (i.e., when the value of the shm_nattch field in the shmid_ds data structure falls to 0). In some applications, we can make sure that a shared memory segment is tidily cleared away on application termination by marking it for deletion immediately after all processes have attached it to their virtual address space with shmat(). This is analogous to unlinking a file once we’ve opened it. On Linux, if a shared segment has been marked for deletion using IPC_RMID, but has not yet been removed because some process still has it attached, then it is possible for another process to attach that segment. However, this behavior is not portable: most UNIX implementations prevent new attaches to a segment marked for deletion. (SUSv3 is silent on what behavior should occur in this scenario.) A few Linux applications have come to depend on this behavior, which is why Linux has not been changed to match other UNIX implementations.

Upvotes: 3

Bobax
Bobax

Reputation: 55

I found a way using a system command and the Linux command "fuser" which allow to list the processes which opened a file. This way, you can check if the shared memory file (located in /dev/shm") is still in use and delete it if not. Note that the operations of check / delete / create must be enclosed in a inter-processes critical section using a named mutex or named semaphore or file lock.

        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;
        }

Upvotes: 4

Hristo Iliev
Hristo Iliev

Reputation: 74495

If there is a point in your program's execution when it is well known, that all processes that need to open the shared memory segment have already done so, you can safely unlink it. Unlinking removes the object from the global namespace but it sill lingers around as long as there is at least one process that keep its file descriptor open. If a crash occurs after that point, the file descriptor is automatically closed and the reference count is decremented. Once no open descriptors to the unlinked shared memory block remain, it is deleted.

This is useful in the following scenario: a process creates a shared memory block, unlinks it and then forks. The child inherits the file descriptor and can use the shared memory block to communicate with the parent. Once both processes terminate, the block is automatically removed as both file descriptors get closed.

While unlinked, the shared memory block is unavailable for other processes to open it. Meanwhile, if one use shm_open() with the same name as the unlinked block, a new and completely different shared memory block would be created instead.

Upvotes: 8

nos
nos

Reputation: 229342

No - at lest on Linux, the kernel doesn't contain anything that can do this. It's up to some application to call shm_unlink() at some point to get rid of a shared memory segment.

Upvotes: 6

Whoami
Whoami

Reputation: 14438

Not sure, if the below works, or feasible. But my try.

Why do not you execute the helper program, which is executed each time your program crashed.

ie:

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

Myprogram is executed when process crashes, and probably you can explore further.

see

man 5 core.  for more information. 

Hope this helps to some extend.

Upvotes: 0

Joe
Joe

Reputation: 7818

Could you not just use a global counting semaphore to reference count? Wrap the attach and detach calls so that the semaphore is incremented when you attach to the memory and decremented when you detach. Release the segment when a detach reduces the semaphore to zero.

Upvotes: 0

Related Questions