gpt4 book ai didi

c++ - 使用多线程处理 SIGTERM 的正确方法

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:26:29 25 4
gpt4 key购买 nike

我在 Raspberry 上有一个多线程程序,我想在其中处理 SIGTERM 并正常关闭所有内容。问题是我有一个在阻塞套接字上调用了 recvfrom() 的后台线程。根据我对手册页的理解,如果我退出我的处理程序,所有系统调用都应该被唤醒并返回 -1 并且 errno 设置为 EINTR。但是在我的例子中,recvfrom 调用一直挂起。

1) 总的来说,我的理解是否正确,即所有具有能够被信号唤醒的阻塞系统调用的线程都应该在这种情况下唤醒?2) 会不会是操作系统在我的thead上设置了一些特殊的信号掩码?

有趣的是我使用的是 VideoCore 原语,而不是 pthread,也许这就是原因?这是一个小测试示例:

#include <iostream>

#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>


#include "interface/vcos/vcos.h"

void SignalHandler(int nSignalNumber)
{
std::cout << "received signal " << nSignalNumber << std::endl;
}

void* ThreadMain(void* pArgument)
{
int nSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (nSocket >= 0)
{
sockaddr_in LocalAddress;
memset(&LocalAddress, 0, sizeof(LocalAddress));
LocalAddress.sin_family = AF_INET;
LocalAddress.sin_addr.s_addr = INADDR_ANY;
LocalAddress.sin_port = htons(1234);
if (bind(nSocket, reinterpret_cast<sockaddr *>(&LocalAddress), sizeof(LocalAddress)) == 0)
{
sockaddr_in SenderAddress;
socklen_t nSenderAddressSize = sizeof(SenderAddress);
unsigned char pBuffer[512];
std::cout << "calling recvfrom()" << std::endl;
int nBytesReceived = recvfrom(nSocket, pBuffer, sizeof(pBuffer), 0, reinterpret_cast<struct sockaddr *>(&SenderAddress), &nSenderAddressSize);
if (nBytesReceived == -1)
{
if (errno == EINTR)
{
std::cout << "recvfrom() was interrupred by a signal" << std::endl;
}
else
{
std::cout << "recvfrom() failed with " << errno << std::endl;
}
}
}
else
{
std::cout << "bind() failed with " << errno << std::endl;
}
close(nSocket);
}
else
{
std::cout << "socket() failed with " << errno << std::endl;
}
return NULL;
}

int main(int argc, char** argv)
{
struct sigaction SignalAction;
memset(&SignalAction, 0, sizeof(SignalAction));
SignalAction.sa_handler = SignalHandler;
sigaction(SIGTERM, &SignalAction, NULL);
VCOS_THREAD_T Thread;
VCOS_STATUS_T nVcosStatus = vcos_thread_create(&Thread, "", NULL, ThreadMain, NULL);
if (nVcosStatus == VCOS_SUCCESS)
{
void* pData = NULL;
vcos_thread_join(&Thread, &pData);
}
else
{
std::cout << "vcos_thread_create() failed with " << nVcosStatus << std::endl;
}
return EXIT_SUCCESS;
}

可以这样编译:

g++ test.cpp -I/opt/vc/include -L/opt/vc/lib -lvcos  -o test

当我运行它然后在正在运行的实例上调用 kill 时,输出是:

calling recvfrom()
received signal 15

然后进程挂起。如果 pthread 的行为不同,我会尝试。

更新

好的,我更新了示例以生成一个 pthread 线程,并且那个线程也没有退出。所以我假设信号没有填充到所有线程?

#include <iostream>

#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>

#include "interface/vcos/vcos.h"

void SignalHandler(int nSignalNumber)
{
std::cout << "received signal " << nSignalNumber << std::endl;
}

void* ThreadMain(void* pArgument)
{
const char* pThreadType = reinterpret_cast<const char*>(pArgument);
int nSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (nSocket >= 0)
{
sockaddr_in LocalAddress;
memset(&LocalAddress, 0, sizeof(LocalAddress));
LocalAddress.sin_family = AF_INET;
LocalAddress.sin_addr.s_addr = INADDR_ANY;
LocalAddress.sin_port = htons(pThreadType[0] * 100);
if (bind(nSocket, reinterpret_cast<sockaddr *>(&LocalAddress), sizeof(LocalAddress)) == 0)
{
sockaddr_in SenderAddress;
socklen_t nSenderAddressSize = sizeof(SenderAddress);
unsigned char pBuffer[512];
std::cout << "calling recvfrom()" << std::endl;
int nBytesReceived = recvfrom(nSocket, pBuffer, sizeof(pBuffer), 0, reinterpret_cast<struct sockaddr *>(&SenderAddress), &nSenderAddressSize);
if (nBytesReceived == -1)
{
if (errno == EINTR)
{
std::cout << "recvfrom() was interrupred by a signal" << std::endl;
}
else
{
std::cout << "recvfrom() failed with " << errno << std::endl;
}
}
}
else
{
std::cout << "bind() failed with " << errno << std::endl;
}
close(nSocket);
}
else
{
std::cout << "socket() failed with " << errno << std::endl;
}
std::cout << pThreadType << " thread is exiting" << std::endl;
return NULL;
}

int main(int argc, char** argv)
{
struct sigaction SignalAction;
memset(&SignalAction, 0, sizeof(SignalAction));
SignalAction.sa_handler = SignalHandler;
sigaction(SIGTERM, &SignalAction, NULL);
VCOS_THREAD_T VcosThread;
VCOS_STATUS_T nVcosStatus = vcos_thread_create(&VcosThread, "", NULL, ThreadMain, const_cast<char*>("vcos"));
bool bJoinVcosThread = false;
if (nVcosStatus == VCOS_SUCCESS)
{
bJoinVcosThread = true;
}
else
{
std::cout << "vcos_thread_create() failed with " << nVcosStatus << std::endl;
}
pthread_t PthreadThread;
int nPthreadStatus = pthread_create(&PthreadThread, NULL, ThreadMain, const_cast<char*>("pthread"));
bool bJoinPthreadThread = false;
if (nPthreadStatus == 0)
{
bJoinPthreadThread = true;
}
else
{
std::cout << "pthread_create() failed with " << nPthreadStatus << std::endl;
}
if (bJoinVcosThread)
{
void* pData = NULL;
vcos_thread_join(&VcosThread, &pData);
}
if (bJoinPthreadThread)
{
void* pData = NULL;
pthread_join(PthreadThread, &pData);
}
return EXIT_SUCCESS;
}

最佳答案

SIGTERM 等信号仅提交给进程中的一个线程。唯一的先决条件是所选线程必须没有屏蔽信号,或者必须使用 sigwait 等待它。不会直接通知其他线程信号已送达。

将信号与线程相结合的一种常见方法是拥有一个单独的线程,该线程仅处理信号并使用线程同步机制(例如条件变量)通知其他线程。

对于中断文件 I/O,这可能还不够,因为在检查终止请求和进行系统调用以执行 I/O 操作之间存在竞争条件。一些语言运行时库使用非阻塞 I/O 和 pollepoll 和一个特殊的文件描述符,它在信号传递时就绪(使用前面提到的线程-based 方法,或者 Linux 特有的东西,比如 signalfd)。其他人试图通过使用 dup2 将文件描述符替换为一个复杂的舞蹈直接使用 readwrite 系统调用来避免这种开销这总是导致 I/O 失败,从而避免竞争条件(但为此所需的簿记相当复杂)。

关于c++ - 使用多线程处理 SIGTERM 的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46978170/

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