在多线程环境中recv()不会被信号中断
我有一个处于阻塞recv()
循环中的线程,我想终止(假设不能将其更改为select()
其他任何异步方法)。
我也有一个捕获的信号处理程序,SIGINT
理论上它应该recv()
返回错误并errno
设置为EINTR
。
但是事实并非如此,我认为这与应用程序是多线程的事实有关。还有另一个线程,正在等待pthread_join()
呼叫。
这里发生了什么事?
好的,现在我将信号从主线程显式传递给所有阻塞recv()
线程pthread_kill()
(这导致SIGINT
安装了相同的全局信号处理程序,尽管多次调用是良性的)。但是recv()
通话仍然没有畅通。
我编写了一个代码示例来重现该问题。
- 主线程将套接字连接到行为异常的远程主机,该主机不会让连接断开。
- 所有信号均被阻止。
- 读取线程线程已启动。
- Main解除封锁并安装的处理程序
SIGINT
。 - 读取线程解除阻塞并安装的处理程序
SIGUSR1
。 - 主线程的信号处理程序将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 socketimport 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允许任何一种行为,因此建议始终使用sigaction
可SA_RESTART
根据需要的行为选择设置或省略标志的位置。请参阅sigaction
此处的文档:
http://www.opengroup.org/onlinepubs/9699919799/functions/sigaction.html
以上是 在多线程环境中recv()不会被信号中断 的全部内容, 来源链接: utcz.com/qa/414547.html