精华内容
下载资源
问答
  • 五种io模型
    千次阅读
    2018-08-21 15:36:52

    高级IO模型共有五种:

    • 阻塞IO: 在内核将数据准备好之前, 系统调用会一直等待. 所有的套接字, 默认都是阻塞方式。
    • 非阻塞IO: 如果内核还未将数据准备好, 系统调用仍然会直接返回, 并且返回EWOULDBLOCK错误码    -----  非阻塞IO一般需要程序员循环的方式反复尝试读写文件描述符, 这个过程称为轮询. 这对CPU来说是较大的浪费, 一般只有特定场景下才使用。
    • 信号驱动IO:内核将数据准备好的时候, 使用SIGIO信号通知应用程序进行IO操作。
    • IO多路转接: 表面看起来与阻塞IO类似. 实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态。
    • 异步IO: 由内核在数据拷贝完成时, 通知应用程序(信号驱动是告诉应用程序何时可以开始拷贝数据).

    五种IO模型的比较

    任何IO过程都包含了两个步骤:等待和拷贝。而一般情况下,等待消耗的时间往往远大于数据拷贝的时间。那么如果我们减少了等待的时间,那么IO过程小号的时间就会更少。

    同步通信和异步通信:  这里一定也要注意与之前在进程和线程处的同步和互斥理解错误

    同步:在系统发出一个调用时,在没有得到响应的结果之前,这个调用不会返回,一旦这个调用返回,那么一定得到了返回值。也就是由调用者来等待这调用的返回结果。

    异步:在系统发出调用以后,这个调用就直接返回,并不会带回来这个调用的返回结果。被调用者通过状态、通知来通知调用者,或者通过回调函数来处理这个调用。

    这里我们重点讨论IO多路转接:

    讨论之前,我们先联想这样一种情况----当我们的程序要从多个文件描述符读取数据时,我们应该怎么办呢?

            如果采用之前学习过的知识采用多进程或者多线程,那么我们就不得不考虑进程和线程的终止、同步互斥。

            另一个方法是继续采用一个进程,但是采用非阻塞的IO来进行数据的读取。基本方法是将多个输入的文件描述符设置为非阻塞,对第一个描述符发出一个read,如果该输入上有数据,那么就读取数据并处理它。然后对后面的文件描述符做同样的处理。然后等待若干秒后,对这些描述符进行轮询。这种方法的缺点是浪费CPU的时间。因为大多数时间这些文件描述是无数据可读的,但是仍然要花费大量的时间反复的执行read系统调用。

            有一种方法是异步IO,其基本思想是告诉内核,当前描述符已经准备好了,可以进行IO操作,用一个信号通知它。这种技术往往存在两个问题。第一,并非所有的系统都支持这种机制。第二,这种信号对于每个进程来说只有一个,当这个信号到达时,进程无法判断是哪个描述符准备好IO了。

            一种比较好的技术就是IO多路转接。先构造一张有关文件描述符的列表,然后调用一个函数,直到这些描述符中一个已经准备好进行IO时,这个函数才返回。在返回时,它告诉进程哪些文件描述符已经准备好可以进行IO。

    在以下几种情况下,网络程序需要使用I/O多路转接。

    • 客户端程序要同时处理多个socket。
    • 客户端程序要同时处理用户输入金额网络连接。
    • TCP服务器需要同时监听socket和链接socket。----这是IO多路转接使用最多的场合
    • 服务器要同时处理TCP请求和UDP请求。
    • 服务器摇头挺熟监听多个端口,或者处理多种服务。

    需要注意的是,IO多路转接技术虽然能监听多个文件描述符,但是它本身是阻塞的,并且当多个文件描述符同时就绪时,如果不采用额外的措施,程序就只能按顺序依次处理其中的每一个文件描述符,这使得服务器程序看起来像是串行工作。如果要实现并发,还需要采用多进程和多线程等手段。

    select:在一段指定时间内,监听用户感兴趣的文件描述符上的可读、可写和异常事件。

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

    返回值:成功准备就绪的文件描述符,若超时则返回0,若出错则返回-1

    nfds:指定被监听的文件描述符的总数。它通常被设置为select监听的所有文件描述符中最大值加1因为文件描述符是从0开始计数的。

    readfds、writefds、exceptfds 分别表示可读、可写和异常等事件对应的文件描述符的集合。进程调用select函数时,通过这三个参数传入自己感兴趣的文件描述符。select调用返回时,内核将修改它们来通知进程哪些文件描述符已经就绪。

    这三个参数都是fd_set结构体指针类型,fd_set结构体仅包含一个整形数组,每个元素的每一位(bit)标记一个文件描述符

    由于位操作过于繁琐,我们应该使用下面一系列宏来访问fd_set结构体中具体的位。

    void FD_CLR(int fd, fd_set *set);
    int  FD_ISSET(int fd, fd_set *set);       
    void FD_SET(int fd, fd_set *set);       
    void FD_ZERO(fd_set *set);

    timeout:指定slect函数的超时时间,它是一个timeval类型的结构体指针。

    timeval == NULL表示永远等待。

    timeout->tv_sec == 0 && timeout->tv_usec == 0   表示完全不等待

    timeout->tv_sec != 0 || timeout->tv_usec != 0 指定等待的秒数和微秒数

     struct timeval {
            long    tv_sec;         /* seconds */
            long    tv_usec;        /* microseconds */
    };
    
    
    struct timespec {
            long    tv_sec;         /* seconds */
            long    tv_nsec;        /* nanoseconds */
    };
    

    常见的程序片段为

    fs_set readset;
    FD_SET(fd,&readset);
    select(fd+1,&readset,NULL,NULL,NULL);
    if(FD_ISSET(fd,readset))
    {
        //do_something
    }

    读就绪:

    • socket内核中, 接收缓冲区中的字节数, 大于等于低水位标记SO_RCVLOWAT. 此时可以无阻塞的读该文件描述符, 并且返回值大于0。
    • socket TCP通信中, 对端关闭连接, 此时对该socket读, 则返回0。
    • 监听的socket上有新的连接请求。
    • socket上有未处理的错误。

    写就绪:

    • socket内核中, 发送缓冲区中的可用字节数(发送缓冲区的空闲位置大小), 大于等于低水位标记SO_SNDLOWAT, 此时可以无阻塞的写, 并且返回值大于0。
    • socket的写操作被关闭(close或者shutdown). 对一个写操作被关闭的socket进行写操作, 会触发SIGPIPE信号。
    • socket使用非阻塞connect连接成功或失败之后。
    • socket上有未读的错误。

    异常就绪:

    • socket上接收到外带数据

    select的特点:

    可监控文件描述符取决于sizeof(fdset)的值。

    将fd加入select监控集的同时,还需要一个使用一个数据结构array来保存放到select监控集中的fd。

             一是用于在select 返回后,array作为源数据和fdset进行FDISSET判断。

             二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描arr扫描array的同时取得fd最大值maxfd,用于select的第一个参数。

    select的缺点:

    每次调用select都要手动设置fd集合,从接口使用角度来说也不方便。

    每次调用select都要把fd集合从用户态拷贝到内核态,在fd数量较多时系统开销会变得相当大。

    每次调用select都要在内核遍历传过来的所有fd,在fd数量较多时系统开销会比较大。

    select所支持的文件描述符的数量太少。

    select 实现回显服务器:  select版本的回显服务器

    poll:用法类似于select。

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

    // pollfd结构
    struct pollfd {
        int fd; /* file descriptor */
        short events; /* requested events 
        short revents; /* returned events */
    };

    fds:poll函数监听的结构体列表,每个元素中包含了三个部分的内容,文件描述符,监听的事件集合,返回的事件集合。

    nfds:表示fds数组的长度。

    返回值:成功返回准备就绪的描述符数,超时返回0,出错返回-1。

    socket就绪的条件:    同select。

    poll的优点:

            1.poll不会为每个状态(读、写、异常)都设置一个描述符集,而是构造一个pollfd数组,每个数组元素指定一个描述符编号及其所关心的状态。接口使用起来比select更方便。

            2.poll没有最大socket数量限制。(但是数量过多性能也会下降)

    poll的缺点:

            1.和select函数一样,poll返回之后,需要轮询pollfd来获得就绪的文件描述符。

            2.每次调用poll都要把大量的pollfd从用户态拷贝到内核中。

            3.同时连接的大量客户端在同一时刻可能只有很少的处于就绪状态, 因此随着监视的描述符数量的增长,其效率也会线性下降

    poll实现简单的回显服务器:epoll版本的回显服务器

    epoll:为了处理大量句柄而做了改进的epoll。它时公认的Linux2.6下最好的多路I/O就绪通知方法。

    epoll三个系统调用:

    1. int epoll_create(int size);

            创建一个epoll的句柄。自从linux2.6.8之后,size参数是被忽略的。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

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

            epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。

    • 第一个参数是epoll_create()的返回值(epoll的句柄)
    • 第二个参数指定操作类型,用三个宏来表示
    EPOLL_CTL_ADD:注册新的fd到epfd中;
    
    EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
    
    EPOLL_CTL_DEL:从epfd中删除一个fd;
    • 第三个参数是需要监听的fd
    • 第四个参数是告诉内核需要监听什么事
    //保存触发事件的某个文件描述符相关的数据(与具体使用方式有关)
    typedef union epoll_data {
        void *ptr;
        int fd;
        __uint32_t u32;
        __uint64_t u64;
    } epoll_data_t;
    //感兴趣的事件和被触发的事件
    struct epoll_event {
        __uint32_t events; /* Epoll events */
        epoll_data_t data; /* User data variable */
    };
    

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

           收集在epoll监控的事件中已经发送的事件底层使用红黑树实现的,是一颗空的红黑树,之后的添加,修改,删除有epoll_ctl维护,红黑树节点中中存放的是文件描述符及其所关心的事件还会创建一个队列——就绪队列:都就绪事件的有序结点。时间复杂度O(1)

           创建好epoll模型之后,底层事件一旦就绪,底层操作系统以回调方式激活红黑树中特定文件描述符的结点,(通知上层对应的文件描述符的事件已经发生,) 激活:底层有数据就绪,将该节点及其所关心的事件生成一个新结点放入就绪队列中。

    • 参数events是分配好的epoll_event结构体数组.
    • epoll将会把发生的事件赋值到events数组中 (events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存).
    • maxevents告之内核这个events有多大,这个maxevents的值不能用于创建epoll_create()时的size。
    • 参数timeout是超时时间 (毫秒,0会立即返回,-1是永久阻塞).
    • 如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时, 返回小于0表示函数调用失败.

    epoll的工作原理:

            epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。

             另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

    epoll的工作方式:  默认的是水平触发(LT)模式。

    一个典型的场景是:

    1.把一个tcp socket添加到epoll描述符
    2.这个时候socket的另一端被写入了2KB的数据
    3.调用epoll_wait,并且它会返回. 说明它已经准备好读取操作
    4.调用read,只读1KB的数据
    5.继续调用epoll

    水平触发(LT):高效的poll。epoll_wait检测到文件描述符上有时间发生并将此事件通知给应用程序后,应用程序可以不立即处理该事件,这样,当程序下一次调用epoll_wait的时候,epoll_wait还会向应用程序通知这个事件,直到该事件被处理。

    当epoll检测到socket上事件就绪的时候, 可以不立刻进行处理. 或者只处理一部分.
        如上述的例子, 由于只读了1K数据, 缓冲区中还剩1K数据, 在第二次调用 epoll_wait时,poll_wait仍然会立刻返回,并通知socket读事件就绪。
        直到缓冲区上的数据被全部读取,epoll_wait才不会立刻返回。
        支持阻塞读写和非阻塞读写。
    

    边缘触发(ET):当epoll_wait检测到文件描述符上有事件发生并将此事件告知应用程序后,应用程序必须立即处理该事件。因为后续的epoll_wait调用将不再向应用程序通知这个事件。ET模式很大程度上降低了同一个epoll事件被重复触发的次数,因此效率要比LT模式高。

    当epoll检测到socket上事件就绪时, 必须立刻处理
        如上述的例子, 虽然只读了1K的数据, 缓冲区还剩1K的数据, 在第二次调用epoll_wait的时候,epoll_wait不会再返回了.
        在ET模式下文件描述符上的时间就绪后就只有一次处理机会。
        ET模式的性能比LT模式性能更高(epoll_wait返回次数减少了许多),Nginx默认采用ET模式的epoll
        只支持非阻塞读写。

     

    epoll的优点:

    • 文件描述符数目无上限: 通过epoll_ctl()来注册一个文件描述符, 内核中使用红黑树的数据结构来管
      理所有需要监控的文件描述符.(对比select的有限个)。
    • 基于事件的就绪通知方式: 一旦被监听的某个文件描述符就绪, 内核会采用类似于callback的回调机制,迅速激活这个文件描述符. 这样随着文件描述符数量的增加, 也不会影响判定就绪的性能;(对比poll的大量文件描述符时的低效率)
    • 维护就绪队列: 当文件描述符就绪, 就会被放到内核中的一个就绪队列中. 这样调用epoll_wait获取就绪文件描述符的时候, 只要取队列中的元素即可, 操作的时间复杂度是O(1)。
    • 内存映射机制: 内核直接将就绪队列通过mmap的方式映射到用户态. 避免了拷贝内存这样的额外性
      能开销。

    epoll的使用场景:epoll的高性能,具有一定的使用场景,如果场景选择错误,epoll的性能可能适得其反。

            对于多连接,且多连接中只有一部分链接比较活跃的时候,epoll的性能提升最为明显。

            典型的例子:一个需要处理上万个客户端的服务器;各种互联网APP的入口服务器。。

    epoll使用的一般过程:

    1.调用epoll_create() 创建一个epoll句柄,使用结束要记得关闭这个句柄。

    2.调用epoll_ctl()将要监控的文件描述符进行注册

    3.调用epoll_wait()等待文件描述符就绪。

    epoll实现简单的回显服务器(LT模式):epoll版本的回显服务器

    更多相关内容
  • 主要介绍了Linux 下的五种 IO 模型详细介绍的相关资料,需要的朋友可以参考下
  • IO模型 | 常见的五种IO模型介绍

    千次阅读 2021-11-18 20:47:01
    常见IO模型 对于一次 IO 访问,它会经历两个阶段:等待数据准备就绪 (Waiting for the data to be ready);将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)。 举例来说: 读函数:...

    目录

    常见IO模型

    1. 阻塞IO模型

    2. 非阻塞I/O模型

    3. I/O复用

    4. 信号驱动IO模型

    5. 异步IO

    一个形象的示例说明


    常见IO模型

    对于一次 IO 访问,它会经历两个阶段:等待数据准备就绪 (Waiting for the data to be ready);将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)。

    举例来说:

    读函数:分为等待系统可读真正的读

    写函数:分为等待网卡可以写真正的写

    说明:等待就绪的阻塞是不使用 CPU 的,是在“空等”。而真正的读写操作的阻塞是使用 CPU 的,真正在“干活”,而且这个过程非常快,属于 memory copy,宽带通常在 1GB/s 级别以上,可以理解为基本不耗时。

    img

    1. 阻塞IO模型

    img

    2. 非阻塞I/O模型

    非阻塞IO通过进程反复调用IO函数,采用轮询,占用CPU。

    img

    3. I/O复用

    主要是select和epoll,对一个IO端口,两次调用,两次返回,能实现对多个IO端口进行监听。多个连接共用一个等待机制。

    img

    4. 信号驱动IO模型

    首先开启接口信号驱动IO功能,通过系统调用sigation执行一个信号处理函数(此信号调用直接返回,进程继续工作)。当数据准备就绪时,生成一个signal信号,通知应用程序来取数据。

    img

    5. 异步IO

    告知内核启动某个操作,并让内核在整个操作完成的的那个之后(将数据从内核复制到用户自己的缓冲区),进行通知。和信号驱动模型的主要区别:信号驱动IO由内核通知我们合适可以开始一个IO操作,在client进行IO操作的时候需要等待,所以是同步的。异步IO模型由内核通知我们IO何时已经完成,client不需要进行IO的处理了,所以是异步的。

    img

    一个形象的示例说明

    活动:演唱会

    角色1:举办商 售票业务员

    角色2:黄牛

    角色3:小明

    角色4:送票快递员

    同步阻塞 :

    小明从家里面先到演唱会现场问售票业务员买票,但是票还没出来,IO三天以后才出来,小明直接打了个地铺睡在举办商售票大厅,一直等票出来,然后买票。

    同步非阻塞 : socket可以设置non-blocking

    小明从家里面先到演唱会现场问售票业务员买票,但是票还没出来,然后小明走了,办理其他事情去了,然后过了2个小时,又去举办商售票大厅买票来了,如果票还没有出来,小明又先去办其他事情了,重复上面的操作,直到有票可以买。

    i/o复用 java selector() => linux epoll_wait() 同步非阻塞I/O

    小明想买票看演唱会,都直接给黄牛(selector/epoll)打电话了,说帮我留意买个票,票买了通知我,我自己去取(当我接到黄牛的电话时,我需要花费整个路成的时间去读这个数据,买拿这个票),那么票没出来之前,小明完全可以做自己的事情。

    信号i/o

    小明想买票看演唱会,给举办商售票业务员说,给你留个电话,有票了请你给我打个电话通知一下,(是看人家操作系统提不提供这种功能,Linux提供,windows没有这种机制),我自己再来买票(小明完全可以做自己的事情,但是票还是需要小明自己去拿的)。

    异步非阻塞i/o Linux aio_read aio_write

    小明想买票看演唱会,给举办商售票业务员说(异步非阻塞i/o)打电话了,给你留个地址,有票了请通知快递员,把这张票送到这个地址来,当小明听到敲门声,看见快递员,就知道票好了,而且知道票好了的时候,票已经到他手上了,票不用小明自己去取(应用不用自己再去read数据了)


    以上都是学习过程中的总结,如果有错误或者有疑问,欢迎一起交流吖~~

    展开全文
  • 五种IO模型详解

    千次阅读 2021-09-04 14:19:32
    五种I/O模型详细讲解前言I/O的本质阻塞IO模型非阻塞IO模型多路复用IO模型selectpollepoll信号驱动IO模型异步IO模型总结 前言 I/O的本质 网络IO的本质就是socket流的读取,通常一次IO读取会涉及两个阶段与两个对象,...

    前言

    I/O的本质

    网络IO的本质就是socket流的读取,通常一次IO读取会涉及两个阶段与两个对象,其中两个对象为:用户进程(线程)Process(Thread)、内核对象(kernel),两个阶段为:等待流数据准备阶段、从内核向进程复制数据阶段。
    对于socket而言,第一步通常等待网络上的数据分组到达,然后被复制到内核的某个缓冲区,第二步数据从内核的缓冲区复制到应用进程的缓冲区。
    I/O模型可细分为五种类型:阻塞IO、非阻塞IO、多路复用IO、信号驱动IO、异步IO。

    阻塞IO模型

    首先,在linux系统中默认所有的IO都是阻塞IO。
    阻塞IO的特点是从kernel读取数据时信号并未立刻返回,而是等待数据到达完毕或发生错误才会返回结果,这个过程是阻塞的。
    术语描述:当用户进程调用recvfrom这个系统调用时,kernel就开始等待数据到来,而进程这边会处于阻塞状态。当kernel将数据准备好后,就会将数据拷贝到用户进程的缓冲区,然后kernel返回结果,用户进程才会解除block状态,重新运行起来。
    阻塞IO调用过程

    非阻塞IO模型

    与阻塞IO不同,当用户进程发出recvfrom调用时,如果kernel中数据还没有准备好,那么并不会block用户进程,而是会返回error错误。相对于用户进程而言,每次发送读取操作后,并不需要等待,而是会立刻返回结果,当收到error时,就知道数据未准备完毕,然后继续发送读取操作,直到可以直接读取数据到缓冲区为止。虽然在执行read请求操作时,用户进程并未阻塞,但是当recvfrom将数据从内核拷贝到进程时,用户进程处于阻塞状态。
    非阻塞IO调用过程

    多路复用IO模型

    产生原因: 在具有大量IO请求的场景下,需要应用进程创建多个线程去读取数据,每个线程都会调用recvfrom去读取数据。在这种高并发的情况下,可能进程需要创建成千上万个线程,增加服务器负荷,并且造成了严重的资源浪费。
    因此,有了多路复用IO模型,使用一个线程去监听多个网络请求,即文件描述符,这样就实现了使用少量线程对大量请求进行监听,然后再让对应的线程进行数据读取。那么目前常用select、poll、epoll函数对fd文件描述符进行监听。

    多路复用IO又称事件驱动IO,进程使用IO多路复用在两个阶段都是阻塞的状态,进程使用select函数,其中select函数有一个参数是文件描述符集合,对这些文件描述符进行监听,当文件描述符就绪时,会返回readable信号,然后用户进程调用recvfrom进行读取数据,由于可同时监听多个IO,效率比阻塞IO高。
    多路复用IO调用过程

    select

    进程调用select后会被阻塞,将需要监听的文件描述符放入fd_set,并将fd_set复制到内核空间,内核空间会对fd_set进行轮询遍历,若无mark值,则会暂时挂起等待超时时间之后继续轮询,直到有数据准备就绪。最后将fd_set复制回用户进程,进行读/写操作。
    复杂度O(n)
    select的缺点:
    1.select监控数量受限
    select能监控的fd数量有上限,32位系统一般为1024,64位系统为2048,这个上限可以通过修改参数提高,但是相应的会损失性能。
    2.轮询效率低
    对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低。
    3.频繁拷贝复杂,开销大
    需要维护一个用来存放大量fd的数据结构,用户空间需要维护一个fd_set,fd_set的每一位都表示一个文件描述符,开始时会将其发给内核,这会使得用户空间和内核空间在传递该结构时复制开销大。
    4.select是水平触发
    应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作。那么之后select调用还是会将这些文件描述符返回,通知进程。

    poll

    poll和select基本是一样的,但是它对fd集合做了优化,使用链表存储,解决了连接数上限的问题。

    epoll

    epoll的实现与上述两种方式完全不同,因此不会造成上述的问题。
    同select、poll不同,复杂度O(1),通过三个函数实现流程:
    1.epoll_create: 创建一个epoll文件描述符集合,同时底层创建一个红黑树和就绪链表,红黑树存储所监控的文件描述符的节点数据,就绪链表存储就绪的文件描述符的节点数据。
    2.epoll_ctl: 用于添加新的描述符,首先判断红黑树中是否存在,如果不存在,插入数据,并告知内核注册回调函数(当文件描述就绪时通过网卡驱动触发),数据就绪后将事件添加到就绪队列中。
    3.epoll_wait: 检查链表,并将数据拷贝到用户空间(两者维护的是片共享内存),最后清空链表。其中epoll的工作方式分为LT、ET。
    注意:epoll是线程安全的,但是当一个线程调用epollwait,而另一个线程用epollctl向同一个epoll_fd添加一个监测fd后,epollwait有可能被改fd的读/写事件唤醒。
    在这里插入图片描述

    信号驱动IO模型

    首先应用进程通过sigaction系统调用安装一个信号处理函数(在内核位置),该系统调用立即返回,进程继续工作(未被阻塞)。当数据准备就绪时,kernel就会为该进程产生一个SIGIO信号,随后用户进程就可以使用recvfrom调用去读取数据到内存,并返回成功指示。
    在这里插入图片描述

    异步IO模型

    真正意义上的非阻塞IO,当用户进程发出aio_read操作之后,就立刻可以去做其它的事。另一方面,当kernel的角度,当kernel收到read信号后,会立刻返回结果,所以不会对用户进程block。之后,kernel会等待数据准备完毕,然后将数据拷贝到内存中,完成之后,kernel会给用户进程发送signal信号,表示数据已经read操作完毕。整个过程无阻塞状态的发生。
    异步IO调用过程

    总结

    1.阻塞IO、多路复用IO是两个阶段都处于阻塞状态,非阻塞IO、信号驱动IO是在第二阶段从kernel读取数据时处于阻塞状态,异步IO整个过程都未处于阻塞状态。
    2.按照阻塞程度排序:阻塞IO > 非阻塞IO > 多路复用IO > 信号驱动IO > 异步IO ,并且效率由低到高。
    3.异步与同步的区别:
    在IO模型里面如果请求方从发起请求到数据最后完成的这一段过程中都需要自己参与,那么这种我们称为同步请求;反之,如果应用发送完指令后就不再参与过程了,只需要等待最终完成结果的通知,那么这就属于异步。
    4.阻塞与非阻塞的区别:
    阻塞就是发起读取数据请求的时,当数据还没准备就绪的时候,这时请求是即刻返回,还是在这里等待数据的就绪,如果需要等待的话就是阻塞,反之如果即刻返回就是非阻塞。

    展开全文
  • linux五种IO模型

    万次阅读 多人点赞 2018-07-01 12:43:36
    为了更好的理解五种IO模型,我们先来说一下几个概念:同步,异步,阻塞和非阻塞。 同步和异步  这两个概念与消息的通知机制有关。 同步  所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用...

      为了更好的理解五种IO模型,我们先来说一下几个概念:同步,异步,阻塞和非阻塞。

    同步和异步

      这两个概念与消息的通知机制有关。

    同步

      所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。比如,调用readfrom系统调用时,必须等待IO操作完成才返回。

    异步

      异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。比如:调用aio_read系统调用时,不必等IO操作完成就直接返回,调用结果通过信号来通知调用者。

    阻塞与非阻塞

      阻塞与非阻塞与等待消息通知时的状态有关。

    阻塞 

      阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。  
      阻塞和同步是完全不同的概念。首先,同步是对于消息的通知机制而言,阻塞是针对等待消息通知时的状态来说的。而且对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。

    非阻塞

      非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回,并设置相应的errno。
      虽然表面上看非阻塞的方式可以明显的提高CPU的利用率,但是也带了另外一种后果就是系统的线程切换增加。增加的CPU执行时间能不能补偿系统的切换成本需要好好评估。

    事例

      以小明下载文件为例,对上述概念做一梳理:
       
    ①、同步阻塞:小明一直盯着下载进度条,到 100% 的时候就完成。
     
      同步:等待下载完成通知;
      阻塞:等待下载完成通知过程中,不能做其他任务处理;

    ②、同步非阻塞:小明提交下载任务后就去干别的,每过一段时间就去瞄一眼进度条,看到 100% 就完成。

      同步:等待下载完成通知;
      非阻塞:等待下载完成通知过程中,去干别的任务了,只是时不时会瞄一眼进度条;【小明必须要在两个任务间切换,关注下载进度】

    ③、异步阻塞:小明换了个有下载完成通知功能的软件,下载完成就“叮”一声。不过小明仍然一直等待“叮”的声音(看起来很傻,不是吗)。

      异步:下载完成“叮”一声通知;
      阻塞:等待下载完成“叮”一声通知过程中,不能做其他任务处理;

    ④、异步非阻塞:仍然是那个会“叮”一声的下载软件,小明提交下载任务后就去干别的,听到“叮”的一声就知道完成了。
      
      异步:下载完成“叮”一声通知;
      非阻塞:等待下载完成“叮”一声通知过程中,去干别的任务了,只需要接收“叮”声通知。

    五种IO模型

    阻塞式I/O
    非阻塞式I/O
    I/O复用(select,poll,epoll等)
    信号驱动式I/O(SIGIO)
    异步I/O(POSIX的aio_系列函数)

    IO执行的两个阶段

      在Linux中,对于一次读取IO的操作,数据并不会直接拷贝到程序的程序缓冲区。通常包括两个不同阶段:
     (1)等待数据准备好,到达内核缓冲区;
     (2)从内核向进程复制数据。
      对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达。当所有等待分组到达时,它被复制到内核中的某个缓冲区。第二步就是把数据从内核缓冲区复制到应用程序缓冲区。

    阻塞式I/O模型:

      同步阻塞 IO 模型是最常用、最简单的模型。在linux中,默认情况下,所有套接字都是阻塞的。 下面我们以阻塞套接字的recvfrom的的调用图来说明阻塞:
    这里写图片描述
      进程调用一个recvfrom请求,但是它不能立刻收到回复,直到数据返回,然后将数据从内核空间复制到程序空间。
      在IO执行的两个阶段中,进程都处于blocked(阻塞)状态,在等待数据返回的过程中不能做其他的工作,只能阻塞的等在那里。
      
    优缺点:
      优点是简单,实时性高,响应及时无延时,但缺点也很明显,需要阻塞等待,性能差;

    非阻塞式I/O:

      与阻塞式I/O不同的是,非阻塞的recvform系统调用调用之后,进程并没有被阻塞,内核马上返回给进程,如果数据还没准备好,此时会返回一个error(EAGAIN 或 EWOULDBLOCK)。进程在返回之后,可以处理其他的业务逻辑,过会儿再发起recvform系统调用。采用轮询的方式检查内核数据,直到数据准备好。再拷贝数据到进程,进行数据处理。
      在linux下,可以通过设置socket套接字选项使其变为非阻塞。下图是非阻塞的套接字的recvfrom操作
    这里写图片描述
      如上图,前三次调用recvfrom请求,但是并没有数据返回,所以内核返回errno(EWOULDBLOCK),并不会阻塞进程。但是当第四次调用recvfrom,数据已经准备好了,然后将它从内核空间拷贝到程序空间,处理数据。
      在非阻塞状态下,IO执行的等待阶段并不是完全的阻塞的,但是第二个阶段依然处于一个阻塞状态。
      
    同步非阻塞方式相比同步阻塞方式
      优点:能够在等待任务完成的时间里干其他活了(包括提交其他任务,也就是 “后台” 可以有多个任务在同时执行)。
      缺点:任务完成的响应延迟增大了,因为每过一段时间才去轮询一次read操作,而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。

    I/O多路复用(select,poll,epol):

      IO 多路复用的好处就在于单个进程就可以同时处理多个网络连接的IO。它的基本原理就是不再由应用程序自己监视连接,取而代之由内核替应用程序监视文件描述符。
      以select为例,当用户进程调用了select,那么整个进程会被阻塞,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从内核拷贝到用户进程。如图:
    这里写图片描述
      这里需要使用两个system call (select 和 recvfrom),而阻塞 IO只调用了一个system call (recvfrom)。所以,如果处理的连接数不是很高的话,使用IO复用的服务器并不一定比使用多线程+非阻塞阻塞 IO的性能更好,可能延迟还更大。IO复用的优势并不是对于单个连接能处理得更快,而是单个进程就可以同时处理多个网络连接的IO。
      实际使用时,对于每一个socket,都可以设置为非阻塞。但是,如上图所示,整个用户的进程其实是一直被阻塞的。只不过进程是被select这个函数阻塞,而不是被IO操作给阻塞。所以IO多路复用是阻塞在select,epoll这样的系统调用之上,而没有阻塞在真正的I/O系统调用(如recvfrom)。

    优势
      与传统的多线程/多进程模型比,I/O多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行,降底了系统的维护工作量,节省了系统资源。
    主要应用场景
      ①、服务器需要同时处理多个处于监听状态或者多个连接状态的套接字;
      ②、服务器需要同时处理多种网络协议的套接字,如同时处理TCP和UDP请求;
      ③、服务器需要监听多个端口或处理多种服务;
      ④、服务器需要同时处理用户输入和网络连接。

    信号驱动式I/O

      允许Socket进行信号驱动IO,并注册一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。如下图:
    这里写图片描述
      阻塞在IO操作的第二阶段

    异步I/O模型:

      上述四种IO模型都是同步的。相对于同步IO,异步IO不是顺序执行。用户进程进行aio_read系统调用之后,就可以去处理其他的逻辑了,无论内核数据是否准备好,都会直接返回给用户进程,不会对进程造成阻塞。等到数据准备好了,内核直接复制数据到进程空间,然后从内核向进程发送通知,此时数据已经在用户空间了,可以对数据进行处理了。
      在 Linux 中,通知的方式是 “信号”,分为三种情况:
      ①、如果这个进程正在用户态处理其他逻辑,那就强行打断,调用事先注册的信号处理函数,这个函数可以决定何时以及如何处理这个异步任务。由于信号处理函数是突然闯进来的,因此跟中断处理程序一样,有很多事情是不能做的,因此保险起见,一般是把事件 “登记” 一下放进队列,然后返回该进程原来在做的事。
      ②、如果这个进程正在内核态处理,例如以同步阻塞方式读写磁盘,那就把这个通知挂起来了,等到内核态的事情忙完了,快要回到用户态的时候,再触发信号通知。
      ③、如果这个进程现在被挂起了,例如陷入睡眠,那就把这个进程唤醒,等待CPU调度,触发信号通知。
    这里写图片描述
      IO两个阶段,进程都是非阻塞的

    五种IO模型比较

    这里写图片描述
      其实前四种I/O模型都是同步I/O操作,他们的区别在于第一阶段,而他们的第二阶段是一样的:在数据从内核复制到应用缓冲区期间(用户空间),进程阻塞于recvfrom调用。相反,异步I/O模型在这等待数据和接收数据的这两个阶段里面都是非阻塞的,可以处理其他的逻辑用户进程将整个IO操作交由内核完成,内核完成后会发送通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。

    参考
    聊聊同步、异步、阻塞与非阻塞:https://www.jianshu.com/p/aed6067eeac9
    《unix网络编程 :卷一》

    展开全文
  • Linux的五种IO模型(形象实例详解)

    千次阅读 2019-04-28 17:40:46
    二、Linux的5种IO模型 简介 1、阻塞IO模型(blocking I/O) 2、非阻塞IO模型(noblocking I/O) 3、信号驱动IO模型(signal blocking I/O) 4、IO复用模型(I/O multiplexing) 5、异步IO模型(asyn...
  • UNIX的五种IO模型

    2020-11-18 11:45:32
    1.UNIX的五种缓冲模型 P188 1.阻塞式I/O模型 2.非阻塞式I/O模型 3.I/O复用模型 4.信号驱动式I/O模型; 5.异步I/O模型.
  • Linux 五种IO模型

    千次阅读 2018-05-27 07:42:40
    聊聊Linux 五种IO模型 猿码道 关注2016.05.18 08:15* 字数 7975 阅读 22866评论 15喜欢 115赞赏 3上一篇《聊聊同步、异步、阻塞与非阻塞》已经通俗的讲解了,要理解同步、异步、阻塞与非阻塞重要的两个概念点了...
  • 操作系统常见的五种IO模型如下: 阻塞式IO模型 非阻塞式IO模型 IO多路复用模型 信号驱动IO模型 异步IO模型 下面是详细介绍。 阻塞IO 所谓阻塞IO就是当应用B发起读取数据申请时,在内核数据没有准备好之前,应用B会...
  • Linux--五种IO模型--原理/区别/详解

    千次阅读 多人点赞 2021-11-19 19:39:45
    本文介绍Linux的五种IO模型。 IO (Input/Output,输入/输出)即数据的读取(接收)或写入(发送)操作。IO有内存IO、网络IO和磁盘IO三种,通常我们说的IO指的是后两者。 可以将IO理解为两步: 等待IO事件就绪 数据...
  • 面试中常被问到(13)五种IO模型

    千次阅读 2021-07-05 17:22:57
    阻塞IO: 发起IO调用,若发现IO未就绪(IO条件不具备),则一直等待 也就说条件不满足一直死等 非阻塞IO: 发起IO调用,若发现IO未就绪,则直接报错返回 应用程序通过recvfrom调用不断和内核交互,当条件未达到...
  • windows socket 五种IO模型-代码全攻略
  • 【IO】IO模型:Linux下的五种IO模型

    千次阅读 2020-12-27 02:40:13
    Linux 提供了五种 IO 模型,包括:阻塞IO、非阻塞IOIO多路复用、信号驱动IO、异步IO。 在unix操作系统中,一个IO操作主要经过两个阶段:1、等待数据准备;2、将数据从内核空间拷贝到用户进程中。示意图如下: 1....
  • 聊聊Linux 五种IO模型 5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO 看了一些文章,发现有很多不同的理解,可能是因为大家入切的角度、环境不一样。所以,我们先说明基本的IO操作及环境。 本文是在《UNIX网络编程 ...
  • 聊聊Linux 五种IO模型

    千次阅读 2017-03-24 22:39:53
    参考网络上相关内容即可: 五种IO模型 UNIX网络编程读书笔记:I/O模型(阻塞、非阻塞、I/O复用、信号驱动、异步)
  • Java中IO模型详解

    千次阅读 2021-02-24 15:58:53
    在《Unix网络编程》一书中提到了五种IO模型,5种IO模型分别为: 阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动的IO模型、异步IO模型。下面就分别来介绍一下这5种IO模型的异同。 二、五种IO模型 1、阻塞IO模型...
  • 网络-五种IO模型/同步异步/阻塞非阻塞

    千次阅读 多人点赞 2019-05-20 21:15:27
    网络中获取数据的读操作步骤: 等待数据准备。 数据从内核空间拷贝到用户空间。 同步与异步: 同步与异步是针对应用程序与内核的交互而言。...同步过程中进程触发IO操作并等待或者轮询的去查看IO操作是...
  • 五种IO模型详解及优缺点

    千次阅读 2020-07-23 12:05:55
    下面就分别来介绍一下这5种IO模型的异同。 1.阻塞IO模型 最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户...
  • 五种基本IO模型介绍

    千次阅读 2021-12-15 17:36:14
    一、阻塞式IO 大家最开始接触网络编程时,基本都是接触的阻塞式IO,在一个基本的TCP服务器程序中,我们使用recv()函数来...我们以图表的形式来描述阻塞式IO模型: 二、非阻塞式IO 在介绍了阻塞IO后,我们不免会对非
  • 推荐一篇文章:《漫话:如何给女朋友解释什么是Linux的五种IO模型?》)
  • JAVA五种IO模型

    千次阅读 2019-06-03 20:23:39
    网络中获取数据的读操作步骤: 同步与异步: 同步与异步是针对应用程序与内核的交互而言。...同步过程中进程触发IO操作并等待或者轮询的去查看IO操作是否完成。 异步过程中进程触发IO操作以后,直接返回,做自己的...
  • Python协程(gevent+asyncio)模块 - Martin8866 - 博客园 简述同步IO、异步IO、阻塞IO、非阻塞IO之间的联系与区别 - 大数据从业者FelixZh - 博客园 ...5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO - 知乎
  • 浅聊Linux的五种IO模型

    千次阅读 2021-04-25 08:01:14
    在日常 Coding 中,多多少少都会接触到网络 IO,就会想要深入了解一下。看了很多文章,总是云里雾里的感觉,直到读了《UNIX网络编程 卷1:套接字联网API》中的介绍后,才豁然开朗。这里就给大家分享一下,如有不对,...
  • [Linux] 操作系统五种IO模型

    千次阅读 2019-06-07 21:30:42
    五种IO模型: 阻塞IO 非阻塞IO 信号驱动IO 异步IO 多路转接(复用)IO IO操作分了两个过程:等待 + 数据拷贝。 阻塞IO:发起IO调用,若不具备IO条件,则等待IO条件具备。具备则数据拷贝完毕后返回。一直等待资源...
  • Linux 五种IO模型详细图解

    千次阅读 2020-02-26 14:34:07
    一、IO 简述 IO (Input/Output,输入/输出)即数据的读取(接收)或...IO有内存IO、网络IO和磁盘IO,通常我们说的IO指的是后两者。 LINUX中进程无法直接操作I/O设备,其必须通过系统调用请求kernel来协助完成I/O...
  • 5种IO模型

    千次阅读 2020-06-05 19:40:52
    目录一、IO模型1、什么是IO2、IO的分类3、网络输入操作的两个阶段4、用户空间和内核空间5、同步和异步6、阻塞和非阻塞二、阻塞IO模型三、非阻塞IO模型四、IO复用模型五、信号驱动IO模型六、异步IO模型七、5种IO模型...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 383,812
精华内容 153,524
关键字:

五种io模型

友情链接: Ch02.rar