事件驱动的IO与阻塞与非阻塞

4

你能否解释一下事件驱动的IO系统调用(例如select、poll和epoll)与阻塞式和非阻塞式IO有什么关系?

我不理解这些概念是否有关联。

3个回答

6
除了您可能希望出于以下原因使用非阻塞文件描述符进行事件驱动IO外,它们基本上没有关联:
1. 旧版本的Linux内核中肯定存在漏洞,即使在`select`指示套接字可读之后,`read`也可能会阻塞(在UDP套接字和校验和错误的数据包中发生)。当前版本的Linux仍然可能存在这样的漏洞;我不确定。
2. 如果有任何其他进程可以访问您的文件描述符并将读/写到它们,或者如果您的程序是多线程的,并且其他线程可能会这样做,则`select`确定文件描述符可读/可写和您的程序执行IO之间存在竞争条件,这可能导致阻塞。
3. 在调用`connect`之前,您几乎肯定要将套接字设置为非阻塞;否则,您将一直阻塞,直到连接成功。使用写入的`select`来确定何时成功连接,并使用错误的`select`来确定连接是否失败。

1
+1,您还可以添加一个使用监听TCP套接字的著名案例,其中客户端在selectaccept之间中断连接尝试。 - Nikolai Fetissov

6

select系统调用几乎支持所有的Unix系统,提供了一种用户空间应用程序监视一组描述符并获取关于哪个子集已经准备好读/写的信息的方法。它的特定接口有点笨拙,在大多数内核中的实现都很差。

epoll仅在Linux中提供相同的功能,但在效率和编程接口方面比select有了很大的改进。其他Unix系统也有他们专门的调用。

话虽如此,事件驱动的IO系统调用不需要阻塞或非阻塞描述符。阻塞是影响像readwriteacceptconnect这样的系统调用的行为。 selectepoll_wait确实有阻塞超时,但那与描述符无关。

当然,使用这些事件驱动的系统调用与阻塞描述符有点奇怪,因为您期望在通知其可用后,可以立即读取数据而不会阻塞。总是依赖于一个阻塞描述符在通知其准备就绪后不会阻塞是有点冒险的,因为可能存在竞争条件。

非阻塞的事件驱动IO可以使服务器应用程序更加高效,因为不需要为每个描述符(连接)使用线程。比较Apache Web服务器与Nginx或Lighttpd在性能方面的差异,您会看到其好处。


我不确定,但我认为它会为每个工作进程中的每个连接生成一个线程(或从池中选择一个线程)。 - Blagovest Buyukliev
1
我认为一个线程对应一个连接的服务器几乎可以和事件驱动的服务器一样快。Apache 的缓慢和臃肿的原因不在于线程,而是在于处理每个请求所涉及的巨大开销。pthread_create 的成本仅约等于 2-3 次 open 调用,当然,如果你已经将线程阻塞在 accept 上等待连接到来,那么成本甚至更低。 - R.. GitHub STOP HELPING ICE
@R..:好的,但是上下文切换怎么办?如果线程对应于CPU的数量,那就没问题,但是如果你有成千上万个线程(连接),该怎么办? - Blagovest Buyukliev
1
@R:我相信你的基准测试已经完成并讨论过了:http://www.kegel.com/c10k.html - Ioan
1
@loan:那个网站相当老了,它来自一个使用线程的时代(特别是在Linux上),当时这还是个完全的笑话。(可怕的LinuxThreads仍然是主流...) - R.. GitHub STOP HELPING ICE
显示剩余4条评论

0

select和类似的函数(您提到了几个)通常用于在事件驱动系统中实现事件循环。

也就是说,应用程序不直接从套接字或文件中进行read()操作,以避免可能会阻塞,而是调用select()在多个文件描述符上等待数据可用于任何一个

当文件描述符变为可用状态时,您可以确信数据已经可用,read()操作将不会阻塞。

这是一种在不使用多个线程的情况下同时处理来自多个来源的数据的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接