【关注我,后续持续新增专题博文,谢谢!!!】

上一篇我们讲了

        这一篇我们开始讲 最全C/C++安全编码规范之三:安全退出

目录

四、 安全退出

4.1【推荐】使用kill、TerminateProcess函数终止其他进程前需要通知和等待

4.2【推荐】勿用pthread_exit、ExitThread函数

4.3【推荐】不要使用abort函数,除非在处理致命错误

4.4【推荐】除非用于定位错误,勿用atexit函数

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来退出当前函数。开发者可逐级处理、传递返回值,直到进程安全地退出。

【关注我,后续持续新增专题博文,谢谢!!!】

下一篇讲解

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐