zoukankan      html  css  js  c++  java
  • IO复用三种方式

    简介

    IO复用技术,简单来说就是同时监听多个描述符。在没有用到IO复用以前,只能是一个线程或一个
    线程去监听,服务端同时有多个连接的时候,需要创建多个线程或者进程。而且,并不是所有的连
    接是一直在传输这数据,可能只是连接后啥都没干,如果这样,进程就啥都没干。
    现在有了IO复用技术,只有描述符就绪的时候才去处理,这样就很方便了。
    IO复用的使用大概是这样:
    设置要监听的描述符以及需要监听的事件

    监听事件,一直阻塞直到有描述符就绪

    遍历所有就绪的描述符,并进行相应处理
    IO复用有三种方式,分别是select, poll, epoll

    select

    select函数原型

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

    参数详解:
    nfds:nfds参数指定的是被监听的文件描述符的总数,一般设置成最大的描述符加上1,因为描述
    符是从0开始的。
    readfds:需要监听读事件的描述符集
    writefds:需要监听写事件的描述符集
    exceptfds:需要监听异常事件的描述符集
    timeout:设置超时时间,如果传递的是NULL,则select会一直阻塞
    一般的使用步骤:
    1 设置FD_SET集合,如果要同时监听多种事件,则需要使用多个描述符集合
    2 调用select,select会通过更改描述集,只留下准备好的描述符,所以需要在调用前保留一份
    3 同时描述符需要在数组中存储一份,这个通过遍历数组和FD_ISSET来判断是否准备好,若准备好则去执行相关任务

    缺点:
    select 最大支持的描述符有限一般是1024。
    同时select内部的操作是等待描述符集合,必然需要进入到内核态,所以,每次需要把描述符集合
    从用户空间拷贝到内核空间,这样是特别消耗资源的,同时select这种方式,只保留准备好的描述符
    这样,使用特别不方便,每次还需要对数组进行遍历来判断描述符是否在准备好的集合当中。

    poll

    poll相对于select的优点就是解决了描述符的限制问题,但是性能并不好。poll也是需要通过遍历
    的方式来判断描述符的准备状态,当描述符较多时,则就尴尬了。

    函数原型:

    int poll(struct pollfd *fds, nfd_t nfds, int timeout);
    

    struct pollfd
    {
    int fd; //文件描述符
    short events; //注册的事件
    short revents; //实际发生的事件
    }
    参数详解:
    fds:需要监听的数组,数组里的每个结构体都设置好描述符和需要注册的事件,如果有多个,使用或('|')操作符
    nfds:数组的长度
    timeout:指定超时值,单位是毫秒
    常用的宏:
    POLLIN:数据(包括普通数据和优先数据)可读
    POLLOUT:数据(包括普通数据和优先数据)可写

    epoll

    epoll 相比select和poll不同,epoll将监听的描述符集存放在内核区,免去了select和poll每次调用
    都需要将描述符集从用户空间拷贝到内核空间的消耗。同时epoll的对于已经准备好的描述符处理比较方便。
    还增加了一些新的特性,比如ET和LT两种触发模式,
    epoll提供了三个函数:

    int epoll_create(int size);
    

    epoll_create用来创建一个描述符,指向内核创建的描述符集。之后的操作都通过描述符来操作。

    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    

    epfd:传入的是指向描述符集的描述符
    op:有三个宏:
    EPOLL_CTL_ADD: 添加fd上的注册事件
    EPOLL_CTL_MOD: 修改fd上的注册事件
    EPOLL_CTL_DEL: 删除fd上的注册事件
    fd:需要操作的描述符
    event:
    struct epoll_event
    {
    __uint32_t events; //epoll事件
    epoll_data_t data; //用户数据
    }
    typedef union epoll_data_t
    {
    void *ptr;
    int fd;
    uint32_t u32;
    uint64_t u64;
    } epoll_data_t;

    int  epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
    

    函数详解:
    返回值:返回准备好的描述符的个数
    epfd:指向描述符集的描述符
    events:这里是作为函数传出使用,epoll_wait之后会设置这个数组,之后再遍历这个数组即可
    maxevents:指定最多监听的描述符的个数
    timeout:超时时间,和poll相同。

    epoll有两种触发模式,一种是LT(条件触发)一种ET(边沿触发)
    LT条件触发:
    当满足某个条件,就会触发,如果没有去处理,会一直去触发
    LT是默认的触发方式
    ET边沿触发:
    当满足某个条件之后触发一次,如果未处理,就不管了。
    在设置触发事件的时候或上 EPOLLET
    那么问题来了?
    如果之后还有数据到来,还会触发吗?
    如果会,那么之前未处理的数据还在吗,会丢弃吗?
    如果之后还有新的数据来,依然会触发,同时之前缓冲的数据可以一块读出来。

  • 相关阅读:
    C++ XML解析之TinyXML篇[转]
    TinyXML:一个优秀的C++ XML解析器[转]
    nginx 出现413 Request Entity Too Large问题的解决方法
    redis配置认证密码
    《Discuz安装时候出现乱码 -- 问题解决方法》
    MySQL创建用户与授权
    CentOS 7 安装mysql
    setfacl命令 来自: http://man.linuxde.net/setfacl
    install pip3 for python 3.x
    自己制作ssl证书:自己签发免费ssl证书,为nginx生成自签名ssl证书
  • 原文地址:https://www.cnblogs.com/0x12345678/p/5978975.html
Copyright © 2011-2022 走看看