实时/非批处理应用程序中的 C 退出代码和 atexit()
Posted
技术标签:
【中文标题】实时/非批处理应用程序中的 C 退出代码和 atexit()【英文标题】:C exit codes and atexit() in a realtime / non-batch app 【发布时间】:2015-08-06 19:45:11 【问题描述】:我正在编写一个游戏,其中许多不同的条件都可能导致失败,例如图像或着色器无法加载、OpenGL 无法获取有效上下文等。
在理想情况下,我想以我认为适用于 C 的方式使用退出代码,即非零用于失败条件。但是,有一些因素会影响到这一点:
在使用非零退出代码时,atexit()
不会被调用,对我来说很遗憾,因为我的程序清理是通过 main()
中的单个 atexit()
注册分层处理的,我喜欢这样。
在我看来,鉴于应用程序的性质 - 与批处理程序不同,游戏具有无限的运行时间 - 可能永远不会出现退出代码实际使用的实际情况。它不像例如由 Makefile 调用的命令行图像转换器,它需要通过其退出代码知道程序是成功还是失败,从而确定是否继续构建。可能是如果程序在运行时出现任何问题,例如丢失文件,那是我自己解决的问题,然后顺利进行。
在我的情况下,支持和反对使用非零退出代码有哪些论据?
【问题讨论】:
atexit()
函数在正常终止时被调用,这与错误代码无关。如果exit()
被调用或main()
返回,那么注册在atexit()
中的函数应该被调用。
就个人而言,我大量使用退出代码。请记住,atexit 处理程序是从调用 exit 的线程调用的。如果你想要每个线程的处理程序,你可以使用 pthread_key_create 析构函数
是的,正常终止包括退出代码非零的情况。在一个小例子中自己尝试一下。
@Veltas 我相信你;我认为我总是混淆“正常程序终止”和“成功程序终止”这两个术语。如果您给出答案,我可以接受 - 谢谢。
我想指出,在程序终止的情况下,清理所有内容是一个有争议的问题(实际上不鼓励)。它所做的只是迫使操作系统交换它可能已经移出系统内存的资源 - 只是为了在片刻之后丢弃该内存。只需终止该过程;这隐式释放所有内存并关闭所有句柄;干净拆解的唯一原因是如果在 Valgrind 或类似程序中运行以检查是否没有内存泄漏。在atexit
处理程序中,您应该只放置操作系统无法为您清理的东西。
【参考方案1】:
触发注册的atexit()
函数
与您的问题相反,使用atexit()
注册的函数即使在程序尝试向主机环境返回非零终止状态时仍会被调用。对exit()
的调用或main()
函数的返回将触发在atexit()
中注册的函数,而不管给定的值如何。
例子:
#include <stdlib.h>
#include <stdio.h>
void print_stuff(void)
puts("Stuff");
int main(void)
atexit(print_stuff);
exit(1);
这将打印Stuff
,即使返回了1
。
技术细节
根据 ISO C 标准,atexit()
中注册的函数是在调用exit()
之后调用的。以下情况也定义为调用exit()
(从而触发atexit()
注册的函数):
main()
返回相当于调用exit()
。
在最后一个线程调用thrd_exit()
之后,调用exit(EXIT_SUCCESS)
。
以下是可能的实现定义的调用exit()
的来源:
SIGTERM
的默认信号处理程序。
使用set_constraint_handler_s()
之前的默认约束处理程序。
该标准提到了以下情况,其中exit()
和对atexit()
注册的函数的调用被规避:
未处理的SIGABRT
或SIGABRT
已完成由signal()
注册的函数处理。 SIGABRT
可以由abort()
提出。
致电_Exit()
。
致电quick_exit()
。
在某些情况下,您实现的宿主环境可能会终止程序,而无需调用在atexit()
注册的函数,例如在segfault 之后。
关于游戏的退出代码
您选择的退出代码对于游戏来说应该没有太大影响。是的,您不会依赖 shell 脚本来运行游戏并向用户报告错误。对于 Linux 等系统,错误反馈可能以对话框弹出、日志或标准错误的形式更有用。
【讨论】:
不错。你已经得到了我的支持;我不能给另一个。我们可能应该清理所有这些 cmets。以上是关于实时/非批处理应用程序中的 C 退出代码和 atexit()的主要内容,如果未能解决你的问题,请参考以下文章