How to Correctly Cleanup and Re-Use Sysv Shared Memory Segments

How do you correctly cleanup and re-use SysV shared memory segments?

I was misled into thinking it was proper form to call shmctl(segmentId, IPC_RMID) as soon as the process designated as the owner has attached to the shared memory.

In fact IPC_RMID should not be called until all processes have attached.

Part of the answer is here:

https://comp.unix.programmer.narkive.com/iLg3PhfZ/shmctl-ipc-rmid-oddity

It seems that IPC_RMID sets the segment to private so that no new processes can attach to it.

A way of guaranteeing a unique segment is deliberately using IPC_PRIVATE to start with:

id = shmget(IPC_PRIVATE, IPC_CREAT | mode);

This also avoids the need to use ftok() and risk colliding with another segment.
Unfortunately I cannot use that here as the interface is predicated on identifying the segment with ftok(). At least I understand the issue here.

Someone wiser may be able to chip in with better ways of cleaning up before re-use.

See also https://www.linuxquestions.org/questions/programming-9/shmctl-ipc_rmid-precludes-further-attachments-574636/

Also consider this question and answer: Linux IPC: shared memory recovery

Deleting shared memory segment with shmctl

Your first reasoning is correct. The shared segment will exists until both: it is marked with IPC_RMID and last process detaches.

The purpose of the second fragment is to remind you, that in a solution using shared memory you need to have some process mark it for destruction or it will remain in memory/swap forever. It might be good idea to use IPC_RMID immediately after creating segment.

If you are not sure you have successfully released memory, you can use ipcs program to list remaining segments.

Relationship between shared memory and files

The path given to ftok is just a placeholder in the "everything is a file" tradition.

After some time considering I would say mmap is a simpler, safer and more effective API. I personally would avoid shmget etc. completely if possible.

It is particularly awkward to clean up after shmget() see:

  • Linux IPC: shared memory recovery

See also:

  • Linux shared memory: shmget() vs mmap()?
  • System V IPC vs POSIX IPC
  • How do you correctly cleanup and re-use SysV shared memory segments?

An argument used in favour of system-v in one of the linked questions is that Posix was less widely implemented. I don't know if that was true then but it seems even less likely to be true now. Given the dominance of GNU/Linux & BSD derivatives. Even the legendarily 'unique' AIX claims Posix compliance these days.

Linux IPC: shared memory recovery

It might be better idea to alter your producer design:

  • Instead of using IPC_CREAT it could first check if there is an existing segment that could be re-used.

  • You could also consider using mmap based shared memory instead which is more flexible in some ways.

  • You could use some other indicator such as a lock file to determine if the shared memory interface is still viable.

However, if for some reason these are not options (someone else controls the producer code for example) then read on.

There are several things you can do:

  1. use shmctl() to 'stat' your memory segment
 // return true if the shared memory region is still 'useful/useable'
bool checkShm(int shmId)
{
struct shmid_ds statBuf;
int res = shmctl(<shmid>, IPC_STAT, statBuf);
if (res == -1) return false;
...

  1. check if the region is marked for deletion (Linux specific)
 if ((statBuf.shm_perm.mode&SHM_DEST) != 0) return false;

  1. assuming you attached after the producer and it is the creator process - check that it dettached after you.
    caveat: It could have reattached again if your design allows this.
 if (statBuf.shm_cpid == shmBuf.shm_lpid) return false;

  1. check the PID of the creator process is a running process.
    caveat: the PID could be recycled by a new process
 if (getpgid(shmBuf.shm_cpid) == -1) return false;

note: you could use kill(shmBuf.shm_cpid,0) instead if the producer is not a different user.


  1. You might also want to check if the file has been modified.
    A key point is that ftok uses the inode number not the actual filename as the man page suggests. So you need to be careful using it:
struct stat fstatBuf;
int res = stat(fileName,&fstatBuf);
if (res == -1) return false; // if the file has disappeared it could be a bad sign!
if (fstatBuf.st_ino != savedInode) return false;

Having done all this you should now have a reasonably good way to check if the SHM you think is still useful is actually being used by the 'producer' you think it is.


  1. Clean up the stale shared memory segmant

You are now free to detach shmdt() from the segment, and try to clean it up shmctl(shmid,IPC_RMID,NULL). The consumer process might not have permissions to remove it if the creator did not grant them.


  1. Attach to the replacement shared memory segment

You are then in principle able to attach to any new shared memory segment created by a replacement producer process:

auto key = ftok(<somefile>,<someid>;
void* memArea = shmat(key,NULL,0);
// check errors and do stuff...

But there a cruel and interesting punishment awaits you. It will not work immediately. You have to wait a time and periodically retry. I guess this is until the operating system has had a chance to clean up the old memory segment.

I found that ftok() returns -1 for a while despite the file existing and having the same inode as the original file.

Remove posix shared memory when not in use?

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.



Related Topics



Leave a reply



Submit