Linux || I/O复用

12 篇文章 0 订阅

IO复用:同时监听多个文件描述符

1.select

  • 面试高频
  • select与poll有什么缺点 => 使用epoll
  • epoll的ET模式和LT模式

重点关注的是读事件EPOLLIN,因为读事件前提需要缓冲区中有数据才能读,没有数据就会被阻塞,所以需要先判断接受缓冲区中是否有数据,写事件(EPOLLOUT)只需要缓冲区为空就可写

  1. select:同时监听多个文件描述符,最多1024个,读,写,异常,集合
    内核:轮询方式
    时间复杂度:O(n)
  2. poll:支持比select更多的文件描述符,更多的事件类型
    内核:轮询方式
    时间复杂度:O(n)
    select与poll的缺点
    - 找到就绪描述符 需要遍历所有描述符O(n)
    - 内核中是轮询的方式
    - 每次都需要向内核传递描述符和事件
  3. epoll:解决select与poll的缺陷
  1. select:就绪描述符数目,不知道具体是谁,数目不多的情况下
    读、写、异常事件,最多1024个
    检测5秒内是否有数据
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include <sys/time.h>
#include<sys/select.h>

int main()
{
   int fd = 0;//stdin 0 stdout 1 stderr 2 键盘对应的描述符
   fd_set fdset;  //集合 收集描述符 被select检测
   while(1)
   {
      struct timeval tv = {5,0}; //超时时间
      FD_ZERO(&fdset); //清空集合中的每个位
      FD_SET(fd,&fdset); //fd添加到集合中 多个数据需要循环
      
      int n = select(fd+1,&fdset,NULL, NULL,&tv); //得到就绪描述符的数目,无法知道是谁 通过循环找到描述符为1的数据
             //fd+1 只检测可能有数据的之前的位 
      if(n == -1)
      {
       printf("err \n"); //失败
       break;
      }
      if(n == 0)
      {
        printf("time out \n");//时间结束
      }
      else
      {   //n>0 有数据
        if(FD_ISSET(fd,&fdset))  //成功
        {
          char buff[128] = {0};
          read(fd,buff,127};    //读取数据 输出
          printf("read:%s\n",buff);
        }
      }
   }
}
   

2. poll

  1. poll:同时监听多个文件描述符
    支持比select更多的文件描述符;使用数组来实现

#include<poll.h>
POLLIN:数据可读
POLLOUT:数据可写
POLLRDHUP:TCP连接被对方关闭,或对方关闭了写操作

3. epoll

  1. epoll:适合于文件描述符特别多的时候
    使用一组函数来实现

#include<sys/epoll.h>

  • epoll_create():创建内核事件表 在内核空间存储的 不需要每次都要传递描述符和事件 只用向内核中传递一次事件 使用到的数据结构是红黑树
  • epoll_ctl():
  • epoll_wait():获取就绪描述符

4. ET LT模式

  1. ET 、LT模式
    ET epoll 高效模式 边沿触发
    LT select poll epoll 水平触发

4.1 LT模式

  • LT模式

像妈妈叫吃饭时,叫一次 不动,就在叫一次 还不动就继续叫 直到你吃饭为止

LT
当客户端输入的数据为hello时,epoll_wait返回了6次,每次只读一个字符,读完h时epoll_wait就会继续发送通知,直至整个数据读完为止
在这里插入图片描述

4.2 ET模式

  • ET模式
  • TCP协议 不存在数据丢失

ET
例如一次只能处理一个字符,客户端发过来的数据是hello,则第一次只处理h,剩下的存储在缓冲区中,等到下一次epoll_wait再有事件通知时,就会输出 c ,在下一次就是 l ;

  • 为了在一次事件通知时能够读完所有数据,需要循环读取数据
  • 不循环则一次只能读一个,循环读取数据时,如果recv读到最后为空时就会发生阻塞

#include<unistd.h>
#include<fcntl.h>

  • int fcntl(int fd,int cmd…) //增加非阻塞的的属性

在这里插入图片描述

ET

  • 设置非阻塞后 可使用循环 ,能够解决防止因数据读完后为空导致阻塞的情况
  • 使用循环就能够使ET模式下,epoll_wait通知一次时就能够读完所有数据,
15 void setnonblock(int fd)  //给fd设置非阻塞属性
 16 {
 17         int oidfl = fcntl(fd,F_GETFL);
 18         int newfl = oidfl | O_NONBLOCK;
 19         if(fcntl(fd,F_SETFL,newfl) == -1)
 20         {
 21                 printf("fcntl err\n");
 22         }
 23 }

epoll_create 思路:创建内核事件表
在这里插入图片描述
epoll_ctl():向内核事件表中添加文件描述符

5. libevent

libevent:服务器端编程常用,对poll,epoll进行封装

定义实例 Base
创建事件
注册(添加)到libevent
事件循环 开始检测 底层调用的是select poll epoll_wait等函数
将就绪的事件调用回调函数fun()方法

win 句柄:文件描述符
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值