gpt4 book ai didi

linux - 从 linux 信号处理程序初始化 c++11 函数静态变量是否安全?

转载 作者:行者123 更新时间:2023-12-03 09:51:36 25 4
gpt4 key购买 nike

关于此引用代码中 [1] 处的 C++11 静态初始化的 2 个问题(如下)(这是一个完整的经过测试的 c++11 示例程序)。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

struct Foo {
/* complex member variables. */
};

void DoSomething(Foo *foo) {
// Complex, but signal safe, use of foo.
}

Foo InitFoo() {
Foo foo;
/* complex, but signal safe, initialization of foo */
return foo;
}

Foo* GetFoo() {
static Foo foo = InitFoo(); // [1]
return &foo;
}

void Handler(int sig) {
DoSomething(GetFoo());
}

int main() {
// [2]

struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = Handler;
sigaction(SIGINT, &act, nullptr);

for (;;) {
sleep(1);
DoSomething(GetFoo());
}
}

问题 1:这是否保证安全(没有死锁等)? C++11 静态初始化涉及锁。如果信号在第一次调用 main 中的 GetFoo() 之前/之后/期间传递怎么办?

问题 2:如果在安装信号处理程序之前在 [2] 插入对 GetFoo() 的调用,这是否保证安全? (编辑:)即在 [2] 处插入 GetFoo() 是否确保稍后在循环运行时信号到达时不会出现死锁?

我假设在最近的 GNU/Linux 上使用 C++11(g++ 或 clang),尽管各种 Unice 的答案也很有趣。 (剧透:我认为答案是 1:否和 2:是,但我不知道如何证明。)

编辑:要清楚,我可以想象静态初始化可以像这样实现:

Mutex mx;           // global variable
bool done = false; // global variable
...
lock(mx);
if (!done) {
foo = InitFoo();
done = true;
}
unlock(mx);

然后它就不是死锁安全的,因为信号处理程序可能会在主线程锁定 mx 时锁定它。

但是还有其他的实现,例如:

Mutex mx;                        // global variable
std::atomic<bool> done = false; // global variable
...
if (!done.load()) {
lock(mx);
if (!done.load()) {
foo = InitFoo();
done.store(true);
}
unlock(mx);
}

如果代码路径在信号处理程序运行之前至少完整运行一次,则不会出现死锁。

我的问题是 c++11(或任何更高版本)标准是否要求实现是异步信号安全的(无死锁,也称为无锁)初始通过代码之后完成了吗?

最佳答案

static Foo foo = InitFoo(); 如何初始化必须在进入信号之前先说明。

需要dynamic initialization ,它将在第一次调用 GetFoo() 时被初始化,因为您在 InitFoo() 中提到的“复杂初始化”无法在编译时完成:

Dynamic initialization of a block-scope variable with static storageduration or thread storage duration is performed the first timecontrol passes through its declaration; such a variable is consideredinitialized upon the completion of its initialization. If theinitialization exits by throwing an exception, the initialization isnot complete, so it will be tried again the next time control entersthe declaration. If control enters the declaration concurrently whilethe variable is being initialized, the concurrent execution shall waitfor completion of the initialization. 85 If control re-enters the declaration recursively while the variable is being initialized, thebehavior is undefined.

85 The implementation must not introduce any deadlock around execution of the initializer. Deadlocks might still be caused by the program logic; the implementation need only avoid deadlocks due to its own synchronization operations.

有了这些,我们就可以开始提问了。

Question1: Is this guaranteed safe (no deadlocks etc)? C++11 static initialization involves locks. What if the signal is delivered before/after/during the first call to GetFoo() in main?

不,这不能保证。考虑何时从 for 循环中第一次调用 GetFoo():

GetFoo() -> a lock is taken to initialize 'foo'-> a signal arrives [control goes to signal handling function] -> blocked here for signal handling to complete

--> Handler() -> DoSomething(GetFoo()) -> GetFoo() -> waits here because the lock is unavailable.

(信号处理程序必须在这里等待,因为 'foo' 的初始化尚未完成 - 请引用上面的引述)。

因此在这种情况下(即使没有任何线程)也会发生死锁,因为线程被自身阻塞。

Question2: Is this guaranteed safe if a call to GetFoo() is inserted at [2] before the signal handler is installed?

在这种情况下,根本没有为 SIGINT 建立信号处理程序。因此,如果 SIGINT 到达,程序就会退出。 SIGINT 的默认处理是终止进程。 GetFoo() 的初始化是否进行并不重要。所以这很好。

案例 (1) 的根本问题是信号处理程序 Handler 不是 async-signal-safe因为它调用的 GetFoo() 不是异步信号安全的。


回复。使用静态初始化的可能实现更新了问题:

C++11 标准只保证 foo 的初始化以线程安全的方式完成(参见上面的粗体引号)。但是处理信号不是“并发执行”。它更像是“递归重新进入”,因为它甚至可以在单线程程序中发生——因此它是未定义的。即使像在避免死锁的第二种方法中那样实现静态初始化也是如此。

换句话说,如果像你第一种方式那样实现静态初始化,是不是违反了标准?答案是不。因此,您不能依赖以异步信号安全方式实现的静态初始化。

前提是您确保“...前提是代码路径在信号处理程序运行之前至少完整运行过一次。”那么你可以引入另一个检查来确保 GetFoo() 是异步信号安全的,无论静态初始化是如何实现的:

std::atomic<bool> foo_done = false;
static_assert( std::atomic<bool>::is_lock_free );

Foo* GetFoo() {
if (!foo_done) {
static Foo foo = InitFoo(); // [1]
foo_done = true;
}
return &foo;
}

关于linux - 从 linux 信号处理程序初始化 c++11 函数静态变量是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56399849/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com