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