淺談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那個路,直接從磁盤到內核,再到消費者。
不得不感慨技術的更新離不開內核啊。

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