POSIX 共享内存实际上是映射内存的一种变体。主要区别在于使用 shm_open() 打开共享内存对象(而不是调用 open())和使用 shm_unlink() 关闭和删除对象(而不是调用不删除对象的 close())。 shm_open() 中的选项比 open() 中提供的选项数量要少很多。
IPC 共享内存是一种在程序之间传递数据的有效方式。一个程序将创建一个内存部分,其他进程(如果允许)可以访问。
恕我直言,它们之间的差异并不大,甚至不是很大,因为 POSIX 共享内存只是共享内存概念的后期和更标准的实现。 kill -9 问题确实是个问题。您不能设置任何处理程序,甚至不调用 atexit。但是您可以为 SIGTERM 设置处理程序并使用命令终止您的进程:
kill -SIGTERM <pid>
atexit 和信号处理程序的示例代码:
#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;
}
另一个有趣的方法是在 shm_open() 之后使用 shm_unlink() 并使用 O_CLOEXEC。我写了一个小代码示例来说明 unlink 和 POSIX 共享内存重新打开的使用:
#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;
};
使用 O_CLOEXEC 您可以完全删除 shm_unlink() 并查看 kill -9 会发生什么。