实时/非批处理应用程序中的 C 退出代码和 atexit()
C exit codes and atexit() in a realtime / non-batch app
我正在编写一个游戏,其中许多不同的条件都可能导致失败,例如图像或着色器无法加载、OpenGL 无法获得有效的上下文等。
在理想情况下,我想以我认为适用于 C 的方式使用退出代码,即对于失败情况非零。但是,有一些因素对此有影响:
- 在使用非零退出代码时,不会调用
atexit()
,这对我来说很不幸,因为我的程序清理是通过 main()
中的单个 atexit()
注册分层处理的,我喜欢。
- 我突然想到,考虑到应用程序的性质 - 与批处理程序不同,游戏具有不确定的 运行 时间 - 可能永远不会出现退出代码为 的实际情况]实际使用。这不像例如由 Makefile 调用的命令行图像转换器,它需要通过其退出代码了解程序是成功还是失败,从而确定是否继续构建。可能是程序在运行时出了什么问题,比如文件丢失,那是我自己解决的问题,然后顺利进行。
在我的情况下,可以提出哪些支持和反对使用非零退出代码的论据?
触发注册的atexit()
个函数
与您的问题相反,即使程序尝试 return 主机环境的非零终止状态,仍会调用使用 atexit()
注册的函数。调用 exit()
或 main()
函数 returning,将触发使用 atexit()
注册的函数,无论给定值如何。
示例:
#include <stdlib.h>
#include <stdio.h>
void print_stuff(void)
{
puts("Stuff");
}
int main(void)
{
atexit(print_stuff);
exit(1);
}
这将打印 Stuff
,即使 1
是 returned。
技术细节
根据ISO C标准,在atexit()
中注册的函数在调用exit()
之后调用。以下情况也被定义为调用exit()
(并因此触发用atexit()
注册的函数):
main()
returning 相当于调用 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.
这样的系统,错误反馈可能以对话框弹出窗口、日志或标准错误的形式更有用。
我正在编写一个游戏,其中许多不同的条件都可能导致失败,例如图像或着色器无法加载、OpenGL 无法获得有效的上下文等。
在理想情况下,我想以我认为适用于 C 的方式使用退出代码,即对于失败情况非零。但是,有一些因素对此有影响:
- 在使用非零退出代码时,不会调用
atexit()
,这对我来说很不幸,因为我的程序清理是通过main()
中的单个atexit()
注册分层处理的,我喜欢。 - 我突然想到,考虑到应用程序的性质 - 与批处理程序不同,游戏具有不确定的 运行 时间 - 可能永远不会出现退出代码为 的实际情况]实际使用。这不像例如由 Makefile 调用的命令行图像转换器,它需要通过其退出代码了解程序是成功还是失败,从而确定是否继续构建。可能是程序在运行时出了什么问题,比如文件丢失,那是我自己解决的问题,然后顺利进行。
在我的情况下,可以提出哪些支持和反对使用非零退出代码的论据?
触发注册的atexit()
个函数
与您的问题相反,即使程序尝试 return 主机环境的非零终止状态,仍会调用使用 atexit()
注册的函数。调用 exit()
或 main()
函数 returning,将触发使用 atexit()
注册的函数,无论给定值如何。
示例:
#include <stdlib.h>
#include <stdio.h>
void print_stuff(void)
{
puts("Stuff");
}
int main(void)
{
atexit(print_stuff);
exit(1);
}
这将打印 Stuff
,即使 1
是 returned。
技术细节
根据ISO C标准,在atexit()
中注册的函数在调用exit()
之后调用。以下情况也被定义为调用exit()
(并因此触发用atexit()
注册的函数):
main()
returning 相当于调用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.
这样的系统,错误反馈可能以对话框弹出窗口、日志或标准错误的形式更有用。