我有一个简单的程序,它使用select
类似的东西来复用 IO。为了中断“服务器”进程,我集成了一个对SIGINT
做出反应的 sig_handler 。
每次分配内存时,包含方法都会释放自身或调用方法。
使用valgrind
显示,有一些分配没有被释放。
也许不需要它,但我想了解处理信号的最佳方法。按STRG + C
时
似乎free
没有调用调用。
所以用中断条件退出循环是没有意义的,这是我的第一种方法。
在关闭整个程序之前,有没有可能清理所有东西?
感谢您的任何提示和建议。
我有一个简单的程序,它使用select
类似的东西来复用 IO。为了中断“服务器”进程,我集成了一个对SIGINT
做出反应的 sig_handler 。
每次分配内存时,包含方法都会释放自身或调用方法。
使用valgrind
显示,有一些分配没有被释放。
也许不需要它,但我想了解处理信号的最佳方法。按STRG + C
时
似乎free
没有调用调用。
所以用中断条件退出循环是没有意义的,这是我的第一种方法。
在关闭整个程序之前,有没有可能清理所有东西?
感谢您的任何提示和建议。
Valgrind 只是一个查找内存泄漏的工具,而不是一个必须注意其建议的预言机。制作“Valgrind-clean”程序是一个有价值的目标,但不要让它失控。问自己一些有关该计划的问题。
SIGINT
当程序收到一个或什么时,它是否需要做任何SIGQUIT
事情?它是否需要进行某种干净的关机?例如,服务器可能决定完成所有打开请求的处理,或者至少向连接的客户端发送关闭消息。
突然终止是否总是留下某些障碍?然后,您可以从 Valgrind 取消这些报告,而无需花费额外的时间来释放已经将被释放的内存。
简单来说,free
调用一个即将退出的程序只有两个原因。
如果这是取消 Valgrind 消息的最简单方法(即,无需阅读 Valgrind 手册)
如果它使您的代码更简单。
否则,不要free
在程序退出期间调用,因为它只会消耗 CPU 周期。
处理 SIGINT:我可以想到四种处理 SIGINT 的常用方法:
使用默认处理程序。强烈推荐,这需要最少的代码,并且不太可能导致任何异常的程序行为。您的程序将简单地退出。
使用longjmp
立即退出。这是为喜欢不戴头盔骑快速摩托车的人准备的。这就像用图书馆电话玩俄罗斯轮盘赌。不建议。
设置一个标志,并中断主循环的pselect
/ ppoll
。做对是一件很痛苦的事,因为你必须摆弄信号掩码。你只想中断pselect
/ ,而不是像or这样ppoll
的不可重入函数,所以你必须非常小心信号掩码之类的东西。不建议。您必须使用/而不是/ ,因为“p”版本可以自动设置信号掩码。如果您使用or ,则在您检查标志之后但在您调用/之前,信号可能会到达,这很糟糕。malloc
free
pselect
ppoll
select
poll
select
poll
select
poll
创建一个管道以在主线程和信号处理程序之间进行通信。select
始终在对/的调用中包含此管道poll
。信号处理程序只是将一个字节写入管道,如果主循环成功地从另一端读取一个字节,则它干净地退出。强烈推荐。您还可以让信号处理程序自行卸载,因此不耐烦的用户可以点击CTRL+C
两次以立即退出。
两种最简单和最简单的方法是#1 和#4。
退出的程序没有任何泄漏。 只有正在运行的程序才会有泄漏。一旦程序退出,所有内存都被释放(因此不再有泄漏)。
这是我的简单且略显肮脏的解决方案。
#include <signal.h>
volatile bool gContinue;
void handleCtrlC(int ) {
gContinue = false;
}
int main () {
gContinue = true;
signal(SIGINT, handleCtrlC);
... allocate memory ...
sigset_t sigmask;
sigemptyset (&sigmask);
while (gContinue) {
/*...*/
ready = pselect(nfds, &readfds, &writefds, &exceptfds,
timeout, &sigmask);
/*...*/
}
... free memory ...
return 0;
}
编辑:将 pselect 添加到循环中。
亚当亨特的建议会奏效,前提是您在调用select
. 它必须紧接在之前,EINTR
否则您的正常处理可能会跳过检查,因此退出将被延迟,直到您的下一个select
返回真实事件。
while (gContinue) {
/*set up some stuff for next select call*/
do {
if (gContinue == 0) break;
nfds = select(...);
} while (nfds == -1 && errno == EINTR);
/*handle select return*/
}
编辑: Dietrich Epp 指出标志检查和select
调用之间存在竞争条件,只能通过调用pselect
. pselect
与 非常相似select
,主要区别在于最后一个参数用作掩码来确定要阻止哪些信号。pselect
因此,下面的代码示例结束了标志检查和调用之间的竞争:
sigset_t emptyset, blockset, origset;
sigemptyset(&emptyset);
sigemptyset(&blockset);
sigaddset(&blockset, SIGINT);
while (gContinue) {
/*...*/
sigprocmask(SIG_BLOCK, &blockset, &origset);
do {
if (gContinue == 0) break;
nfds = pselect(..., &emptyset);
} while (nfds == -1 && errno == EINTR);
sigprocmask(SIG_SETMASK, &origset, NULL);
/*...*/
};
另一种方法是将所有分配的元素注册到全局数据结构中,以便它们可以由atexit
您安装的处理程序释放。如果你的代码最终会释放它,首先从全局数据结构中注销它;
m = malloc(sz);
register_allocation(m);
/*...*/
unregister_allocation(m);
free(m);
并使用atexit
:
void cleanup_allocations () {
/*...*/
}
atexit(cleanup_allocations);