根据这篇文章poll vs select vs event-based:
select()仅对每个文件使用(最多)三位数据 描述符,而poll()通常使用每个文件描述符64位。 在每个系统调用中,poll()因此需要将更多内容复制到 内核空间。 select()的小胜利。
这是fd_set的实现(在Advisories : multiple applications fd_set structure bitmap array index overflow
上找到)#ifndef FD_SETSIZE
#define FD_SETSIZE 1024
#endif
#define NBBY 8 /* number of bits in a byte */
typedef long fd_mask;
#define NFDBITS (sizeof (fd_mask) * NBBY) /* bits per mask */
#define howmany(x,y) (((x)+((y)-1))/(y))
typedef struct _types_fd_set {
fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];
} _types_fd_set;
#define fd_set _types_fd_set
所以,最后,fd_set只是一个long
的数组。它也写得:
对FD_SET的调用使用套接字号作为索引将位设置为1:
这意味着,如果我得到一个套接字fd编号为5,则索引为5的元素将被选中,其第一个位将从0翻转为1.由于select()
使用3位,我猜其他两个位用于发送和接收。它是否正确?为什么select()
在需要3位时才使用long?
另外,如上所述,poll()
使用64位进行检查。为什么poll
需要检查pollfd
结构中的每一位?这是pollfd
结构:
struct pollfd {
int fd; // the socket descriptor
short events; // bitmap of events we're interested in
short revents; // when poll() returns, bitmap of events that occurred
};
结构中的总位数是64位,对于32位int和两个16位short。我知道检查位标志的通常方法是使用AND (&)
运算符来过滤掉其他不相关的位。这适用于这种情况吗?
答案 0 :(得分:4)
select()
使用long
的原因是将多个位编码为单个变量值。 long
通常可以容纳32位(有时是64位),这意味着您可以使用long
类型的单个值来表示32个文件集。
"每个文件三位"来自您通常用于选择读取,写入和异常的三个不同fd_set
值。三个long
值是(假设32位long
),32 * 3 = 96位,但可以为32个不同文件选择所有三个条件,因此每个文件花费3位。这假设您为这三个集合中的32个文件中的每个文件select()
,我认为这至少有点罕见。
由于poll
直接使用int
大小的字段表示文件描述符,而不是通过索引到位集中隐式地表示文件描述符,因此它使用更多空间。
基本上,select()
的设计利用了文件描述符是*小*整数的假设,以及它们从0及以上分配。
答案 1 :(得分:4)
poll()通常每个文件描述符使用64位。在每个系统调用中,poll()因此需要将更多内容复制到内核空间。 select()的小胜利。
谬误 - 如果你只想监视一个fd,poll(2)会让你获得64位,而对于select(),你必须复制到12288位(3组,每组4096位)通常情况下)。
此外,poll()支持 fd ,其值大于FD_SETSIZE。
答案 2 :(得分:3)
它本身不使用1 long
。它使用FD_SETSIZE
位的位字段,在这种情况下为32位32位长或16位64位长。通过使用long,它允许在64位机器上使用更少的内存操作(这在Unix上已经存在了20年)。他们本可以使用普通的int
但是他们在64位机器上没有优势。这只是前瞻性思维。
如果您在多个文件描述符上使用select,则需要为所有文件描述符留出空间。想象一下,你想要读取3个套接字,来自SCSI设备和文件,文件描述符值分别为3,7,10,12和67,你会如何表示?使用bitset设置该位的位。
00010001.00101000:00000000.00000000:00000000.00000000:00000000.00000000 00010000.00000000
^ ^ ^ ^ ^
0 0 1 1 6
3 7 0 2 7