最全C/C++安全编码规范之三:安全退出
本文介绍了进程安全退出的五个推荐规范:1. 终止进程前应发送通知信号,给被终止进程留出清理时间;2. 避免使用pthread_exit和ExitThread,应通过return安全退出线程;3. 除致命错误外,不应使用abort函数;4. atexit函数仅应用于错误定位,而非资源清理;5. 仅在main函数中使用exit/ExitProcess。这些规范旨在确保进程和线程能安全释放资源,避免内存
【关注我,后续持续新增专题博文,谢谢!!!】
上一篇我们讲了:
这一篇我们开始讲: 最全C/C++安全编码规范之三:安全退出
目录
4.1【推荐】使用kill、TerminateProcess函数终止其他进程前需要通知和等待
4.2【推荐】勿用pthread_exit、ExitThread函数
4.5【推荐】勿在main函数之外的函数中使用exit、ExitProcess
四、 安全退出
4.1【推荐】使用kill、TerminateProcess函数终止其他进程前需要通知和等待
-
场景介绍
在进程终止某个其他进程时,若直接使用kill或者TerminateProcess函数,会强行终止,没有给被终止的进程清理资源的时间。应该在使用这些函数之间先用其他信号进行通信,给被终止的进程一点清理时间。
-
风险分析
直接用kill或者TerminateProcess函数强行终止进程,会导致被终止的进程无法清理资源。
-
正确措施
在使用kill、TerminateProcess函数之前,先用其他约定好的信号量通知被终止的进程。被终止的进程收到这个信号之后需要清理。等待一段时间后,再强行终止。
-
错误案例
kill(pid, SIGKILL);
本例中,直接使用了kill函数终止了进程。没留出清理资源的时间。
-
正确案例
/* 发出终止信号的进程中 */
kill(pid, SIGTERM);
waitAWhile();
if (0 == (waitpid(pid, &status, WNOHANG))) {
//发现要终止的进程没有终止
kill(pid, SIGKILL);
}
/* 被终止的进程中 */
void cleanAndExit(int signum) {
doSomething();
}
int main(void) {
signal(SIGTERM, cleanAndExit); //注册函数cleanAndExit到信号SIGUSR1
somethingElse();
}
本例中,发出终止信号的进程首先发送SIGTERM,等一段时间后才强行清理。这给被终止的进程留下了清理资源的时间。
4.2【推荐】勿用pthread_exit、ExitThread函数
-
场景介绍
pthread_exit函数和ExitThread函数用于终止自身线程。
-
风险分析
若终止自身线程,容易导致线程持有的资源不能有效回收,造成资源泄漏。
-
正确措施
应该等待线程执行完毕后自己安全的退出。可以使用return来逐级退出函数。
-
错误案例
int main(void) {
someFunctions();
if (someCondition) {
pthread_exit(NULL);
}
return 0;
}
本例中,当someCondition为真,会退出当前线程。但是,此时可能造成资源泄漏。
-
正确案例
int main(void) {
someFunctions();
if (someCondition) {
return 1;
}
return 0;
}
本例中,当someCondition为真,会使用return来退出当前线程。这是安全的退出。
4.3【推荐】不要使用abort函数,除非在处理致命错误
-
场景介绍
abort函数会直接退出进程。这可能导致一些资源没有清理(例如,没写完的IO流)。
-
风险分析
直接终止进程,则代码中的一些清理工作可能不会有执行,例如IO流没有写入。
-
正确措施
不要使用abort函数。若出现错误,可以用return来逐级传递错误信息等。
例外:发生致命错误,在错误处理函数中,可以用abort函数终止程序。
-
错误案例
if (someCondition) {
abort();
}
本例中,当someCondition为真时,会直接退出当前进程,没有回收资源,这可能导致资源泄漏。
-
正确案例
if (someCondition) {
return 3;
}
本例中,当someCondition为真,会使用return来退出当前函数。开发者可逐级处理、传递返回值,直到进程安全地退出。
若用于处理致命错误,可用abort进行程序的退出,如下:
// 错误处理函数
void handleError(int errorCode) {
if (conditionOfFatalError) {
abort();
}
}
if (someCondition) {
handleError(errorCode);
}
4.4【推荐】除非用于定位错误,勿用atexit函数
-
场景介绍
atexit函数可以注册函数,当程序退出时调用注册的函数。建议开发者用该方法定位错误,不要用于做别的事。
-
风险分析
开发者不应使用atexit函数注册函数来清理内存。不用的内存应第一时间回收。
-
正确措施
atexit只用于注册函数以定位错误。
-
错误案例
void free_atexit() {
free(p);
}
atexit(free_atexit);
本例中,注册到atexit()的函数是free_atexit(),它的作用是释放内存。但是,开发者应该在不用的时候就释放掉内存,而非在程序退出时释放。
-
正确案例
someExpression();
use(p);
free(p);
someExpression();
如上所示,开发者应该在用完后就释放指针等资源,而非使用atexit最后释放。
除此之外,atexit()可以用于在程序退出时帮助定位错误,例如:
void log_from_atexit() {
printf("@log_from_atexit()@%s\n", strerror(errno));
printf("@log_from_atexit()@errorno=%d\n", errno);
}
atexit(log_from_atexit);
本例中,在atexit()注册了打印错误信息的函数log_from_atexit()。
4.5【推荐】勿在main函数之外的函数中使用exit、ExitProcess
-
场景介绍
exit函数和ExitProcess函数会直接退出进程。这可能导致一些资源没有清理,例如IO流没有写入。
-
风险分析
直接终止进程,则代码中的一些清理工作可能不会有执行,例如IO流没有写入。
-
正确措施
不要使用exit函数和ExitProcess函数。若出现错误,可以用return来逐级传递错误信息等;传递到main函数后,可以退出。在main函数中可以使用exit函数和ExitProcess函数。
-
错误案例
if (someCondition) {
exit(3);
}
本例中,当someCondition为真时,会直接退出当前进程,没有回收资源,这可能导致资源泄漏。
-
正确案例
if (someCondition) {
return 3;
}
本例中,当someCondition为真,会使用return来退出当前函数。开发者可逐级处理、传递返回值,直到进程安全地退出。
【关注我,后续持续新增专题博文,谢谢!!!】
下一篇讲解:
更多推荐
所有评论(0)