任何IO过程中, 都包含两个步骤. 第一是等待(准备数据), 第二是拷贝(将数据从内核缓冲区拷贝到进程地址空间). 而且在实际的应用场景中, 等待消耗的时间往往都远远高于拷贝的时间. 让IO更高效, 最核心的办法就是让等待的时间尽量少.
五种IO模型包括:阻塞IO、非阻塞IO、信号驱动IO、IO多路转接、异步IO。其中,前四个被称为同步IO。
博客链接什么是同步什么是异步。
https://blog.csdn.net/famur/article/details/105029243
在介绍五种IO模型时,我会举生活中钓鱼的例子,加深理解。
有五个人分别是大明、二明、小明、小美、小伟。
阻塞IO(blocking I/O)
大明开始钓鱼,然后专心的在钓鱼,不做其他的事情。
当用户进程调用了recvfrom等阻塞方法时,内核进入IO的第1个阶段:准备数据(内核需要等待足够的数据再拷贝)这个过程需要等待,用户进程会被阻塞,等内核将数据准备好,然后拷贝到用户地址空间,内核返回结果,用户进程才从阻塞态进入就绪态
Linux中默认情况下所有的socket都是阻塞的
非阻塞IO
二明钓鱼,每隔2小时来池塘边看看鱼有没有上钩。
当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。
用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作
一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回
非阻塞IO模式下用户进程需要不断地询问内核的数据准备好了没有
信号驱动IO
小美钓鱼,放好鱼饵后当鱼竿震动时,小美就知道鱼上没上钩。(这里的鱼竿震动相当于鱼给小美说我上钩了)
内核文件描述符就绪后,通过信号通知用户进程,用户进程再通过系统调用读取数据。
IO多路转接
select/poll
小明钓鱼,放好鱼饵后就让大明帮忙看,每隔俩小时来问大明鱼上钩了没有。
epoll
小明钓鱼,放好鱼饵后就让大明在鱼上钩以后叫他。
通过一种机制,一个进程可以监视多个文件描述符(套接字描述符)一旦某个文件描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作(这样就不需要每个用户进程不断的询问内核数据准备好了没)
模拟实现
https://blog.csdn.net/famur/article/details/105031935
异步IO
小伟钓鱼,直接让大明把钓上来以后把鱼给他。
用户进程发起read操作之后,立刻就可以开始去做其它的事。内核收到一个异步IO read之后,会立刻返回,不会阻塞用户进程。
内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,内核会给用户进程发送一个signal告诉它read操作完成了