非阻塞套接字在C中没有自旋锁接受
可能重复:
唤醒线程在accept()调用时被阻塞
我正在编写一个小型服务器,它监听连接(接受它们并将它们传递给工作线程),直到发送自定义停止信号。
如果我使用阻塞套接字,那么我的主接受循环不会破坏正在发送的自定义停止信号。 但是,我希望避免使用带有非阻塞套接字的busy-wait / spinlock循环。
我想要的是我的主接受循环阻止,直到收到连接或发送停止信号。
这可能在Linux上的C?
非常感谢。
如果我理解正确,那么您可以使用任何类型的“信号”,而不一定是POSIX信号。 实际上,POSIX信号是一个糟糕的选择,因为检查你是否已经在一个循环中收到了一个不可避免的竞争条件。
您需要使用的是可以通过文件描述符监视的任何内容。 它可能是:
- 管道。 您的接受循环监视管道的读取端。 为了唤醒循环,另一个线程将一些内容写入写入结束(无关紧要)。
- 另一个sockets。 类似地,另一个线程通过写入套接字的另一端唤醒你的接受循环。
- 文件系统中使用inotify监视的文件。
- 在应该中断循环时接收一些数据的设备。
- 等等…
示例列表中的后面条目通常不实用。 它们只是为了说明它可以是任何类型的对象,只要它具有可监视的文件描述符即可。 最简单,最便宜,最受欢迎的方式是管道。
如果您已经在使用非阻塞套接字,那么您肯定已经有某种轮询循环来检查它们何时准备接受连接。 我假设您正在使用poll()
来执行此操作。
在开始循环之前,设置如下管道:
pipe(&pipefd[0]); read_end = pipefd[0]; write_end = pipefd[1]; fcntl(read_end, F_SETFL, O_NONBLOCK);
fcntl
用于将管道的读取端设置为非阻塞模式。 您将在poll
调用中与套接字一起使用管道的那一端,因此它需要是非阻塞的。
然后,只需将管道的读取末尾添加到您在accept循环中监视的tile描述符列表中:
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(),那么发送一个信号(如man signal
所描述的那样accept()
线程是中断阻塞accept()
(等待连接)的唯一方法。
正如一些评论者已经指出的那样,大多数系统调用(包括accept()
)将在其线程收到信号时被中断。
如果accept()
返回-1
并且errno
设置为EINTR
则对accept()
的调用将被信号中断。
要向进程发送信号,请使用kill(
。
如果您处于multithreading环境中,请阻止您将使用的信号(典型的SIGUSR1
或SIGUSR2
)用于所有其他线程,但调用accept()
的线程,asi t是不确定的,进程的哪个线程将处理发送到进程的信号(通过进程’ pid
“寻址”)
或者,您可以使用pthread_kill(
仅向特定线程发送信号。