可能重复:
唤醒线程在 accept() 调用时被阻塞
我正在编写一个小型服务器,它监听连接(接受它们并将它们传递给工作线程),直到发送自定义停止信号。
如果我使用阻塞套接字,那么我的主接受循环不能在发送自定义停止信号时中断。但是,我希望避免使用非阻塞套接字进行忙等待/自旋锁循环。
我想要的是让我的主接受循环阻塞,直到接收到连接或发送停止信号。
这在Linux上的C中可能吗?
非常感谢。
可能重复:
唤醒线程在 accept() 调用时被阻塞
我正在编写一个小型服务器,它监听连接(接受它们并将它们传递给工作线程),直到发送自定义停止信号。
如果我使用阻塞套接字,那么我的主接受循环不能在发送自定义停止信号时中断。但是,我希望避免使用非阻塞套接字进行忙等待/自旋锁循环。
我想要的是让我的主接受循环阻塞,直到接收到连接或发送停止信号。
这在Linux上的C中可能吗?
非常感谢。
如果我理解正确,那么您可以使用任何类型的“信号”,不一定是 POSIX 信号。实际上,POSIX 信号是一个糟糕的选择,因为检查您是否在循环中收到了一个不可避免的竞争条件。
您需要使用的是可以通过文件描述符监视的任何内容。它可能是:
示例列表中后面的条目通常不实用。他们只是为了说明它可以是任何类型的对象,只要它具有可监控的文件描述符。最简单、最便宜和最受欢迎的方法是管道。
如果您已经在使用非阻塞套接字,那么您肯定已经有某种轮询循环来检查它们何时准备好接受连接。我将假设您正在使用poll()
它来执行此操作。
在开始循环之前,请像这样设置管道:
pipe(&pipefd[0]);
read_end = pipefd[0];
write_end = pipefd[1];
fcntl(read_end, F_SETFL, O_NONBLOCK);
就是将fcntl
管道的读取端设置为非阻塞模式。您将在poll
调用中将管道的那一端与套接字一起使用,因此它需要是非阻塞的。
然后只需将管道的读取端添加到您在接受循环中监视的图块描述符列表中:
for (;;) { /* accept loop, loop forever */
/* Monitor your socket. You should already have this in your code */
pollfd[0].fd = your_socket;
pollfd[1].events = POLLIN;
/* Add a new entry to monitor the read end of the pipe */
pollfd[1].fd = read_end;
pollfd[1].events = POLLIN;
/* Now call poll, as before, but monitor 2 file descriptors instead of just 1 */
n = poll(&pollfd[0], 2, -1);
/* Check if your socket is ready to accept.
This part, you are already doing. */
if (pollfd[0].revents) {
newsocket = accept(your_socket, etc....)
do_somehting_with(new_socket);
}
/* New part: check if anyone has written something for you into your pipe */
if (pollfd[1].revents) {
break; /* stop the loop */
}
}
该示例使用poll
但您可能正在使用select
(或什至epoll
)。select
有不同的界面,但想法是一样的。
这种技术被称为自管技巧。
如果您不想将解除阻塞的套接字传递给 accpet(),那么发送信号(如ing 线程man signal
所述是中断阻塞(等待连接)accept()
的唯一方法)。accept()
正如一些评论者已经指出的那样,accept()
当他们的线程收到信号时,大多数系统调用(包括 )将被中断。
如果accept()
返回-1
并errno
设置为EINTR
调用accept()
被信号中断。
要向进程发送信号,请使用kill(<pid>, <signal-number>)
.
如果您处于多线程环境中,请阻止您将为所有其他线程使用(典型SIGUSR1
或SIGUSR2
)的信号,但调用accept()
, asit 的线程不确定进程的哪个线程将处理发送到进程的信号(通过流程“解决” pid
。)
或者,您可以使用pthread_kill(<pthread>, <signal-number>)
仅将信号发送到特定线程。