recv()在多线程环境中不会被信号中断
我有一个线程位于阻塞recv()在多线程环境中不会被信号中断,c,linux,sockets,pthreads,signals,C,Linux,Sockets,Pthreads,Signals,我有一个线程位于阻塞recv()循环中,我想终止它(假设这不能更改为select()或任何其他异步方法) 我还有一个信号处理程序,它捕获SIGINT,理论上它应该使recv()返回错误,并将errno设置为EINTR 但事实并非如此,我认为这与应用程序是多线程的这一事实有关。还有另一个线程正在等待pthread\u join()调用 这里发生了什么事 编辑: 好的,现在我通过主线程的pthread\u kill()将信号显式地传递给所有阻塞recv()线程(这会导致安装相同的全局SIGINT信号
recv()
循环中,我想终止它(假设这不能更改为select()
或任何其他异步方法)
我还有一个信号处理程序,它捕获SIGINT
,理论上它应该使recv()
返回错误,并将errno
设置为EINTR
但事实并非如此,我认为这与应用程序是多线程的这一事实有关。还有另一个线程正在等待pthread\u join()
调用
这里发生了什么事
编辑:
好的,现在我通过主线程的pthread\u kill()
将信号显式地传递给所有阻塞recv()
线程(这会导致安装相同的全局SIGINT
信号处理程序,尽管多个调用是良性的)。但是,recv()
调用仍然没有被解除阻止
编辑:
我已经编写了一个代码示例来重现这个问题
SIGINT
解除阻止并安装处理程序SIGUSR1
的处理程序SIGUSR1
sleep()
替换recv()
,它会被很好地中断
PS
或者,您可以只打开UDP套接字,而不使用服务器
客户
在多线程应用程序中,正常信号可以任意传送到任何线程。使用
pthread\u kill
将信号发送到感兴趣的特定线程。是否在recv()中等待的同一线程中调用信号处理程序?
您可能需要通过pthread_sigmask()显式屏蔽所有其他线程中的SIGINT正常情况下,信号不会用
EINTR
中断系统调用。过去有两种可能的信号传递行为:BSD行为(被信号中断时系统调用会自动重新启动)和Unix系统V行为(被信号中断时系统调用返回-1,且errno
设置为EINTR
)。Linux(内核)采用了后者,但GNU C库开发人员(正确地)认为BSD行为更理智,因此在现代Linux系统上,调用信号(这是一个库函数)会导致BSD行为
POSIX允许这两种行为,因此建议始终使用sigaction
,您可以根据需要的行为选择设置sau RESTART
标志或忽略它。参见此处的sigaction
文档:
正如在文章中提到的,确实可以改变信号活动。
我经常使用sigaction创建自己的“信号”函数。这是我用的
typedef void Sigfunc(int);
static Sigfunc*
_signal(int signum, Sigfunc* func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (signum != SIGALRM)
act.sa_flags |= SA_NODEFER; //SA_RESTART;
if (sigaction(signum, &act, &oact) < 0)
return (SIG_ERR);
return oact.sa_handler;
}
typedef void Sigfunc(int);
静态信号函数*
_信号(int signum,Sigfunc*func)
{
结构信号行动法;
act.sa_handler=func;
sigemptyset(和act.sa_面具);
act.sa_标志=0;
if(signum!=SIGALRM)
act.sa_flags |=sa_NODEFER;//sa_重启;
if(sigaction(signum,&act,&oact)<0)
返回(信号错误);
返回oact.sa_处理程序;
}
上面讨论的属性是sau flags字段的'or'ing。这来自“sigaction”的手册页:SA_RESTART提供了类似于BSD的行为,允许系统调用跨信号重启。SA_NODEFER意味着允许从其自身的信号处理器中接收信号
当信号调用被替换为“_signal”时,线程中断。输出输出输出“中断系统调用”,当发送SIGUSR1时,recv返回-1。发送SIGINT时,程序完全停止,输出相同,但最后调用了abort
我没有编写代码的服务器部分,只是将套接字类型更改为“DGRAM,UDP”以允许客户端启动。您可以在Linux recv上设置超时:
当你收到一个信号时,给做接收的班级打电话
void* signalThread( void* ptr )
{
CapturePkts* cap=(CapturePkts*)ptr;
sigset_t sigSet=cap->getSigSet();
int sig=-1;
sigwait(&sigSet,&sig); //signalThread: signal capture thread enabled;
cout << "signal=" << sig << " caught,ending process" << endl;
cap->setDone();
return 0;
}
class CapturePkts
{
CapturePkts() : _done(false) {}
sigset_t getSigSet() { return _sigSet; }
void setDone() {_done=true;}
bool receive( uint8_t *buffer, int32_t bufSz, int32_t &nbytes)
{
bool ret=true;
while( ! _done ) {
nbytes = ::recv( _sockid, buffer, bufSz, 0 );
if(nbytes < 1 ) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
nbytes=0; //wait for next read event
else
ret=false;
}
return ret;
}
private:
sigset_t _sigSet;
bool _done;
};
void*信号线程(void*ptr)
{
CapturePkts*cap=(CapturePkts*)ptr;
sigset_t sigset=cap->getSigSet();
int-sig=-1;
sigwait(&sigSet,&sig);//信号线程:信号捕获线程已启用;
cout Correct-进程定向信号被传递到未被阻止的abitrary线程,因此解决方案是在所有其他线程中阻止它。为完整起见:新线程从其父线程继承信号掩码。您可以在父线程中阻止它,并在创建线程后取消阻止它。这可能是唯一的解决方案(我知道)如果你依赖于一个创建线程的库,你无法更改它。我认为这对我不起作用。我需要主线程来停止阻塞线程,这意味着信号处理程序应该安装在不调用recv()
的线程中。由于某些原因,这不起作用(recv调用没有中断)。我是否需要对线程的信号处理程序和/或信号掩码执行任何特殊操作?您是否确认是否调用了信号处理程序,如果是,从哪个线程调用?在recv()之后是否接收到信号
call completed?@Alex-因此,使用sigaction
并设置sau RESTART
标志解决了您的问题?省略sau RESTART
标志将给出所需的行为。实际上在Linux/Ubuntu 12.04上,这两种行为都是公开的:recv
将在Ctrl-C上重新启动,而poll
将返回EINT
在同一个Ctrl-C上。但是使用sigaction
而不是signal
确实解决了这个问题。这意味着siga
typedef void Sigfunc(int);
static Sigfunc*
_signal(int signum, Sigfunc* func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (signum != SIGALRM)
act.sa_flags |= SA_NODEFER; //SA_RESTART;
if (sigaction(signum, &act, &oact) < 0)
return (SIG_ERR);
return oact.sa_handler;
}
void* signalThread( void* ptr )
{
CapturePkts* cap=(CapturePkts*)ptr;
sigset_t sigSet=cap->getSigSet();
int sig=-1;
sigwait(&sigSet,&sig); //signalThread: signal capture thread enabled;
cout << "signal=" << sig << " caught,ending process" << endl;
cap->setDone();
return 0;
}
class CapturePkts
{
CapturePkts() : _done(false) {}
sigset_t getSigSet() { return _sigSet; }
void setDone() {_done=true;}
bool receive( uint8_t *buffer, int32_t bufSz, int32_t &nbytes)
{
bool ret=true;
while( ! _done ) {
nbytes = ::recv( _sockid, buffer, bufSz, 0 );
if(nbytes < 1 ) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
nbytes=0; //wait for next read event
else
ret=false;
}
return ret;
}
private:
sigset_t _sigSet;
bool _done;
};