当前位置: 首页 > 面试题库 >

在多线程环境中recv()不会被信号中断

浦琪
2023-03-14
问题内容

我有一个处于阻塞recv()循环中的线程,我想终止(假设不能将其更改为select()其他任何异步方法)。

我也有一个捕获的信号处理程序,SIGINT理论上它应该recv()返回错误并errno设置为EINTR

但是事实并非如此,我认为这与应用程序是多线程的事实有关。还有另一个线程,正在等待pthread_join()呼叫。

这里发生了什么事?

编辑:

好的,现在我将信号从主线程显式传递给所有阻塞recv()线程pthread_kill()(这导致SIGINT安装了相同的全局信号处理程序,尽管多次调用是良性的)。但是recv()通话仍然没有畅通。

编辑:

我编写了一个代码示例来重现该问题。

  1. 主线程将套接字连接到行为异常的远程主机,该主机不会让连接断开。
  2. 所有信号均被阻止。
  3. 读取线程线程已启动。
  4. Main解除封锁并安装的处理程序SIGINT
  5. 读取线程解除阻塞并安装的处理程序SIGUSR1
  6. 主线程的信号处理程序将a发送SIGUSR1到读取线程。

有趣的是,如果我替换recv()sleep()它,就可以了。

聚苯乙烯

或者,您可以仅打开UDP套接字而不使用服务器。

客户

#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>

static void
err(const char *msg)
{
    perror(msg);
    abort();
}

static void
blockall()
{
    sigset_t ss;
    sigfillset(&ss);
    if (pthread_sigmask(SIG_BLOCK, &ss, NULL))
        err("pthread_sigmask");
}

static void
unblock(int signum)
{
    sigset_t ss;
    sigemptyset(&ss);
    sigaddset(&ss, signum);
    if (pthread_sigmask(SIG_UNBLOCK, &ss, NULL))
        err("pthread_sigmask");
}

void
sigusr1(int signum)
{
    (void)signum;
    printf("%lu: SIGUSR1\n", pthread_self());
}

void*
read_thread(void *arg)
{
    int sock, r;
    char buf[100];

    unblock(SIGUSR1);
    signal(SIGUSR1, &sigusr1);
    sock = *(int*)arg;
    printf("Thread (self=%lu, sock=%d)\n", pthread_self(), sock);
    r = 1;
    while (r > 0)
    {
        r = recv(sock, buf, sizeof buf, 0);
        printf("recv=%d\n", r);
    }
    if (r < 0)
        perror("recv");
    return NULL;
}

int sock;
pthread_t t;

void
sigint(int signum)
{
    int r;
    (void)signum;
    printf("%lu: SIGINT\n", pthread_self());
    printf("Killing %lu\n", t);
    r = pthread_kill(t, SIGUSR1);
    if (r)
    {
        printf("%s\n", strerror(r));
        abort();
    }
}

int
main()
{
    pthread_attr_t attr;
    struct sockaddr_in addr;

    printf("main thread: %lu\n", pthread_self());
    memset(&addr, 0, sizeof addr);
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (socket < 0)
        err("socket");
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) <= 0)
        err("inet_pton");
    if (connect(sock, (struct sockaddr *)&addr, sizeof addr))
        err("connect");

    blockall();
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    if (pthread_create(&t, &attr, &read_thread, &sock))
        err("pthread_create");
    pthread_attr_destroy(&attr);
    unblock(SIGINT);
    signal(SIGINT, &sigint);

    if (sleep(1000))
        perror("sleep");
    if (pthread_join(t, NULL))
        err("pthread_join");
    if (close(sock))
        err("close");

    return 0;
}

服务器

import socket
import time

s = socket.socket(socket.AF_INET)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1',8888))
s.listen(1)
c = []
while True:
    (conn, addr) =  s.accept()
    c.append(conn)

问题答案:

通常,信号不会通过中断系统调用EINTR。从历史上看,有两种可能的信号传递行为:BSD行为(当被信号中断时,系统调用将自动重新启动)和Unix系统V行为(当被信号中断时,系统调用将返回-1并errno设置为EINTR)。Linux(内核)采用了后者,但是GNU
C库开发人员(正确地)认为BSD行为更为理智,在现代Linux系统上,调用signal(这是一个库函数)会导致BSD行为。

POSIX允许任何一种行为,因此建议始终使用sigactionSA_RESTART根据需要的行为选择设置或省略标志的位置。请参阅sigaction此处的文档:

http://www.opengroup.org/onlinepubs/9699919799/functions/sigaction.html



 类似资料:
  • 问题内容: 典型的(对于x86-64平台和Linux OS)是在开始时幼稚地锁定互斥锁并在完成后将其释放,还是以更巧妙的方式将互斥锁锁定在更精细的级别,从而减少了锁争用?如果确实采用第二种方法,那么该如何做? 问题答案: 经营多个分配 场所 。每个竞技场都有自己的锁。当线程需要分配内存时,选择一个竞技场,将其锁定,然后从中分配内存。 选择竞技场的机制有些复杂,旨在减少锁争用: 考虑到这一点,基本上

  • 我正在使用多线程执行插入操作。我使用了带注释的方法,我的方法是注释。但我无法执行插入操作,导致出现以下异常。 异常线程"Thread-21"javax.persistence.Transaction必需异常:在org.hibernate.ejb.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:96)在sun.reflect.Native

  • 我的 Web 应用程序中出现随机错误,我迷路了。我创建了一个库来解码代码。我尝试了很多,从未失败过测试。但突然间,它开始随机失败。由于它在单线程测试中运行良好,有时在 servlet 环境中失败时,我能想象的唯一解释是问题与多线程环境中使用的库有关。老实说,我知道多线程是一个非常复杂的问题。我担心我的库可能不是线程安全的。顺便说一下,它非常简单,它是一个具有几种静态方法的正面类。基本上,假设您正在

  • 我在学习多线程时发现对象的速度变慢了。hashCode在多线程环境中,因为对于相同数量的对象,计算运行4个线程的默认哈希代码要比运行1个线程的默认哈希代码花费两倍的时间。 但根据我的理解,并行完成这项工作也需要类似的时间。 您可以更改线程数。每个线程都有相同的工作量,因此您希望在我的四核机器上运行4个线程可能需要与运行单个线程相同的时间。 我看到4x大约2.3秒,但是。1x为9秒。 我的理解是否有

  • 问题内容: 这应该非常简单,并且令我感到惊讶的是,我还没找到关于stackoverflow的答案。 我有一个类似程序的守护程序,该程序需要响应SIGTERM和SIGINT信号才能与新贵一起正常工作。我读到最好的方法是在与主线程不同的线程中运行程序的主循环,并让主线程处理信号。然后,当接收到信号时,信号处理程序应通过设置通常在主循环中检查的哨兵标志来告诉主循环退出。 我已经尝试过这样做,但是它没有按

  • 在Linux中,当一个程序(可能有多个线程)收到信号(如SIGTERM或SIGHUP)时会发生什么? 哪个线程拦截信号?多个线程可以获得相同的信号吗?是否有专门处理信号的特殊线程?如果没有,那么处理信号的线程内部会发生什么?信号处理程序例程完成后,执行如何继续?