Reputation: 33
I am currently implementing a small C function that uses posix shared memory (shm_open()
, ftruncate()
, mmap()
and shm_unlink()
.
The problem I am facing is that normaly my application exists gracefully and my cleanup routine calls shm_unlink()
.
But if my process got kill -9
I am facing the issue that the shm segment is still there. My application uses fork()
and can even be started in several instances. So, if during startup I detect there is a shared memory segment, how can I decide if it is a leftover from a crash, so I can reset it or some other process may be still using it?
In SystemV IPC shared memory set of function, once I do a shmget()
and a subsequent shmat()
I have shm_nattch
.
Is there something similar with posix shared memory?
Upvotes: 3
Views: 4099
Reputation: 439
POSIX shared memory is actually a variation of mapped memory. The major differences are to use shm_open() to open the shared memory object (instead of calling open()) and use shm_unlink() to close and delete the object (instead of calling close() which does not remove the object). The options in shm_open() are substantially fewer than the number of options provided in open().
IPC Shared Memory is an efficeint means of passing data between programs. One program will create a memory portion which other processes (if permitted) can access.
IMHO, the difference between them is not huge or even large since POSIX shared memory is just latter and more standard implementation of shared memory concept. The kill -9 problem is really problem. You cannot setup any handler and even atexit is not called. But you can set handler for SIGTERM and terminate your process with command:
kill -SIGTERM <pid>
Sample code for atexit and signal handler:
#include <signal.h>
#include <unistd.h>
#include <cerrno>
#include <system_error>
#include <iostream>
static sigset_t theMask;
static void
signalWrapper(
int theSignalNumber,
siginfo_t* theSignalDescription,
void* theUserContext)
{
if (theSignalNumber == SIGTERM)
{
std::cerr << "Clear shared memory and exit" << std::endl;
exit(1);
}
// Reinstall handler (usefull for other signals)
struct ::sigaction sa;
sa.sa_sigaction = &signalWrapper;
sa.sa_mask = theMask;
sa.sa_flags = SA_SIGINFO;
try
{
if (::sigaction(theSignalNumber, &sa, NULL) == -1)
throw std::error_code(errno, std::system_category());
}
catch (const std::error_code& ec)
{
std::cerr << ec << std::endl;
}
}
void
setupSignalHandlers()
{
struct ::sigaction sa;
// Prepare mask
sigemptyset(&theMask);
sigaddset(&theMask, SIGTERM);
// Add some more if you need it to process
sa.sa_mask = theMask;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = &signalWrapper;
// Perform setup
try
{
if (::sigaction(SIGTERM, &sa, NULL) == -1)
throw std::error_code(errno, std::system_category());
}
catch (const std::error_code& ec)
{
std::cerr << ec << std::endl;
}
}
void
bye()
{
std::cout << "Bye!" << std::endl;
}
int
main()
{
std::cout << "Set handler!" << std::endl;
setupSignalHandlers();
if (std::atexit(bye))
{
std::cerr << "Failed to register atexit" << std::endl;
return 2;
}
std::cout << "Emit SIGTERM signals" << std::endl;
kill(getpid(), SIGTERM);
sleep(100);
return 0;
}
Another interesting approach is shm_unlink() right after shm_open() and use of O_CLOEXEC. I wrote small code sample to illustrate use of unlink and POSIX shared memory reopen:
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cerrno>
#include <cstring>
#include <system_error>
#include <iostream>
// Simple ShMem with RAII
struct ShMem
{
ShMem(
const std::string& theName)
: _handle(-1),
_mode(S_IRUSR | S_IWUSR),
_name(theName)
{
// Here we try to create the object with exclusive right to it
// If the object exists - it must fail.
// File opened as 0600. The open mode might be set with
// class enum and additional paramter to constructor
if ((_handle = shm_open(_name.c_str(), O_CREAT | O_RDWR | O_EXCL, _mode)) < 0)
{
std::cerr << strerror(errno) << std::endl;
std::cout << "File " << _name << "exists. Try to reopen it!" << std::endl;
if ((_handle = shm_open(_name.c_str(), O_RDWR | O_TRUNC, _mode)) < 0)
{
std::cerr << strerror(errno) << std::endl;
throw std::error_code(errno, std::system_category());
}
else
std::cout << _name << " reopened OK";
}
else
std::cout << _name << " created OK";
// Unlink but keep descriptor
shm_unlink(_name.c_str());
}
~ShMem()
{
}
const int
handle()
{
return _handle;
}
private:
int _handle;
mode_t _mode;
std::string _name;
};
With O_CLOEXEC you can remove shm_unlink() at all and see what happens on kill -9 .
Upvotes: 3