英文:
How to delete shared memory when ctrl c?
问题
我编写了一个shm结构如下:
class ShmWorker {
public:
ShmWorker() = default;
virtual ~ShmWorker() {
shmdt(m_data);
shmctl(shmid, IPC_RMID, 0); // 这里是问题所在,如果使用Ctrl-C终止进程,共享内存
// 将不会被删除
}
protected:
key_t get_keyid(const std::string & name) {
const std::string & s = "./" + name;
if (access(s.c_str(), F_OK) == -1) { const std::string & s1 = "touch " + s; system(s1.c_str()); }
key_t semkey = ftok(name.c_str(), 1);
CE(semkey == -1, "shm_file:%s 不存在\n", s.c_str());
return semkey;
}
template <typename T>
void init(const std::string& name, int size) {
key_t m_key = get_keyid(name);
shmid = shmget(m_key, 0, 0); // TODO: 检查m_key是否存在
char * p;
if (shmid == -1) {
CE(errno != ENOENT && errno != EINVAL, "errno 是 %s\n", strerror(errno));
if (std::is_same_v<T, std::string>) shmid = shmget(m_key, sizeof(Header) + STRING_SIZE * size, 0666 | IPC_CREAT | O_EXCL);
else shmid = shmget(m_key, sizeof(Header) + sizeof(T) * size, 0666 | IPC_CREAT | O_EXCL);
CE(shmid == -1, "shm 的连接和创建都失败了\n")
printf("正在创建新的shm %s %p\n", name.c_str(), p);
p = (char*)shmat(shmid, NULL, 0);
Header h(size, 0);
memcpy(p, &h, sizeof(Header));
} else {
p = (char*)shmat(shmid, 0, 0);
printf("已存在的m_data = %p\n", p);
}
}
}
正如你所见,析构函数将会分离这个共享内存并删除它。
但有时我需要使用Ctrl-C来结束进程。在这种情况下,共享内存将不会被删除。
是否有一种方法可以在没有活动进程链接到共享内存时安全地删除它?
或者在下次链接时删除它?
英文:
I write a shm struct as following:
class ShmWorker {
public:
ShmWorker() = default;
virtual ~ShmWorker() {
shmdt(m_data);
shmctl(shmid, IPC_RMID, 0); // here is the problem, if ctrl-c, the shared memory
// won't be deleted
}
protected:
key_t get_keyid(const std::string & name) {
const std::string & s = "./" + name;
if (access(s.c_str(), F_OK) == -1) { const std::string & s1 = "touch " + s; system(s1.c_str()); }
key_t semkey = ftok(name.c_str(), 1);
CE(semkey == -1, "shm_file:%s not existed\n", s.c_str());
return semkey;
}
template <typename T>
void init(const std::string& name, int size) {
key_t m_key = get_keyid(name);
shmid = shmget(m_key, 0, 0); // TODO: check m_key existed
char * p;
if (shmid == -1) {
CE(errno != ENOENT && errno != EINVAL, "errno is %s\n", strerror(errno));
if (std::is_same_v<T, std::string>) shmid = shmget(m_key, sizeof(Header) + STRING_SIZE * size, 0666 | IPC_CREAT | O_EXCL);
else shmid = shmget(m_key, sizeof(Header) + sizeof(T) * size, 0666 | IPC_CREAT | O_EXCL);
CE(shmid == -1, "both connet and create are failed for shm\n")
printf("creating new shm %s %p\n", name.c_str(), p);
p = (char*)shmat(shmid, NULL, 0);
Header h(size, 0);
memcpy(p, &h, sizeof(Header));
} else {
p = (char*)shmat(shmid, 0, 0);
printf("existed m_data = %p\n", p);
}
}
}
}
as you can see, the deconstructor will detach this shared memory and delete it.
but sometimes i need to ctrl-c to end the process. in that case, the shared memory will not be deleted.
is there any method can delete it safely if no live process linked the shared memory?
or delete it in the next time link?
答案1
得分: 2
Ctrl-C和类似的按键在某些环境中由终端处理,并在其上调用适当的操作,例如通过调用kill
。在其他情况下,它由运行时库处理。您需要处理中断。
例如,在兼容POSIX的环境中(也包括Windows上的控制台MinGW应用程序),Ctrl-C可以被拦截为SIGINT信号:
#include <iostream>
#include <thread>
#include <atomic>
#include <signal.h>
std::atomic<int> keepRunning;
void interruptHandler(int signum) {
// 如果拦截多个信号,应在此处执行检查
// 设置一些标志以通知主程序退出运行。
keepRunning = 0;
}
// "主要工作" 线程。
void threadFunction() {
std::cout << "Hello, world!" << std::endl;
std::cout << "按Ctrl-C退出。" << std::endl;
while (keepRunning);
// 在这里调用exit()会导致terminate调用,但会调用atexit处理程序
std::cout << "检测到中断。" << std::endl;
}
std::thread *infiniteThread;
void exitHandler() {
std::cout << "再见,残酷的世界。" << std::endl;
delete infiniteThread;
}
int main()
{
signal(SIGINT, interruptHandler);
std::atexit(exitHandler);
keepRunning = 1;
infiniteThread = new std::thread(threadFunction);
infiniteThread->join();
}
interruptHandler
应该执行某些操作以使进程正常结束。这可能包括关闭文件、套接字,释放其他与操作系统相关的资源,这些资源不是根据操作系统规范正常释放的,调用atexit
处理程序。
并非所有信号都可以捕获,当操作系统实际终止进程时,它没有那个正常结束的时间。interruptHandler
从所谓的信号跳板中调用,因此不建议在那里引发异常。参见manpages。
在处理程序中可以使用的内容是有限的。在POSIX环境中,从信号处理程序中调用exit()
根据安全函数列表来看是不安全的。但 _exit()
或 _Exit()
不会调用任何atexit
函数,它们只会执行与初始化相关的清理工作。
英文:
Ctrl-C and similar keystrokes in some environments are processed by terminal and terminal invokes appropriate actions upon them, e.g. by calling kill
. In other cases it's handled by run-time library. You have to handle interrupts.
E.g. in POSIX-compatible (which also includes console MinGW applications on Windows) Ctrl-C can be intercepted as SIGINT signal:
#include <iostream>
#include <thread>
#include <atomic>
#include <signal.h>
std::atomic<int> keepRunning;
void interruptHandler(int signum) {
// if intercepting several signals, we should do checks here
// setting some flag to signal main program to quit running.
keepRunning = 0;
}
// The "main worker" thread.
void threadFunction() {
std::cout << "Hello, world!" << std::endl;
std::cout << "Press Ctrl-C to exit." << std::endl;
while(keepRunning);
// calling exit() here would result in terminate call but atexit handlers will be invoked
std::cout << "Interrupt detected." << std::endl;
}
std::thread *infiniteThread;
void exitHandler() {
std::cout << "Bye, cruel world." << std::endl;
delete infiniteThread;
}
int main()
{
signal(SIGINT, interruptHandler);
std::atexit(exitHandler);
keepRunning = 1;
infiniteThread = new std::thread(threadFunction);
infiniteThread->join();
}
interruptHandler
should do something to cause process end gracefully. Which may include closing files, sockets, freeing other OS-related resources which aren't under normal circumstances by OS specs, calling atexit
-handlers.
Not all signals can be caught, when OS actually kills the process, it doesn't have that grace time. interruptHandler
is called from what is known as signal trampoline, so provoking exceptions there is not recommended.
See manpages.
What can be used in handler is limited. In POSIX environment it's not safe to call exit()
from signal handler according to the list of safe functions. But _exit()
or _Exit()
wouldn't call any atexit
functions, it would do only init-related cleanup.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论