• Select IO多路复用。


    内核态:运行操作系统的应用程序,可以操作调度使用硬件资源,比如CPU。

    用户态:用户态的应用程序,不能直接操作硬件资源,内存空间有限。 

    疑问:单线程处理多个请求,会不会被丢弃(线程在处理A请求的同时,B发请求过来了,B会不会被丢弃)?

    不会,因为不是CPU在处理B的IO请求,而是DMA直接内存访问。

    进程间的切换变化:

    1、保存处理机上下文,包括程序计数器和其他寄存器

    2、更新PCB信息

    3、把进程的PCB移入相应的队列,如就绪、阻塞队列。

    4、选择另一个进程执行,更新PCB

    5、更新内存管理的数据结构

    6、恢复处理机上下文。

    上下文切换资源开销是较大的。

      

    linux系统的五大IO

    1、同步阻塞IO BIO

    2、同步非阻塞IO NIO

    3、I/O多路复用 IO Multiplexing

    4、信号驱动I/O

    5、异步IO AIO

    select、poll、epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,

    一旦某个描述符就绪(一般指读就绪或者写就绪),能够通知程序进行相应的读写操作。

    但是select、poll,epoll本质上都是同步阻塞I/O,因为他们都需要在读写时间就绪以后自己负责进行读写,

    也就是整个读写过程是足鳃的,而异步I/O唔需要自己负责读写,异步IO的实现会把数据从内核态拷贝到用户空间。

     优势:比如select就是批量进行从用户态拷贝到内核态进行判断,但是如果是在用户态进行访问,会增大系统开销,会单个进行一次用户态和内核态的切换。

    Select(阻塞)

    int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    int select(
        int nfds,
        fd_set *readfds,
        fd_set *writefds,
        fd_set *exceptfds,
        struct timeval *timeout);
    // nfds:监控的文件描述符集里最大文件描述符加1
    // readfds:监控有读数据到达文件描述符集合,传入传出参数
    // writefds:监控写数据到达文件描述符集合,传入传出参数
    // exceptfds:监控异常发生达文件描述符集合, 传入传出参数
    // timeout:定时阻塞监控时间,3种情况
    //  1.NULL,永远等下去
    //  2.设置timeval,等待固定时间
    //  3.设置timeval里时间均为0,检查描述字后立即返回,轮询

    select函数监视的文件描述符分为3类,分别是writefds,readfds,exceptfds。调用后select函数会阻塞,直到有描述符就绪(有数据可读、可写、或者except),或者超时(timeout指定等待时间)函数返回,当select函数返回后可以通过遍历fdset,来找到就绪的描述符。

    服务端:创建一个线程不断接收客户端的连接请求,并把socket文件放入文件描述符的list中。

    while(1) {
      connfd = accept(listenfd);
      fcntl(connfd, F_SETFL, O_NONBLOCK);
      fdlist.add(connfd);
    }

    处理任务

    启动一个线程,不是调用select,将这批文件描述符list交给操作系统遍历

    while(1) {
      // 把一堆文件描述符 list 传给 select 函数
      // 有已就绪的文件描述符就返回,nready 表示有多少个就绪的
      nready = select(list);
      ...
    }

    不过,当 select 函数返回后,用户依然需要遍历刚刚提交给操作系统的 list。

    只不过,操作系统会将准备就绪的文件描述符做上标识,用户层将不会再有无意义的系统调用开销。

    while(1) {
      nready = select(list);
      // 用户层依然要遍历,只不过少了很多无效的系统调用
      for(fd <-- fdlist) {
        if(fd != -1) {
          // 只读已就绪的文件描述符
          read(fd, buf);
          // 总共只有 nready 个已就绪描述符,不用过多遍历
          if(--nready == 0) break;
        }
      }
    }

    select函数就是将文件描述符从用户态拷贝到内核态,交由内核来判断哪个FD有数据

    缺点:

    1、1024bitmp (有1024的限制,bitmap默认空间大小)

    2、rset 每次循环都必须从头开始,不可以重复使用

    3、用户-》内核态 开销  (rset从用户态到内核态,内核态判断是否有数据,但是还是存在着拷贝的开销)

    4、ON遍历 fd(i) (可以优化为只返回就绪的队列)

    poll

    用的pollfd{

    int f d

    short events;

    short revents;

    }

    执行流程:

    1、将文件描述符拷贝到内核态

    2、poll为阻塞方法,执行poll方法,如果有数据将fd的revents置为pollin

    3、方法返回后循环遍历查找那个fd的revent被置为pollin

    4、将revents重新复位便于复用

    5、对被置的fd进行处理

    解决了问题:

    1、解决了bitmap大小限制问题

    2、解决了rset复用问题(代码中处理后revent后,会置为0,恢复默认,就达到了重用)

    epoll

    epoll函数时非阻塞的

    epoll执行流程:

    1、当有数据的时候,会把相应的描述符置位,但是epoll没有revent标志位,所以并不是真正的置位。这时候会把有事件的描述符放到队首。

    2、epoll会返回有事件的描述符个数

    3、根据个数读取前N个描述符

    4、读取数据进行处理

    基本解决了select问题

    epoll

      select
    fd_set 使用数组实现
    1.fd_size 有限制 1024 bitmap
    fd【i】 = accept()
    2.fdset不可重用,新的fd进来,重新创建
    3.用户态和内核态拷贝产生开销
    4.O(n)时间复杂度的轮询
    成功调用返回结果大于 0,出错返回结果为 -1,超时返回结果为 0
    具有超时时间

    poll
    基于结构体存储fd
    struct pollfd{
    int fd;
    short events;
    short revents; //可重用
    }
    解决了select的1,2两点缺点

    epoll
    解决select的1,2,3,4
    不需要轮询,时间复杂度为O(1)
    epoll_create 创建一个白板 存放fd_events
    epoll_ctl 用于向内核注册新的描述符或者是改变某个文件描述符的状态。已注册的描述符在内核中会被维护在一棵红黑树上
    epoll_wait 通过回调函数内核会将 I/O 准备好的描述符加入到一个链表中管理,进程调用 epoll_wait() 便可以得到事件完成的描述符

    两种触发模式:
    LT:水平触发
    当 epoll_wait() 检测到描述符事件到达时,将此事件通知进程,进程可以不立即处理该事件,下次调用 epoll_wait() 会再次通知进程。是默认的一种模式,并且同时支持 Blocking 和 No-Blocking。
    ET:边缘触发
    和 LT 模式不同的是,通知之后进程必须立即处理事件。
    下次再调用 epoll_wait() 时不会再得到事件到达的通知。很大程度上减少了 epoll 事件被重复触发的次数,
    因此效率要比 LT 模式高。只支持 No-Blocking,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

  • 相关阅读:
    Qt之界面数据存储与获取(使用setUserData()和userData())
    UML中关联(Association)、聚合(Aggregation)和合成(Composition)之间的区别
    Entity Framework Model First下改变数据库脚本的生成方式
    keepalive学习
    函数、极限、连续
    C#集合基础与运用
    面向查询服务的参数化查询
    WinDbg 命令手册
    知识管理方法论
    项目管理Project
  • 原文地址:https://www.cnblogs.com/Alei777/p/16255871.html
Copyright © 2020-2023  润新知