Answers:
我认为这可以回答您的问题:
从Richard Stevens(rstevens@noao.edu):
基本区别在于select()的fd_set是位掩码,因此具有某些固定大小。编译内核时,内核可能不会限制此大小,从而允许应用程序将FD_SETSIZE定义为所需的大小(正如今天系统头中的注释所暗示的),但它需要做更多的工作。4.4BSD的内核和Solaris库功能都有此限制。但是我看到现在已经对BSD / OS 2.1进行了编码以避免这种限制,因此它是可行的,仅需编程即可。:-)有人应该对此提交一份Solaris Bug报告,并查看它是否得到修复。
但是,使用poll()时,用户必须分配一个pollfd结构数组,并传递该数组中的条目数,因此没有基本限制。正如Casper所指出的那样,具有poll()的系统少于select的系统,因此后者更具可移植性。同样,使用原始实现(SVR3),您无法将描述符设置为-1来告诉内核忽略pollfd结构中的条目,这使得很难从数组中删除条目;SVR4解决了这个问题。就我个人而言,我总是使用select()而很少使用poll(),因为我也将代码移植到了BSD环境中。对于这些环境,有人可以编写使用select()的poll()的实现,但是我从未见过这样的实现。POSIX 1003.1g正在将select()和poll()标准化。
以上引用的电子邮件至少与2001年一样古老;poll()
现在(2017)在所有现代操作系统(包括BSD)中都支持该命令。实际上,有些人认为select()
应该弃用。除了观点,围绕可移植性的问题poll()
不再是现代系统的关注点。此外,epoll()
自从开发出(您可以阅读手册页)以来,它的受欢迎程度持续上升。
对于现代开发,您可能不想使用select()
,尽管没有任何明显的错误。 poll()
,并且它是更现代的演变epoll()
,提供了相同的功能(以及更多),select()
而没有受到其中的限制。
select
还是poll
:(
该select()
调用使您创建了三个位掩码,以标记要监视的套接字和文件描述符,以查看它们的读取,写入和错误,然后操作系统标记哪些套接字和文件描述符实际上具有某种活动。poll()
您是否创建了描述符ID列表,并且操作系统用发生的事件类型标记了每个描述符。
该select()
方法相当笨拙且效率低下。
通常,一个进程可以使用一千多个潜在文件描述符。如果长时间运行的进程仅打开了几个描述符,但是至少其中一个分配了高编号,则传递给的位掩码select()
必须足够大以容纳最高的描述符-因此数百个位的整个范围将不必担心操作系统必须在每次select()
调用时循环,以发现它们未设置。
一旦select()
返回,调用者必须遍历所有三个位掩码以确定发生了什么事件。在许多典型应用中,在任何给定时刻只有一个或两个文件描述符将获得新的通信量,但是必须一直读取所有三个位掩码以发现那些描述符。
由于操作系统会通过重写位掩码来向您发出有关活动的信号,因此它们被破坏了,并且不再用您要收听的文件描述符列表进行标记。您要么必须从保存在内存中的其他列表中重建整个位掩码,要么必须memcpy()
在每个select()
调用之后在每个损坏的位掩码之上保留每个位掩码和数据块的副本。
因此该poll()
方法效果更好,因为您可以继续重复使用相同的数据结构。
实际上,poll()
它启发了现代Linux内核中的另一种机制:epoll()
对该机制进行了进一步改进,以实现可扩展性的又一次飞跃,因为当今的服务器通常希望一次处理成千上万的连接。这是对工作的良好介绍:
http://scotdoyle.com/python-epoll-howto.html
尽管此链接有一些不错的图表,它们显示了epoll()
()的好处(您会注意到,select()
到目前为止,它被认为是效率低下且过时的,因此甚至无法在这些图表上显示一条直线!):
http://lse.sourceforge.net/epoll/index.html
更新:这是另一个堆栈溢出问题,其答案甚至提供了有关差异的更多详细信息: