我有一个小程序,其中包含我需要malloc的变量:
char **v;
v = (char**)malloc(sizeof(char *) * MAX_EVENTS);
for (int i = 0; i < MAX_EVENTS; i++)
v[i] = (char *)malloc(MAX_NAME_SIZE);
为了使 Valgrind 开心,为了避免任何内存泄漏,我设置了终止信号处理程序。该处理程序将简单地在退出之前释放该分配,并终止子进程。
static void term_handler() {
if (v != NULL) {
for (int i = 0; i < MAX_EVENTS; i++) {
if (v[i] != NULL)
free(v[i]);
}
free(v);
}
for (int i = 0; i < MAX_PROCS; i++)
if (children[i])
kill(children[i], SIGTERM);
exit(EXIT_SUCCESS);
}
要从处理程序访问v
,请将其作为全局变量。 children
是静态数组pid_t children[MAX_PROCS];
,但也可能已分配。
从处理程序访问这些分配的最干净的方法是什么?建议不要使用全局变量,也不建议使用内存泄漏和未正确终止的程序。 我是否应该将指向分配的指针数组作为全局变量保存?还是应该避免处理意外的信号?
答案 0 :(得分:4)
信号处理程序很棘手,因为它们被异步调用,因此只有small set of function calls可以从信号处理程序中安全调用。特别是,从信号处理程序中分配或释放内存是不可以的(就像调用exit()!一样),所以不要这样做。
但是,如果要确保释放内存(*),可以通过让信号处理程序“告诉”程序的主线程来退出内存来实现。然后,主线程可以退出其事件循环,释放内存,并执行退出前通常执行的其他任何清理工作。
那么问题就变成了,信号处理程序如何安全地告诉主线程执行受控/优美的退出?
如果主线程正在运行一个按固定时间表执行的事件循环(例如,每隔几毫秒),则声明一个全局变量(例如,getUserProfile
就像主线程在其上进行测试一样容易)在事件循环的每次迭代中,都将信号处理程序将该变量设置为不同的值,然后主线程将在下一次迭代中看到更改后的变量,并通过退出事件循环进行响应。
另一方面,如果主线程的事件循环是基于事件的(例如,它被阻塞在select()或poll()或类似内部,则调用将不会无限期地返回时间),那么唤醒主线程的另一种方法是在程序启动时创建一个pipe()或socketpair(),并让主线程监视两个文件描述符之一以获取就绪状态。然后,当信号处理程序运行时,它可以在另一个文件描述符上发送()一个字节,这将导致第一个文件描述符指示就绪状态。主线程可以通过中断其事件循环并正常退出来响应已准备就绪的状态。
除了避免异步信号不安全调用之外,这样做的好处是您只有一个shutdown / cleanup路径来测试/调试/维护,而不是两个。
(*)当然,在任何现代OS上,无论如何,都会通过OS的进程清理例程来释放内存。但是valgrind会抱怨内存泄漏,因此,如果可能的话,最好手动释放内存,以便您可以使用valgrind查找“真正的”内存泄漏,而不必每次都对一堆假阳性进行排序。>