I/O多路复用之select函数分析

转载前注明出处,欢迎转载分享
如果一堆的client.c与server.c进行连接的话一定会产生很多的进程,也就是说每当来一个客户端请求,就会产生一个进程来服务,然而进程不可能无限制的产生,所以为了解决一对一占用资源的连接方式,引入了IO复用。
即:一个进程可以同时对多个客户请求进行服务。

I/O复用的适用如下:
  • 当客户处理多个文件句柄时必须使用。
  • 当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
  • 如果一个TCP服务器既要处理监听套接口,又要处理已连接的套接口,一般也要用到I/O复用。
  • 如果一个服务器既要处理TCP,又要处理UDP,一般要使用I/O复用。
  • 如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
总结:实际上,当一个接收端需要接收来自多个不同发送端的数据时,我们一般要用到I/O复用。

I/O复用的优点:
与多进程多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程、线程,也不必维护这些进程、线程,从而大大减小了系统的开销。


在与POSIX标准兼容的平台上,我们可以使用select函数实现I/O端口的复用,传递给select函数的参数会告诉内核:
  • 我们所关心的文件描述符
  • 对每个描述符,我们所关心的状态。(我们是要想从一个文件描述符中读或者写,还是关注一个描述符中是否出现异常)
  • 我们要等待多长时间。(我们可以等待无限长的时间,等待固定的一段时间,或者根本就不等待)
从 select函数返回后,内核告诉我们以下信息:
  • 对我们的要求已经做好准备的描述符的个数
  • 对于三种条件哪些描述符已经做好准备。(读,写,异常)
有了这些返回信息,我们可以调用合适的I/O函数(通常是 read 或 write),并且这些函数不会再阻塞.

select函数
1
2
3
4
#include sys/select.h >
#include sys/time.h >

int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);

返回值:
若有就绪描述符则为其数目,若超时则为0,若出错则为-1。
参数:
参数int maxfdp1代表的是文件描述符的数量+1,文件描述符是由0开始取的。

fd_set *readset,fd_set *writeset,fd_set*exceptset为select函数监视的三类文件描述符,调用select函数会阻塞,直到有描述符就绪(有数据可读,可写,或者有except)

最后一个参数,它指明我们要等待的时间:  
1
2
3
4
5
struct timeval
{
    
long tv_sec;  //秒
    long tv_usec;  //微秒
}

有三种情况:
    timeout == NULL  等待无限长的时间。等待可以被一个信号中断。当有一个描述符做好准备或者是捕获到一个信号时函数会返回。如果捕获到一个信号,select函数将返回 -1,并将变量 erro设为 EINTR。
    timeout->tv_sec == 0&&timeout->tv_usec ==0不等待,直接返回。加入描述符集的描述符都会被测试,并且返回满足要求的描述符的个数。这种方法通过轮询,无阻塞地获得了多个文件描述符状态。
    timeout->tv_sec !=0||timeout->tv_usec!= 0等待指定的时间。当有描述符符合条件或者超过超时时间的话,函数返回。在超时时间即将用完但又没有描述符合条件的话,返回0。对于第一种情况,等待也会被信号所中断。

注意:当在100秒完成时而你指定200秒,则如果在100秒完成,就结束


对于fd_set类型的变量我们所能做的就是声明一个变量,然后为变量赋一个同种类型变量的值,或者使用一下几个宏来控制它:  
1
2
3
4
5
#include sys/select.h >
int FD_ZERO(int fd, fd_set *fdset);
int FD_CLR(int fd, fd_set *fdset);
int FD_SET(int fd, fd_set *fdset);
int FD_ISSET(int fd, fd_set *fdset);

select函数模型主要建立在fd_set类型的基础上, fd_set是一组文件描述符的集合,在UNIX系统上通常会在头文件<sys/select.h>中定义常量FD_SETSIZE,即fd_set类型的大小,其值通常在1024合理,这样就能表示小于1024个的fd集合。
< sys/select.h>所在目录为:/usr/include/sys

我的centos6.8测出的原本大小为128字节,如图:
I/O多路复用之select函数分析

其中FD_ZERO宏将一个fd_set类型的所有位都置为0,对于select函数中间的三个参数,除了我们所感兴趣的条件外,其他的都可以是空指针,如果三个参数都被设置为空(NULL), 这样我们就获得了一个比sleep更精确的计时器。


使用宏进行如下操作时:  
1
2
3
4
5
6
7
8
fd_set readset, writeset;
FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_SET(0&readset);
FD_SET(
3&readset);
FD_SET(
1&writeset);
FD_SET(
2&writeset);
select(
4&readset, &writeset, NULLNULL);

效果如图:

I/O多路复用之select函数分析
我们将最大描述符加1是因为描述符从0开始,第一个参数是我们所要测试的描述符个数。


select函数的优点和缺点:
优点:
  • select函数可以处理多个socket描述符
  • select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。
  • select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024(软上限),可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低,也就是说1024个描述符的集合数量大小刚刚好。

什么是软上限?什么是硬上限?
软上限:一定要达到的目标,超过就不划算而已。
硬上限:超过就毫无价值。

缺点:
  • select调用,要遍历内核中就绪的描述符集合,内核把东西拷贝给用户,用户还要遍历一遍描述符集合。
  • select调用,要将用户态的描述符集合拷贝到内核态,再将内核态数据拷贝到用户态。
参考资料:
https://segmentfault.com/a/1190000003063859(LinuxIO模式及select.poll.epoll分析)(推荐阅读)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值