在 std::thread 创建的线程中调用 pthread_sigmask 是一种好的做法吗?

Is it a good practice to call pthread_sigmask in a thread created by std::thread?

本文关键字:一种 sigmask 线程 创建 thread std 调用 pthread      更新时间:2023-10-16

1) 我是 std::thread 的新手,我想知道调用pthread_sigmask()来阻止std::thread创建的特定线程中的某些信号是否是一种好习惯。

我不希望新线程接收 SIGTERM、SIGHUP 等信号,因为主进程已经为这些信号安装了处理程序。

那么,调用pthread_sigmask()来阻止std::thread创建的线程中的某些信号是否是一种好的做法?

2)另外,我相信pthread_sigmask(SIG_BLOCK, &mask, NULL)的效果仅适用于使用创建的线程

std::thread(&Log::rotate_log, this, _logfile, _max_files, _compress).detach();

并调用rotate_log()作为启动函数。

并且pthread_sigmask(SIG_BLOCK, &mask, NULL)的效果将不适用于调用std::thread(&Log::rotate_log, this, _logfile, _max_files, _compress).detach()的线程。

我的理解正确吗?

void rotate_log (std::string logfile, uint32_t max_files, bool compress)
{
sigset_t mask;
sigemptyset (&mask);
sigaddset (&mask, SIGTERM);
sigaddset (&mask, SIGHUP);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
// Do other stuff.
}
void Log::log (std::string message)
{
// Lock using mutex
std::lock_guard<std::mutex> lck(mtx);
_outputFile << message << std::endl;
_outputFile.flush();
_sequence_number++;
_curr_file_size = _outputFile.tellp();
if (_curr_file_size >= max_size) {
// Code to close the file stream, rename the file, and reopen
...

// Create an independent thread to compress the file since
// it takes some time to compress huge files.
if (!_log_compression_on)
{
std::thread(&Log::rotate_log, this, _logfile, _max_files, _compress).detach();
}
}
}

理论上,即使在具有 POSIX 线程的系统上,std::thread的实现也可能创建一个非 POSIX 线程,而pthread_sigmask不适用于此类线程。 (不过,Maxim Egorushkin的评论是正确的——你真的应该阻止线程创建线程中的信号,并且只取消阻止你想要在新线程上处理的信号,以避免竞争条件。

我不能代表其他实现,但是这样的事情在GNU/Linux实现中是不可能发生的。 当然,我也不能权威地谈论这个实现,但是有太多的 C 和 C++ 代码假设用户空间线程(无论是 C、C++ 还是 POSIX)和内核任务(那些有 TID 的东西)之间有 1:1 的映射。 十年前,人们仍然认为n:m线程库是一种可能性(POSIX线程的早期1:m实现只是一个特例)。

但是今天,程序员从线程调用unshare (CLONE_FS),为该线程提供一个专用的当前目录,与所有其他线程分开。 他们调用setfscreatecon并期望这只影响调用线程。 他们甚至直接调用系统调用setresuidsetresgid,因为他们想避免 glibc 用来将更改传播到所有线程的 setxid 广播(内核不直接支持)。 所有这些都将在 n:m 线程模型下停止工作。 因此,std::thread和 POSIX 线程必须映射到内核任务,强制执行 1:1 模型。

此外,C 和 C++ 只有一个 GNU TLS ABI,这反过来又要求系统中只能有一种类型的线程,其中一个线程指针用于最终访问线程本地数据。

这就是为什么在GNU/Linux上,除了glibc提供的POSIX线程之外,std::thread不太可能使用任何东西。

正确的方法是在创建线程之前在父级中设置所需的信号掩码,然后在父级中将其还原。这样,您新创建的线程从一开始就设置了正确的信号掩码。(信号掩码继承自父线程)。

在线程启动设置信号掩码时,有一个时间段,在此期间线程没有所需的掩码。

如果您需要在POSIX系统上的多线程程序中设置信号掩码,那么pthread_sigmask是您需要使用的功能。C++标准库中没有与信号掩码交互的函数。

另外,我相信pthread_sigmask(SIG_BLOCK, &mask, NULL)的效果仅适用于使用...

pthread_sigmask适用于在其中执行调用的线程,而不管线程是如何创建的。它不适用于其他预先存在的线程。在您的示例中,调用pthread_sigmask的函数rotate_logstd::thread创建的线程中执行。

相关文章: