阻塞IO模型
在用户进程请求IO时,调用系统调用recfrom,内核开始准备数据,用户进程阻塞,当数据准备好了内核将数据拷贝到用户内存,返回结果,然后用户进程接触阻塞。
非阻塞IO模型
用户进程请求读IO时,如果内核数据没有准备好,不会阻塞用户进程,而是会返回一个error,用户收到该错误消息后,知道数据还没准备好,又再次发起请求,直到数据准备好以后,阻塞用户进程,然后将数据拷贝到用户内存,再正常返回。
多路复用IO模型
多个进程的IO注册到一个复用器(Seletor)上,Selector监听所有注册的IO,如果Selector监听的所有IO的缓冲区没有可读的数据,那么Selector进程将会被阻塞,当有数据时,系统调用会返回,调用进程会自己或者通知其他进程发起读取IO。
IO多路复用的三种调用方式
**select方式:**将监听的读、写、异常进程(fd)分别放到三个个fd_set数组中,调用select时三个fd_set发给内核,内核收到fd_set后,遍历数组,发现有读写事件后,把没有事件的fd句柄清除,把事件的fd返回给应用进程,应用进程收到后发起读写操作。
select的缺点: 1、每调用异常select需要三个fd_set拷贝到内核,返回时把相关的fd拷贝给用户空间,性能消耗大。2、select需要遍历fd_set,如果fd_set数组太大的话,需要时间很长。3、fd_set的长度受数组大小限制。
poll方式: poll里用链表记录监控的fd,没有了长度限制,能够支持的并发请求量增多。select的其他缺点依然存在。
epoll方式: 先创建一个fd文件描述符作为内核事件表(B+树结构的文件,没有数量限制),用来报错应用进程监听的fd和对应的事件。调用epoll ctl,动态地往内核事件表添加移出fd和对应事件。调用epoll_wait给内核事件表绑定一个回调函数,当监听的fd活跃时调用回调函数把事件加到一个活跃事件队列,最后epoll_wait返回时把事件队列的fd和事件类型返回给应用进程。
epoll的优点: 事件表在内核中,不用像select、poll一样不断地拷贝fd。不用遍历事件列表,通过回调函数将激活的事件添加到事件队列里,返回队列里的fd和事件。
信号驱动IO模型
进程预先告知内核,向内核注册一个处理函数,内核数据准备好以后发送一个信号给用户进程,用户进程在处理函数中调用IO。在内核拷贝数据的过程中用户进程是阻塞的,在数组准备好之前,用户进程不阻塞。
异步IO模型
用户进程调用aio_read后,把read描述符、缓冲区指针、缓冲区大小、文件偏移传递给内核;内核在收到aio_read后直接返回,等数据拷贝完成后给用户进程发送IO完成的信号。
各类IO对比
参考资料:https://zhuanlan.zhihu.com/p/126278747