• Linux I/O复用中select poll epoll模型的介绍及其优缺点的比較


    关于I/O多路复用:

    I/O多路复用(又被称为“事件驱动”),首先要理解的是。操作系统为你提供了一个功能。当你的某个socket可读或者可写的时候。它能够给你一个通知。这样当配合非堵塞的socket使用时,仅仅有当系统通知我哪个描写叙述符可读了,我才去运行read操作。能够保证每次read都能读到有效数据而不做纯返回-1和EAGAIN的无用功。写操作相似。操作系统的这个功能通过select/poll/epoll之类的系统调用来实现。这些函数都能够同一时候监视多个描写叙述符的读写就绪状况,这样。**多个描写叙述符的I/O操作都能在一个线程内并发交替地顺序完毕,这就叫I/O多路复用,这里的“复用”指的是复用同一个线程。

    一、I/O复用之select

    1、介绍:
    select系统调用的目的是:在一段指定时间内。监听用户感兴趣的文件描写叙述符上的可读、可写和异常事件。poll和select应该被归类为这种系统调用,它们能够堵塞地同一时候探測一组支持非堵塞的IO设备,直至某一个设备触发了事件或者超过了指定的等待时间——也就是说它们的职责不是做IO,而是帮助调用者寻找当前就绪的设备。
    以下是select的原理图:
    这里写图片描写叙述

    2、select系统调用API例如以下:

    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

    fd_set结构体是文件描写叙述符集,该结构体实际上是一个整型数组,数组中的每一个元素的每一位标记一个文件描写叙述符。fd_set能容纳的文件描写叙述符数量由FD_SETSIZE指定。普通情况下,FD_SETSIZE等于1024,这就限制了select能同一时候处理的文件描写叙述符的总量。

    3、以下介绍一下各个參数的含义:
    1)nfds參数指定被监听的文件描写叙述符的总数。

    通常被设置为select监听的全部文件描写叙述符中最大值加1;
    2)readfds、writefds、exceptfds分别指向可读、可写和异常等事件相应的文件描写叙述符集合。这三个參数都是传入传出型參数,指的是在调用select之前,用户把关心的可读、可写、或异常的文件描写叙述符通过FD_SET(以下介绍)函数分别加入进readfds、writefds、exceptfds文件描写叙述符集,select将对这些文件描写叙述符集中的文件描写叙述符进行监听,假设有就绪文件描写叙述符,select会重置readfds、writefds、exceptfds文件描写叙述符集来通知应用程序哪些文件描写叙述符就绪。这个特性将导致select函数返回后。再次调用select之前,必须重置我们关心的文件描写叙述符,也就是三个文件描写叙述符集已经不是我们之前传入 的了。
    3)timeout參数用来指定select函数的超时时间(以下讲select返回值时还会谈及)。

    struct timeval
    {
        long tv_sec;        //秒数
        long tv_usec;       //微秒数
    };

    4、以下几个函数(宏实现)用来操纵文件描写叙述符集:

    void FD_SET(int fd, fd_set *set);   //在set中设置文件描写叙述符fd
    void FD_CLR(int fd, fd_set *set);   //清除set中的fd位
    int  FD_ISSET(int fd, fd_set *set); //推断set中是否设置了文件描写叙述符fd
    void FD_ZERO(fd_set *set);          //清空set中的全部位(在使用文件描写叙述符集前。应该先清空一下)
        //(注意FD_CLR和FD_ZERO的差别,一个是清除某一位,一个是清除全部位)

    5、select的返回情况:
    1)假设指定timeout为NULL,select会永远等待下去,直到有一个文件描写叙述符就绪,select返回。
    2)假设timeout的指定时间为0,select根本不等待,马上返回;
    3)假设指定一段固定时间,则在这一段时间内,假设有指定的文件描写叙述符就绪,select函数返回,假设超过指定时间,select同样返回。
    4)返回值情况:
    a)超时时间内,假设文件描写叙述符就绪,select返回就绪的文件描写叙述符总数(包含可读、可写和异常),假设没有文件描写叙述符就绪,select返回0;
    b)select调用失败时,返回 -1并设置errno。假设收到信号。select返回 -1并设置errno为EINTR。

    6、文件描写叙述符的就绪条件:
    在网络编程中,
    1)下列情况下socket可读:
    a) socket内核接收缓冲区的字节数大于或等于其低水位标记SO_RCVLOWAT;
    b) socket通信的对方关闭连接,此时该socket可读,可是一旦读该socket。会马上返回0(能够用这种方法推断client端是否断开连接);
    c) 监听socket上有新的连接请求。
    d) socket上有未处理的错误。


    2)下列情况下socket可写:
    a) socket内核发送缓冲区的可用字节数大于或等于其低水位标记SO_SNDLOWAT;
    b) socket的读端关闭。此时该socket可写。一旦对该socket进行操作。该进程会收到SIGPIPE信号。
    c) socket使用connect连接成功之后;
    d) socket上有未处理的错误。

    二、I/O复用之poll

    1、poll系统调用的原理与原型和select基本相似。也是在指定时间内轮询一定数量的文件描写叙述符。以測试当中是否有就绪者。

    2、poll系统调用API例如以下:

    #include <poll.h>
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);

    3、以下介绍一下各个參数的含义:
    1)第一个參数是指向一个结构数组的第一个元素的指针,每一个元素都是一个pollfd结构,用于指定測试某个给定描写叙述符的条件。

    struct pollfd
    {
        int fd;             //指定要监听的文件描写叙述符
        short events;       //指定监听fd上的什么事件
        short revents;      //fd上事件就绪后,用于保存实际发生的时间
    }。

    待监听的事件由events成员指定,函数在相应的revents成员中返回该描写叙述符的状态(每一个文件描写叙述符都有两个事件,一个是传入型的events,一个是传出型的revents。从而避免使用传入传出型參数。注意与select的差别),从而告知应用程序fd上实际发生了哪些事件。events和revents都能够是多个事件的按位或。
    2)第二个參数是要监听的文件描写叙述符的个数,也就是数组fds的元素个数;
    3)第三个參数意义与select同样。

    4、poll的事件类型:
    这里写图片描写叙述
    在使用POLLRDHUP时,要在代码開始处定义_GNU_SOURCE

    5、poll的返回情况:
    与select同样。

    三、I/O复用之epoll

    1、介绍:
    epoll 与select和poll在使用和实现上有非常大差别。

    首先,epoll使用一组函数来完毕,而不是单独的一个函数。其次。epoll把用户关心的文件描写叙述符上的事件放在内核里的一个事件表中。无须向select和poll那样每次调用都要反复传入文件描写叙述符集合事件集。

    2、创建一个文件描写叙述符,指定内核中的事件表:

    #include<sys/epoll.h>
    int epoll_create(int size);
        //调用成功返回一个文件描写叙述符。失败返回-1并设置errno。

    size參数并不起作用。仅仅是给内核一个提示。告诉它事件表须要多大。

    该函数返回的文件描写叙述符指定要訪问的内核事件表,是其它全部epoll系统调用的句柄。

    3、操作内核事件表:

    #include<sys/epoll.h>
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
        //调用成功返回0,调用失败返回-1并设置errno。

    epfd是epoll_create返回的文件句柄。标识事件表。op指定操作类型。

    操作类型有以下3种:

    a)EPOLL_CTL_ADD, 往事件表中注冊fd上的事件;
    b)EPOLL_CTL_MOD, 改动fd上注冊的事件;
    c)EPOLL_CTL_DEL, 删除fd上注冊的事件。

    event參数指定事件,epoll_event的定义例如以下:

    struct epoll_event
    {
        __int32_t events;       //epoll事件
        epoll_data_t data;      //用户数据
    };
    
    typedef union epoll_data
    {
        void *ptr;
        int  fd;
        uint32_t u32;
        uint64_t u64;
    }epoll_data;

    在使用epoll_ctl时,是把fd加入、改动到内核事件表中,或从内核事件表中删除fd的事件。

    假设是加入事件到事件表中,能够往data中的fd上加入事件events。或者不用data中的fd,而把fd放到用户数据ptr所指的内存中(由于epoll_data是一个联合体。仅仅能使用当中一个数据),再设置events。

    3、epoll_wait函数
    epoll系统调用的最关键的一个函数epoll_wait,它在一段时间内等待一个组文件描写叙述符上的事件。

    #include<sys/epoll.h>
    int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
        //函数调用成功返回就绪文件描写叙述符个数,失败返回-1并设置errno。

    timeout參数和select与poll同样。指定一个超时时间;maxevents指定最多监听多少个事件。events是一个传出型參数。epoll_wait函数假设检測到事件就绪,就将全部就绪的事件从内核事件表(epfd所指的文件)中复制到events指定的数组中。

    这个数组用来输出epoll_wait检測到的就绪事件,而不像select与poll那样。这也是epoll与前者最大的差别,下文在比較三者之间的差别时还会说到。

    四、三组I/O复用函数的比較

    同样点:
    1)三者都须要在fd上注冊用户关心的事件。
    2)三者都要一个timeout參数指定超时时间。
    不同点:
    1)select:
    a)select指定三个文件描写叙述符集,各自是可读、可写和异常事件,所以不能更加仔细地区分全部可能发生的事件。
    b)select假设检測到就绪事件,会在原来的文件描写叙述符上改动,以告知应用程序,文件描写叙述符上发生了什么时间,所以再次调用select时,必须先重置文件描写叙述符
    c)select採用对全部注冊的文件描写叙述符集轮询的方式,会返回整个用户注冊的事件集合,所以应用程序索引就绪文件的时间复杂度为O(n)。
    d)select同意监听的最大文件描写叙述符个数通常有限制。通常是1024。假设大于1024,select的性能会急剧下降;
    e)仅仅能工作在LT模式。

    2)poll:
    a)poll把文件描写叙述符和事件绑定,事件不但能够单独指定。并且能够是多个事件的按位或。这样更加细化了事件的注冊,并且poll单独採用一个元素用来保存就绪返回时的结果,这样在下次调用poll时。就不用重置之前注冊的事件;
    b)poll採用对全部注冊的文件描写叙述符集轮询的方式。会返回整个用户注冊的事件集合。所以应用程序索引就绪文件的时间复杂度为O(n)。
    c)poll用nfds參数指定最多监听多少个文件描写叙述符和事件,这个数能达到系统同意打开的最大文件描写叙述符数目。即65535。
    d)仅仅能工作在LT模式。

    3)epoll:
    a)epoll把用户注冊的文件描写叙述符和事件放到内核当中的事件表中。提供了一个独立的系统调用epoll_ctl来管理用户的事件,并且epoll採用回调的方式。一旦有注冊的文件描写叙述符就绪,讲触发回调函数,该回调函数将就绪的文件描写叙述符和事件复制到用户空间events所管理的内存。这样应用程序索引就绪文件的时间复杂度达到O(1)。
    b)epoll_wait使用maxevents来制定最多监听多少个文件描写叙述符和事件,这个数能达到系统同意打开的最大文件描写叙述符数目,即65535。
    c)不仅能工作在LT模式,并且还支持ET高效模式(即EPOLLONESHOT事件,读者能够自己查一下这个事件类型,对于epoll的线程安全有非常好的帮助)。

    select/poll/epoll总结:
    这里写图片描写叙述

  • 相关阅读:
    函数
    python操作文件
    POJ-2689-Prime Distance(素数区间筛法)
    POJ-2891-Strange Way to Express Integers(线性同余方程组)
    POJ-2142-The Balance
    POJ-1061-青蛙的约会(扩展欧几里得)
    Educational Codeforces Round 75 (Rated for Div. 2) D. Salary Changing
    Educational Codeforces Round 75 (Rated for Div. 2) C. Minimize The Integer
    Educational Codeforces Round 75 (Rated for Div. 2) B. Binary Palindromes
    Educational Codeforces Round 75 (Rated for Div. 2) A. Broken Keyboard
  • 原文地址:https://www.cnblogs.com/llguanli/p/8721103.html
Copyright © 2020-2023  润新知