多路IO(一)——IO多路复用

IO多路复用

如何解决同时“读鼠标”和“读键盘”的问题

  1. 多进程实现
  2. 多线程
  3. 将“读鼠标”和“读键盘”设置为非阻塞实现
  4. 多路IO

有关多路IO

多路IO的工作原理

  • 使用多路IO时,不需要多进程、多线程以“多线任务”方式实现,也不需要用到非阻塞,那么多路IO的实现原理又是什么?
  • 我们以阻塞读为例,来讲解多路IO的原理。
  • 图:

如果是阻塞写的话,需要将文件描述符加入写集合,不过我们说过对于99%情况,写操作不会阻塞,所以一般情况下对于写来说,使用多路IO没意义。

注意:对于多路IO来说,只有操作阻塞的fd才有意义,如果文件描述符不是阻塞的,使用多路IO没有意义

多路IO有什么优势

比如以同时读写鼠标、读键盘为例,如果使用。

  1. 多进程实现
    • 开销很大的,所以不建议这么做。
  2. 非阻塞方式
    • cpu空转,很消耗cpu资源,不建议。
  3. 多线程
    • 常用方法
  4. 多路IO
    • 使用多路IO时,多路IO机制由于在监听时如果没有动静的话,监听会休眠,因此开销也很低,相比多进程和非阻塞来说,多路IO机制也是不错的方式。

select和poll

多路IO有两种实现方式,分别是poll和select,其中select会比poll更常用些。

多路IO之select机制

select函数

  1. 函数原型

    int select(int nfds, fd_set *readfds, fd_set *writefds,
    fd_set *exceptfds, struct timeval *timeout);

    1. 功能

      监听集合中的描述符有没有动静,如果没有动静就阻塞。

      如果有动静就成功返回,返回值为集合中有动静的fd的数量

      什么是有动静?

      比如以读为例,如果fd有数据来了需要被read时,这就是动静。

    2. 参数

      • nfds:readfds、writefds、exceptfds这三个集合中值最大的那个描述符+1,用以说明需要关心描述符的范围,这个范围必须覆盖所有集合中的文件描述符。
      • 比如“读集合”中包含0,3,6这三个文件描述符,写集合和异常集合为NULL(没有)。
      • nfds == 6+1
      • 表示监听范围包含7个描述符,描述符因为是从0算起的,所以监听范围所包含的描述符为
      • 0、1、2、3、4、5、6
      • 疑问:集合中只包含了0、3、6三个,但是为什么需要监听的有这么多,只能说人家select机制就是这么实现的,这个没办法。
    3. readfds、writefds、exceptfds:读、写、异常集合。

      -readfds:读集合,放读会阻塞的文件描述符

      **-**writefds:写集合,放写会阻塞的文件描述符

      -exceptfds:放会异常出错的文件描述符

      常用的是读集合,写集合和异常集合基本用不到,所以这里不做介绍,写、异常集合不用时就写NULL

      -至于如何将文件描述符放入集合中,我们使用如下带参宏来实现。

      void FD_CLR(int fd, fd_set *set); //将fd从集合set中清除,这个宏不需要
      int FD_ISSET(int fd, fd_set *set); //判断是不是set中的fd有动静
      void FD_SET(int fd, fd_set *set); //将fd放到集合set中
      void FD_ZERO(fd_set *set); //将整个集合全部清空

    4. 比如:

fd_set readfds; //定义一个读集合
FD_ZERO(&readfds);//先将集合全部清空
FD_SET(fd, &readfds);//将fd放到readfds集合中

5.timeout:用于设置阻塞超的时间

select函数监听集合时,如果没有任何动静的话就阻塞(休眠)。

如果timeout被设置为NULL的话,select会永远阻塞下去,直到被信号中断或者集合中的某些文件描述符有动静了。

如果你不想休眠太久的话,就可以设置超时时间,如果时间到了但是集合中的fd没有任何动静。

select就返回,然后不再阻塞,超时时的返回的值为0。

成员如下:

struct timeval {
	long tv_sec;  /*seconds(秒)*/
	long tv_usec; /*microseconds(微妙)*/
};

tv_sec:设置秒

tv_usec:设置微妙

时间精度为微秒,也就是说可以设置一个精度为微妙级别的超时时间。

由于select函数有超时功能,实际上可以使用select模拟出一个微秒级精度的定时器,大家下去后可以自己去网上查阅资料研究下怎么实现?

可以熟悉select函数

返回值:

失败返回-1,errno被设置。

select调用出错的情况有很多种,比如select在阻塞时被信号唤醒从而导致出错返回。

errno被设置为EINTR错误号,这个错误号表示函数是被信号中断而出错返回的。

如果不想被信号打断

  • 我们可以自己忽略,屏蔽这些信号
  • 手动重启select的调用

技巧来的

label:

ret = select(...);

if(ret == -1 && errno == EINTR) goto label;

else if(ret == -1) print_err(...);

0:超时时间并且集合中没有一个描述符有响应时,就返回。

大于0:集合中fd有动静时,函数返回有动静的文件描述符的数量。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值