为什么在Linux中使用select?

43
我正在阅读一个串行程序,我观察到他们在使用read()之前使用select()。为什么这是必须的?为什么我们不能直接调用read()并检查它是否失败?另外,为什么我们必须将文件描述符增加1并传递,而我已经将文件描述符集合传递给了select()
例子:
r=select(fd+1, &fds, NULL, NULL, &timeout);
其中fds已经具有fd的值。

调用 read(2) 可能会阻塞。此外,了解 poll(2) 系统调用(它比 select(2) 更适合多路复用;请阅读有关 C10K 问题 的更多信息)。 - Basile Starynkevitch
@basile:你所说的“多路复用目的”是什么意思? - Darshan L
1
poll(2)和旧版的select(2)都在等待和多路复用几个文件描述符。它们的作用在概念上是相似的(但poll更加C10K友好,因为能够在超过1024个文件描述符上进行多路复用)。 - Basile Starynkevitch
3个回答

47

select() 系统调用会告诉你,你关注的文件描述符上是否有数据可以读取。严格来说,它是判断对文件描述符进行读操作时是否会阻塞。

如果你在一个文件描述符上(例如连接到串口的文件描述符)执行 read() 操作,而没有数据可读,则该调用将一直挂起,直到有数据可读。使用 select() 的程序不希望像这样被阻塞。

你还问道:

为什么我们要将文件描述符加1并将其传递给 select 函数,我已经传递了文件描述符集合?

那可能是指定了 FD_SET 的大小。select() 的第一个参数称为 nfds,POSIX 规定如下:

nfds 参数指定要测试的描述符的范围。每个集合中的前 nfds 个描述符都将被检查;也就是说,描述符集合中从零到 nfds-1 的描述符都将被检查。

因此,要测试一个文件描述符 nnfds 中的值必须至少为 n+1


http://manpages.courier-mta.org/htmlman2/select.2.html 中,我们需要将 nfds 作为第一个参数传递。至于第一部分,我现在理解了。 - user1667307
文件描述符的数量必须与传递到第二个参数的文件句柄数组中的元素数量相匹配。传入一个较大的数字可能不会失败,但肯定会导致有趣的无效内存访问。 - Pekka
@Jonathan Leffler 有没有一些易于理解select函数的示例? - Kalanidhi
不完全是;这不是一个容易理解的函数。它有着Unix所有系统调用中最复杂的接口之一。你可以查看是否需要在select()系统调用之间重置FD集合?; 你可以查看是否有任何平台在FD集合上使用结构体复制(对于select()pselect())会导致问题?。你可以使用“[c] select”在SO上搜索其他问题。 - Jonathan Leffler
有人能否添加一些背景/原因,解释为什么在内部减去1,导致用户总是要加1?不保存用户时间的原因是什么?加1的人是少数用户群吗?在什么使用场景下不需要加1? - nmz787
显示剩余3条评论

9

需要在读取交互用户输入时希望程序继续运行的程序需要是多线程的,或者它们需要仔细阅读输入流,并具体有条件。

Select(2) 可用于实现第二种设计模式。 它可以确定是否可以读取输入而不阻塞整个应用程序。


1. 或者其他不可预测的输入方式。


你也可以使用 poll(2) - Basile Starynkevitch
为什么这个答案和前面的评论都有“(2)”标记?这意味着什么?这让我想到传递了一个参数,或者“读取第二个定义”(就像英语词典可以为同一个单词拥有多个意义/用法一样)。 - nmz787
2
数字表示手册章节:https://unix.stackexchange.com/questions/3586/what-do-the-numbers-in-a-man-page-mean - bohrax

4

当您需要持续监视文件描述符,直到它们准备好进行某些 IO 操作时而不阻塞时,使用 select 调用。

一般在您希望 IO (例如 read() )是非阻塞的情况下使用,可参考 :手册

同时请查看相关的 API 文档。


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