Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/64.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
recv()在多线程环境中不会被信号中断_C_Linux_Sockets_Pthreads_Signals - Fatal编程技术网

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()
调用仍然没有被解除阻止

编辑:

我已经编写了一个代码示例来重现这个问题

  • 主线程将套接字连接到行为不正常的远程主机,而该主机将不允许连接
  • 所有信号都被封锁了
  • 读取线程已启动
  • Main为
    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;
    };