浅谈bio,nio,select,epoll。

所有的东西只针对linux。
bio,一个程序一个进程监听一个端口,每次有连接之后,抛出一个新的线程去管理那次连接的操作。如果这个连接没有操作,就造成了浪费,而且操作系统cpu单核情况下每次只能运行一个线程,线程发生上下文切换会造成等待,从某种角度上来说他是堵塞的。
nio,linux系统内核提供了一个systemcall,可以使一个线程监听很多端口,但是这样还是会有问题,因为当一个并发量很大的情况下,假设监听了10w的端口,那么每次读写都要去请求系统内核,调用系统内核的接口也是很花费时间的。
select,select其实也是多路复用,监听端口会把所有的一次发给操作系统内核,操作系统在内核里面去遍历,这样就节省了很多时间,但是读还是得挨个遍历,这样相比nio他的时间复杂度缩小了一半,因为之前读和写都要建立连接,而select只需要建立读的连接。
epoll,epollo我这里主要讲一下,大家其实可以去看linux的指令,epollo你可以这么理解,就是写,一次发送过去,内核新开辟了一个空间,每次调用系统内核的接口只需要传新的端口就行了,之前的select是全部都要传。然后内核处理完之后,会在开辟一个空间,这个空间主要表示已经处理好的数据,需要进程去拿数据。这样就做到了处理好我就拿。而之前的select则要挨个遍历,这样的时间复杂度相比select又降低了许多。epollo包含了epoll_create,epoll_ctl,epoll_wait
epoll_create返回一个文件描述符,文件描述符指向内核的第一个空间的具体区域,epoll也要先调用epoll_create这个指令。
epoll_ctl,在epoll_create那个文件描述符所指向的内核区域进行操作,它包括了add,update,delete。
当建立连接后,accept后,这个时候调用epoll_wait,这个时候开始处理,处理完之后把处理好的数据放到第二块空间后,这个时候你的线程就可以去第二块空间去取数据进行读。 现在我们只需要研究下是如何把数据放到第二块空间里去的。这个地方设计到操作系统和计算机组成原理的一些知识,怎么完成这个操作的是根据硬中断,如何回调第一块空间的文件描述符,一起写入第二块空间。epoll的主要原理主要根据计算机组成原理中的中断实现。更成分的发挥硬件,尽量不浪费cpu。
redis,nginx都会用到epoll,netty就看情况了。
具体你要去验证可以自行去验证,strace -ff -o 这个指令你会用到的。抓取线程调用SystemCall的。
nginx的epollwait阻塞,redis的epoll_wait轮询(6.x之前)redis是单线程的,io是单线程的不代表redis是单线程的!redis的单线程的io是原子操作,串行化的,读计算写顺序执行,一个线程处理多个端口,6.x之后有IOthreads,还是用epoll,把读的并发去读,计算还是串行化。时间复杂度又比之前的版本又变低了。
netty本人还不会,等学会了再来分享技术。
顺便在讲一下kafka的0拷贝,kafka基于jvm的。kafka数据是可以持久化的,要存到磁盘。(java的RandomAccessFile)mmap技术就可以将kafka和磁盘的路径直接打通,透过系统内核。这样就可以减少对内核的系统调用。
再来说一下sendfile系统调用。0拷贝,0拷贝的前提是数据不需要加工,首先kafka读消息,是先从磁盘读到系统内核,系统内核读到kafka,kafka在发送给系统内核,消费者再去系统内核去拿。有了sendflie之后就不需要走kafka那个路,直接从磁盘到内核,再到消费者。
不得不感慨技术的更新离不开内核啊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章