zoukankan      html  css  js  c++  java
  • IO多路复用的理解

    最近看了《后台开发核心技术与应用实践》有关select、poll和epoll部分以及相关的一些博客,学习了这三个函数的使用方法和区别,写一个易理解的总结。

    IO多路复用

    之前程序中使用的IO函数都是同步的,无论阻塞式还是非阻塞式,在数据从内核拷贝到用户空间过程,用户线程都是被阻塞的。非阻塞IO只是当内核还没准备好数据时立即返回不等待,需要用户自己去不断检查内核数据是否准备好,依然不高效。IO多路复用提出了新的思路,将IO过程分为等待内核数据准备好和读取/写入内核两部分。一个IO函数监控多个IO可读/可写事件,任意1个IO设备准备好时返回(需要代码中轮询查看是哪个IO文件描述符,什么事件),再调用对应的read/write函数操作,减少不必要的等待时间,高效了很多。具体的实现有select、poll和epoll三种。

    select

    基于位图型集合,通过宏和fd_set结构体设置事件和检测事件的发生。最早被提出所以可移植性最好,该实现有以下缺点:1.每次调用都需要将fd集合从用户空间拷贝到内核空间,完成后再从内核空间拷贝回用户空间,fd很多时开销很大。2.实现过程是在内核中遍历所有fd,fd很多时开销很大。3.支持同时可监控的文件描述符数少,1024或2048。4.fd_set在select返回后会改变,所以再次调用select时需要再次设置fd_set
    原型:int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
    fd_set set;
    FD_ZERO(&set); /* 将set清零*/
    FD_SET(fd, &set); 将fd加入set
    FD_CLR(fd, &set); 将fd从set中清除
    FD_ISSET(fd, &set); 测试fd是否在set中,如果在则为true
    maxfdp是描述符最大值加1,指定描述符的范围
    timeout是NULL则无限等待,阻塞模式;timeout等于0则立即返回,非阻塞模式;timeout大于0则为超时时间,timeout内阻塞,到达超时时间不管怎样一定返回。
    返回值:文件无变化返回0,有变化返回正值

    poll

    要比select高级一些,实现和select大致相同,内核中遍历所有文件描述符。使用链表式集合,不需要重复设置监控事件,同时监控文件描述符数远大于select。缺点也和select大致相同:1.每次调用都需要将pollfd集合从用户空间拷贝到内核空间,完成后再从内核空间拷贝回用户空间。2.实现过程是在内核中遍历所有pollfd

    原型: int poll(struct pollfd* fds, unsigned int nfds, int timeout);
    成功时返回revents不为0的文件描述符个数,0表示超时但没有任何事件发生,-1表示失败
    struct pollfd{
         int fd; 文件描述符
         short events; 等待的事件,掩码控制多个事件
         short revents; 实际发生的事件,掩码控制多个事件
    }
    fds链表是要监控文件描述符的pollfd链表
    nfds指定描述符个数
    timeout:0表示立即返回,非阻塞;正值表示等待的毫秒数;负值表示无限等待,阻塞模式
    返回值:revents不为0的pollfd数,-1表示出错
    需要头文件#include <poll.h>
    events和revents中的事件:
    合法事件:
    POLLIN 有数据可读
    POLLRDNORM 有普通数据可读
    POLLRDBAND 有优先数据可读
    POLLPRI 有紧迫数据可读
    POLLOUT 写数据不会导致阻塞
    POLLWRNORM 写普通数据不会导致阻塞
    POLLWRBAND 写优先数据不会导致阻塞
    POLLMSGSIGPOLL 消息可用不会导致阻塞
    非法事件:
    POLLER 文件描述符发生错误
    POLLHUP 文件描述符挂起事件
    POLLNVAL 文件描述符非法

    epoll

    通过3个函数来实现,更加高效,当前使用也最多。在epoll_ctl中注册事件到epoll文件描述符中,把fd全部拷贝进内核,而不是在epoll_wait中重复拷贝。实现中内核通过为每个fd指定一个回调函数,当fd就绪时调用回调函数把就绪fd加入一个就绪链表,epoll_wait只需要查看这个就绪链表是否有就绪fd就可。可监控文件描述符数是系统可同时打开文件数(超过10万)
    原型:
         int epoll_create(int size);  //返回epoll文件描述符,size表示要监听的数目 (这个返回的fd要记得close)
         int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); //epoll事件注册函数
              epfd是epoll_create返回的值
              op是动作:EPOLL_CTL_ADD/EPOLL_CTL_MOD/EPOLL_CTL_DEL分别表示:注册fd到epfd,修改已注册的fd,从epfd删除1个fd
              fd是要监听的fd
              event是告诉内核要监听什么事件
              struct epoll_event{
                   __uint32_t events;  //epoll events
                   epoll_data_t data; //user data variable
              }
              event是宏的集合:EPOLLIN可读;EPOLLOUT可写;EPOLLPRI紧急数据可读;EPOLLERR发生错误;EPOLLHUP被挂断;EPOLLET将EPOLL设置为边缘触发模式;EPOLLONESHOT只监听1次事件
         int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);  //等待事件发生,events是返回的事件链表;maxevents是events链表元素个数,timeout是等待毫秒数(0表示立即返回,非阻塞;正值表示等待的毫秒数;负值表示无限等待,阻塞模式 ),函数返回值是需要处理的事件数,通过events返回需要处理的事件。(通过events[i].data.fd和events[i].events匹配判断)
    需要#include <sys/epoll.h>
     
    转载请注明出处

    参考:

    《后台开发核心技术与应用实践》

    http://www.cnblogs.com/Anker/p/3265058.html 

  • 相关阅读:
    python yield yield from
    python isinstance()与type()的区别
    python isinstance用法
    python 展开嵌套的序列
    python getmtime() 最近修改文件内容的时间
    python getctime() 文件最后一次的改变时间
    python getatime() 查看文件的访问时间
    python模拟随机游走
    getopt例子
    matplotlib 代码风格
  • 原文地址:https://www.cnblogs.com/liulaoshi/p/6984840.html
Copyright © 2011-2022 走看看