常见IO模型以及IO多路复用

目录

一、阻塞IO

二、非阻塞IO

三、IO多路复用

1、select

2、poll

3、epoll

四、异步IO

五、零拷贝


一、阻塞IO

Read的时候要从用户空间切换到内核空间,内核空间分两个阶段,等待数据和复制数据,复制数据就是把数据从网卡复制到内存。阻塞io调用read的时候就会直接切换到内核等待数据,等数据来了再复制,最后再返回用户态,整个过程线程都是阻塞的。

二、非阻塞IO

Read的时候不阻塞等待数据,如果有数据就复制,没有数据就返回重新请求,是个循环调用判断是否有数据来,但是复制数据的过程用户线程还是阻塞的,他其实只是等待数据阶段的非阻塞。

三、IO多路复用

不是调用read了,而是调用select方法,看有没有事件发生,如果有事件发生了,就会通知用户态去复制,他select等待数据是阻塞的,通知后去复制也是阻塞的,但是他的好处是他可以监听多个事件,什么事件都可以触发他继续运行。他监听到了可以把一批数据一次返回回去处理,他是用数据存储的。

1、select

他说把bigmap数组的文件描述符都清空然后拷贝到内核态,然后进入阻塞队列阻塞,内核会遍历判断哪个已经就绪,如果有请求来会dma处理完加到对应的描述符上,然后职为1,最后会返回给用户态整个bitmap数组,1代表有数据处理,他的数组长度上固定1024的有限制,要经过两次遍历而且要频繁内核和用户之间拷贝,性能不是很好,无法重用每次都要清理

2、poll

他定义了一个pollfd的结构体(类)里面有int的fd描述符,注册的事件,和实际发生的事件填充。他也是阻塞然后一次性将一批的数组都放到内核态,然后在内核遍历去判断哪些就绪,有数据到网卡也是一样到dma到内核缓冲区然后给对应的变1,最后再拷回用户态,但是他判断完之后又可以恢复那个值为0,然后就不需要全部清空,所以是可以重用的。他比select多了没有限制1024的限制,因为是结构体,而且可以重用,但是他仍然需要拷贝到内核和遍历两次,开销也比较大。

3、epoll

他也是个结构体类,有个事件和一些文件描述信息,他另外会创建个eventpoll结构体,这有3个重要的属性,一个是rdylist已经就绪的双向链表,还有rbr一个红黑树去管理用户进程放进来的所有socket连接,wq是个等待队列,进程堵塞就是放到这里,后续会唤醒他执行相应的操作。他首先先去看就绪队列有没有事件,有直接返回,没有就会阻塞在队列里等待事件,当客户端数据到接收到后会执行会调函数到就绪队列,然后内核去唤醒阻塞的进程去返回给用户态。他的好处就是不需要之前那样一个个遍历,他是通过回调函数直接回调到队列里面,然后队列有就返回,但是他占用更多内存。select每次都是全量拷贝,而epoll不需要一次返回全部提高效率,而且他用红黑树和链表存,所以没有最大限制。

Epoll当有数据到网卡中,就会dma拷贝到内核中,然后注册回调函数,把红黑树对应的socket从拷贝到用双向链表中(实际上只是改变了指针指向到了双端队列并不是拷贝)然后他会拷贝到用户态的数组中。

默认的水平触发是只要有就绪事件就一直通知上层,通知用户态有数据到达,所以你可以不用一次性处理完所有数据,可以只处理一部分数据,下次他还会通知你继续触发;边缘触发是只会触发一次,通知用户态一次,但是当再发一批的时候也会有一次,只通知一次,所以他通知用户态的次数会比较少,因此效率会高些,nginx用的就是这种。

四、异步IO

同步是线程自己去获取结果,所以上面3个都是同步的,异步是线程自己不去获取,而且其他线程送结果给他。用户态调用read后立刻返回,他会另外起一个线程来复制然后返回结果,调用回调方法来调用。异步都不需要等待,所以异步一定是非阻塞的

五、零拷贝

原本的read的时候要切换到内核态,从磁盘读取到操作系统的内核缓冲区,然后再复制到用户缓冲区,然后回到用户态,然后再切换到内核态write写到socket缓冲区,然后再拷贝到网卡,数据复制了4次。

在NIO里通过byteBuffer优化了,他有个操作系统内核缓冲区和用户缓冲区共用的,咱们的用户缓冲区是直接用os的缓冲区,所以不需要从os拷贝到用户缓冲区这个操作了,减少了一次拷贝。

Java中的sendFile方法,调用transferTo/transferFrom方法拷贝,java程序直接用户态到内核态,使用DMA将数据读入内核缓冲区,从内核缓冲区直接传输到socket缓冲区,最后使用dma将socket缓冲区写入网卡,他主要是没有到用户缓冲区这步

还有进一步优化linux2.4把sendFile优化了,可以直接从磁盘到内核缓冲区,然后跳过socket缓冲区,直接到网卡,只有一些offset和length一些少部分信息才到socket缓冲区。

这后面两种都算零拷贝,并不是没有拷贝,而是不会把数据拷贝到jvm内存中,直接用操作系统的内存,他不太会占用cpu的时间,而且直接用操作系统的dma。

你可能感兴趣的:(网络)