我知道atexit用于注册函数处理程序。然后,当代码中退出时,将调用该函数。但是,如果函数处理程序内部发生退出怎么办?
我期待一个无限循环,但实际上程序可以正常退出。为什么?
void handler(){
printf("exit\n");
exit(1);
}
int maint(int argc, char *argv[]) {
atexit(handler);
exit(1);
}
答案 0 :(得分:4)
行为是不确定的。
7.22.4.4退出功能
2退出功能导致正常程序终止。没有 调用由at_quick_exit函数注册的函数。如果一个 程序多次调用退出函数,或调用 quick_exit函数除了退出函数外,其行为是 未定义。
在exit
处理程序中调用at_exit
(在正常退出处理过程中正在运行)绝对是第二次退出。
正常退出是一种可能的行为,但是看到任何事情都可能发生(行为的性质未定义),很可能导致灾难。最好不要这样做。
答案 1 :(得分:0)
正如您所指出的那样,行为是不确定的....但是,尽管如此,为了证明您观察到的行为是合理的,库作者通常倾向于应对奇怪的程序员行为(例如至少很奇怪)。 在程序位于exit()
时调用exit()
),我会说:
在调用任何退出处理程序之前,exit(3)
函数可能只是从信号处理程序列表中取消注册。这将使exit(2)
函数仅调用一次每个退出处理程序,而不递归调用该处理程序。只需尝试再次注册它,看看会发生什么事是一个好习惯。
退出函数可能会将其自身标记为正在运行,并且如果在处理程序中被调用,则仅返回就好像什么都没有发生。
您的预期行为可能会导致严重溢出(此处没有双关语:))
可以...
无论发生什么事,都是英国的一部分。在其他答案中进行了评论,但是对于试图在标准上扩展并正常运行的库,可能最好的行为是避免以某些建议的方式在出口处理程序中进行递归调用。
另一方面,您最好不要在程序中使用此功能(让我们这样称呼),因为如果不遵循标准,它可能会给您带来麻烦将来将程序移植到其他地方。
您可能认为exit(3)
是一个永远不会被调用的函数(除了像您公开的那样递归地调用之外),但您认为程序上有多个线程,其中两个调用了{{1} }同时起作用。
可能的最佳行为是具有某种信号量,以防止处理程序相互访问...但是使处理程序列表短时间受到损害的最佳方法是从列表中取消链接一个处理程序(让我们将列表视为一个队列,每个线程都到达并接收一个处理程序),他们获取处理程序,解锁队列然后执行它。这可能会导致每个处理程序由已调用exit(3)
的线程中的一个线程执行。实现者要面对的第一件事是如何处理多个同时调用exit()
的线程。
答案 2 :(得分:0)
POSIX.1说多次调用exit(3)的结果(即在使用atexit()注册的函数中调用exit(3)的结果)未定义。 在某些系统(而不是Linux)上,这可能导致无限递归。可移植程序不应在使用atexit()注册的函数内调用exit(3)。