使用select()时,我理解的流程是:
但是,我不明白“设置”文件描述符到底意味着什么。在这个documentation中,它说这意味着文件描述符是指定的fd_set的一部分。但是,如果 ISSET() 检查文件描述符是否发生了某些情况,那么为什么要在每次迭代开始时甚至调用 select() 之前“设置”每个文件描述符呢?难道它们不应该只在发生变化时才被“设置”吗?它们可以在 select() 返回之前的某个时刻“取消设置”吗?
为您希望内核查看的文件句柄设置读取、写入和异常的选择位。
内核将循环遍历这些位,直到您在第一个参数中提供的限制为止。它会用选择的结果覆盖您发送的位。
select 返回它设置的位数,这很重要,因为内核不需要检查所有句柄,它可能只在一个句柄后返回,如果你有很多文件句柄,你应该使用 epoll,但是无论如何,您可以计算找到的位数,直到它与选择返回匹配为止,并避免执行整个位掩码。
(尽管我认为大多数当前的 Unixish 内核都会扫描所有位,因为存在一些错误,其中低编号的文件句柄可能会因始终报告就绪而导致高编号的文件句柄挨饿。)
您可以根据需要设置要发送到 select 的位掩码。如果您希望从套接字读取数据,请设置其读取位。如果有数据等待写入套接字,请设置写入位。等等
您可能希望始终设置读取位,因为这样您就可以知道套接字已关闭,或者您可以尝试写入已关闭的套接字并收到 EPIPE 错误。
假设内核会缓冲对套接字所做的所有写入,这是一个重大错误,这就是为什么 select 有一个写入位:当有缓冲区空间可写入更多数据(可能至少是一个页面)时,它将触发4K 字节。