1、CPU/内存/硬盘
IO设备:网卡/硬盘等,数据从网络经过网卡直接放到内存中的DMA或从硬盘读数据到DMA,CPU从内存读数据执行。
2、内核
2.1、网络IO流程/数据传输分析
网络中的数据socket传递到网卡,应用程序通过内核提供的IO函数从网卡获取数据。内核是通过驱动从网卡拿到数据。应用程序和内核本质都是程序。
2.2、应用程序是如何拿到数据提供给CPU计算?
应用程序启动时会在内存分配一块空间叫用户空间,内核启动时也会在内存分配一块空间叫内核空间。
2.3、网络中的数据如何到达内存?
数据准备:网络数据socket传递到内核空间中
数据拷贝:内核空间通过拷贝将socket拷贝到用户空间,此时应用程序可以读取数据
2.4、同步调用/异步调用、阻塞/非阻塞
根据结果通知的方式进行区分。
2.5、阻塞IO
我们通常使用线程Thread监听端口,Thread线程通过accpet()方法监听,accpet()会调用内核的recvFrom(block...)方法【第一步取socket】,此时如果没有client传递IO数据,则Thread会阻塞在accept()处;如果有client传递IO数据到网卡,则内核recvFrom(block...)函数看到网卡处有数据,则进行1)数据准备,2)数据拷贝,则Thread从用户空间拿到client。接着3)Thread开始执行read()读取input流,read()也会调用内核的recvFrom(block...)方法【第二步取字节流】,此时如果没有input流【即只有连接没有数据传递】则Thread会阻塞在read()处;如果有input流传递IO数据到网卡,则内核recvFrom(block...)函数看到网卡处有数据,则进行1)数据准备,2)数据拷贝,则Thread从用户空间拿到input。
2.6、当有多个客户端到达网卡Thread是否处理?
因为此时Thread正在进行read(),对于其他client不能立即操作,只能等待。
2.7、非阻塞IO
我们通常使用线程Thread监听端口,Thread线程通过accpet()方法监听,accpet()会调用内核的recvFrom(noBlock...)方法【第一步取socket】,此时如果没有client传递IO数据,则Thread不会阻塞在accept()处,会继续向下执行;如果有多个client传递IO数据到网卡,则内核recvFrom(noBlock...)函数看到网卡处有数据,则进行1)数据准备,2)数据拷贝,则Thread从用户空间拿到client,将多个client链接起来,此时Thread进行无限制循坏。接着3)Thread开始执行read()读取input流,read()也会调用内核的recvFrom(noBlock...)方法【第二步取字节流】,此时如果没有input流【即只有连接没有数据传递】则Thread不会阻塞在read()处;如果有input流传递IO数据到网卡,则内核recvFrom(noBlock...)函数看到网卡处有数据,则进行1)数据准备,2)数据拷贝,则Thread从用户空间拿到input。
问题:如果程序一直无限循环,给CPU带来巨大负担。
2.8、IO多路复用
Thread中的多路复用器核心函数是select(),select调用内核的epoll()。
epoll可以理解为维护了一个链接,如果有多个client连接到网卡,epoll会把所有client同时做1)数据准备【数据准备,非阻塞】,此时epoll()返回给多路复用器多个client的索引值表示数据准备阶段完成,然后多路复用器调用内核的recvFrom(Fb...),此时进行2)数据拷贝【阻塞】,需要一个一个从内核空间拷贝到用户空间。
当有多个socket发送input,epoll会把所有input同时做1)数据准备【数据准备,非阻塞】,此时epoll()返回给多路复用器多个input的索引值表示数据准备阶段完成,然后多路复用器调用内核的recvFrom(Fb...),此时进行2)数据拷贝【阻塞】,需要一个一个从内核空间拷贝到用户空间。
问题:如何解决从内核空间拷贝数据到用户空间的大开销?
零拷贝:2.6版本后epoll升级为零拷贝,原理是将clinet或者input放到应用程序和内核的公共内存空间,而不是内核空间的内存。而内核空间仅仅保存内存地址【8字节】,此时仅仅从内核空间拷贝内存地址到用户空间。
3、BIO/NIO/AIO
3.1、BIO重要组件