非阻塞套接字在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环境中,请阻止您将使用的信号(典型的SIGUSR1SIGUSR2 )用于所有其他线程,但调用accept()的线程,asi t是不确定的,进程的哪个线程将处理发送到进程的信号(通过进程’ pid “寻址”)

或者,您可以使用pthread_kill(, )仅向特定线程发送信号。