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