我正在阅读 signal safety在联机帮助页中,abort
是 POSIX 标准要求的异步信号安全功能之一。
但是,根据 abort 的联机帮助页, 它说
Up until glibc 2.26, if the abort() function caused process termination, all open streams were closed and flushed (as with fclose(3)).
fclose
未列为异步信号安全(尽管 close
是)。
这是否意味着 abort
在 2.27 之前的 glibc 版本中不是异步信号安全的?
我这么想的另一个原因是 exit(3)
不是异步信号安全的,因为它会刷新 stdio 缓冲区,这正是 abort
使用的做。
最佳答案
fclose
is not listed as async-signal-safe (althoughclose
is). Does this mean thatabort
was not async-signal-safe in glibc versions before 2.27?
是的,你是对的。 abort
在早期版本中不是异步信号安全的。在某个阶段,它会[尝试]fclose
所有打开的流。而且,它将通过它设置的内部信号处理程序执行此操作。
给定的流可能被锁定或处于不确定状态。 TL;DR 是 abort
不是 异步安全的,因为 fclose
是[并且] 不是 异步安全的。
缺乏异步安全的原因之一是 malloc
等。阿尔。 不异步安全。尽管它们将访问包装在互斥体中,但它们不会屏蔽信号。进程/线程可以在涉及执行 realloc
的流上执行 set*buf
。信号处理程序中的调用可能会损坏堆和/或崩溃。
能够查看较旧的源代码可能会有所帮助。这是 glibc
源代码页面:
https://www.gnu.org/software/libc/sources.html
在该页面上,我们可以克隆 glibc
git 存储库:
git clone https://sourceware.org/git/glibc.git
cd glibc
git checkout master
这将为我们提供一个相当现代的版本。
但是,只要稍微改变那里的说明,我们就可以检查早期版本:
git checkout release/2.25/master
然后,如果我们查看 stdlib/abort.c
,我们会看到:
/* Now close the streams which also flushes the output the user
defined handler might has produced. */
if (stage == 4)
{
++stage;
__fcloseall ();
}
在以后的版本中,第 4 阶段缺少 __fcloseall
调用:
/* Now try to abort using the system specific command. */
if (stage == 4)
{
++stage;
ABORT_INSTRUCTION;
}
这是 2.25 到当前的 [手动] 差异:
--- abort.c 2022-12-11 22:42:33.064470777 -0500
+++ /tmp/abort.c 2022-12-11 22:42:21.106728443 -0500
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991-2017 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -13,7 +13,7 @@
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
- <http://www.gnu.org/licenses/>. */
+ <https://www.gnu.org/licenses/>. */
#include <libc-lock.h>
#include <signal.h>
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <internal-signals.h>
/* Try to get a machine dependent instruction which will make the
program crash. This is used in case everything else fails. */
@@ -30,11 +31,8 @@
# define ABORT_INSTRUCTION
#endif
-#include <libio/libioP.h>
-#define fflush(s) _IO_flush_all_lockp (0)
-
/* Exported variable to locate abort message in core files etc. */
-struct abort_msg_s *__abort_msg __attribute__ ((nocommon));
+struct abort_msg_s *__abort_msg;
libc_hidden_def (__abort_msg)
/* We must avoid to run in circles. Therefore we remember how far we
@@ -50,32 +48,24 @@
abort (void)
{
struct sigaction act;
- sigset_t sigs;
/* First acquire the lock. */
__libc_lock_lock_recursive (lock);
/* Now it's for sure we are alone. But recursive calls are possible. */
- /* Unlock SIGABRT. */
+ /* Unblock SIGABRT. */
if (stage == 0)
{
++stage;
- if (__sigemptyset (&sigs) == 0 &&
- __sigaddset (&sigs, SIGABRT) == 0)
- __sigprocmask (SIG_UNBLOCK, &sigs, (sigset_t *) NULL);
- }
-
- /* Flush all streams. We cannot close them now because the user
- might have registered a handler for SIGABRT. */
- if (stage == 1)
- {
- ++stage;
- fflush (NULL);
+ internal_sigset_t sigs;
+ internal_sigemptyset (&sigs);
+ internal_sigaddset (&sigs, SIGABRT);
+ internal_sigprocmask (SIG_UNBLOCK, &sigs, NULL);
}
/* Send signal which possibly calls a user handler. */
- if (stage == 2)
+ if (stage == 1)
{
/* This stage is special: we must allow repeated calls of
`abort' when a user defined handler for SIGABRT is installed.
@@ -93,7 +83,7 @@
}
/* There was a handler installed. Now remove it. */
- if (stage == 3)
+ if (stage == 2)
{
++stage;
memset (&act, '\0', sizeof (struct sigaction));
@@ -103,30 +93,22 @@
__sigaction (SIGABRT, &act, NULL);
}
- /* Now close the streams which also flushes the output the user
- defined handler might has produced. */
- if (stage == 4)
- {
- ++stage;
- __fcloseall ();
- }
-
/* Try again. */
- if (stage == 5)
+ if (stage == 3)
{
++stage;
raise (SIGABRT);
}
/* Now try to abort using the system specific command. */
- if (stage == 6)
+ if (stage == 4)
{
++stage;
ABORT_INSTRUCTION;
}
/* If we can't signal ourselves and the abort instruction failed, exit. */
- if (stage == 7)
+ if (stage == 5)
{
++stage;
_exit (127);
关于c - abort() 在 glibc 版本 < 2.27 中不是异步信号安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74766327/