深入理解select网络模型(linux/windows)

来源:互联网 发布:程序员平均工资 编辑:程序博客网 时间:2024/06/06 23:16

IO模型主要分为以下几种

(1)阻塞I/O模型

(2)非阻塞IO模型

(3)IO复用模型(select 、poll)

(4)信号驱动式IO模型

(5)异步IO模型


select模型属于IO复用模型,所谓的IO复用就是内核一旦发现进程指定的一个或多个IO就绪,它就通知进程,让进程去完成IO操作。

在select模型中,我们会阻塞于select调用,直到调用超时或者套接字变为可读它才返回。

由于select对比于普通IO模型,需要两个系统调用,在效率上还略有劣势。但是他真正的好处在于,可以等待多个描述符就绪。

与IO复用密切相关的另一种模型就是在多线程中去使用阻塞式IO,这种模型和IO复用很类似。

select函数的原型:int select(int maxfdpl,fd_set* readset,fd_set* writeset,fd_set* exceptset,const struct timeval* timeout);

我们将通过对函数传参,告诉内核对那些描述符感兴趣,在linux中,不仅仅局限于套接字,任何描述符都可以。


关于timeval这个结构体,它是用来设置阻塞时间的,经过测试,这个阻塞的时间是整体的阻塞时间,而不是集合内的单个套接字的阻塞时间。

比如,我设置阻塞时间为10s,那么连接了一个客户端也是阻塞10s,连接5个客户端,也还是阻塞10s。

这个时间参数,存在三种可能

(1)传递一个空指针,让它一直阻塞。

(2)传递普通指定的时间。

(3)传递时间参数被设置为0,即不等待。但是,经过我的测试·,虽然说时间设置为0,但是当有套接字变为可读时它还是可以正常检测出来。

本人推测,当为0时,系统只会轮询一遍,当有套接字可读写时,立即返回。如果,设置了指定时间,它就会在这个时间段里,一直反复的

轮询,直到轮询到第一个可读写的套接字,然后立即返回。注意,是轮询到第一个就返回,不是说一直轮询到指定时间结束,然后把所有可

读写的套接字一起在集合内返回。

所有感兴趣的套接字都要先添加到fd_set这个集合中,关于这个集合的是怎么回事,普遍推论是它是一个整数数组,每个整数都是32位整数。

那么,每个描述符都可以在数组里找到指定位。什么意思呢?就是数组中第一个元素是32为的,那它就可以表示0~31这个区间内的描述符,

然后第二个整数元素,可以表示32~63,以此类推。

当有套接字进入可读写状态时,那么它所在的位,就会被置位,然后我们通过FD_ISSET宏,就可以检测出哪一位被置位了,就可以得到对应

的套接字。

对于maxfdpl这个参数,它是待测试的描述符个数,在数值上等于被等待的最大的描述符的值加1.为什么要指定它呢?前面提出过了,fd_set

是一个数组,每一位对应这描述符的值,假设最大的描述符是5,那么我们只需要让系统轮询前6位就好了,后面的位就可以不要浪费时间去

轮询了,因为根本就没有用到,所以我们把5加1作为参数传递给select函数。当然,这个参数只有伯克利套接字对它感兴趣,在windows中

·MSDN指出它是可以被忽略的。以上的推测,也仅限于linux的实现,对于windows并不适用。

对于windows

typedef struct fd_set {
        u_int fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

这是他的fd_set的实现,它是维护了一个socket的数组,将socket添加进数组里,我想这也是为什么他保留了第一个参数,但是并不对它感兴趣的原因。


select等待的最大描述符数是有限制的,在windows中是64.而linux是1024.


对于linux,当描述符数超过最大时,我在网上找到了一个实例,即便每个select检查的描述符数,小于1024,但是整个进程检查的描述符数

超过了1024也不行。select 的正确语义应该时,整个进程中,select 最多可以检查1024个描述符,而不管用了多少 select 函数。

对于大于1024的描述符,FD_SET的处理方式是拿描述符对1024取模。假设描述符为1029,FD_SET(1029,&set)相当于FD_SET(5,&set),

而 5 可能不是一个有效的描述符,当 select 去检查描述符 5 时,因为 5 不是有效的描述符,所以出 Invalid argument 或 Bad file descriptor 就比较合理了。


那么如何超出这个最大的限制呢,仅仅通过修改FD_SETSIZE宏的大小,对于linux通常是行不通的。对于windows。。。。。我也不了解。

0 0