一、用戶空間和內(nèi)核空間
應用需要通過Linux內(nèi)核與硬件交互。
內(nèi)核本質(zhì)也是應用,運行的時候也需要CPU資源、內(nèi)存資源。用戶應用也在消耗這些資源。
為了避免用戶應用導致沖突甚至內(nèi)核崩潰,用戶應用與內(nèi)核是分離的:
尋址空間:無論內(nèi)核還是用戶應用,都無法直接訪問物理內(nèi)存,而是分配虛擬的內(nèi)存空間,映射到不同的物理內(nèi)存空間。內(nèi)核和用戶應用,再去訪問虛擬內(nèi)存空間,就需要有對應的虛擬地址(無符號的整數(shù))
示例:32位系統(tǒng),帶寬32,地址的最大值就是2的32次方32位程序最大尋址空間,也就是尋址的范圍從0到2的32次方,也就是4GB.
IO在用戶空間和內(nèi)核空間切換的整體流程:
二、阻塞IO
訪問流程圖
顧名思義,阻塞IO就是兩個階段都必須阻塞等待。
三、非阻塞IO
顧名思義,非阻塞IO的操作會立即返回結(jié)果而不是阻塞用戶進程。
可以看到,非阻塞IO模型中,用戶進程在第一個階段是非阻塞,第二個階段是阻塞狀態(tài)。雖然是非阻塞,但是性能并沒有得到提高。而且忙等機制導致CPU空轉(zhuǎn),CPU使用率暴增。
四、IO多路復用 1、背景
無論是阻塞IO還是非阻塞IO,用戶應用在一階段都需要調(diào)用來獲取數(shù)據(jù),差別在于無數(shù)據(jù)時的處理方案:
比如服務端處理客戶端請求時,在單線程情況下,只能依次處理每一個,如果正在處理的soket恰好未就緒(數(shù)據(jù)不可讀或不可寫),線程就會被阻寒,所有其它客戶端都必須等待,性能自然會很差。
這就像服務員給顧客點餐,分兩步:
1、顧客思考要吃什么(等待數(shù)據(jù)就緒);
2、顧客想好了,開始點餐(讀取數(shù)據(jù))。
第一步要提高效率的幾種方法:
1、方案一:增加更多服務員(多線程)
2、方案二:不排隊,誰想好了吃什么(數(shù)據(jù)就緒了),服務員就給誰點餐(用戶應用就去讀取數(shù)據(jù))
那么問題來了:用戶進程如何知道內(nèi)核中數(shù)據(jù)是否就緒呢?
2、IO多路復用
文件描述符(File ):簡稱FD,是一個從0 開始遞增的無符號整數(shù),用來關聯(lián)Linux中的一個文件。在Linux中,一切皆文件,例如常規(guī)文件、視頻、硬件設備等,當然也包括網(wǎng)絡套接字 ()。
IO多路復用:是利用單個線程來同時監(jiān)聽多個FD,并在某個FD可讀、可寫時得到通知,從而避免無效的等待,充分利用CPU資源。
監(jiān)聽FD的方式、通知的方式又有多種實現(xiàn),常見的有:
1、
2、poll
3、epoll
差異:
(1)和poll只會通知用戶進程有FD就緒,但不確定具體是哪個FD,需要用戶進程逐個遍歷FD來確認;
(2)epoll則會在通知用戶進程FD就緒的同時,把已就緒的FD寫入用戶空間
3、 (1)底層框架
是Linux中最早的I/O多路復用時限方案:
(2)執(zhí)行流程 用戶空間創(chuàng)建 rfds,默認值0,大小位用戶空間假如要監(jiān)聽 fd=1,2,5,把1、2、5bit位置為1用戶空間執(zhí)行(5+1, rfds, null, null, 3)用戶空間拷貝 到內(nèi)核空間內(nèi)核空間遍歷內(nèi)核空間沒有就緒,則休眠。內(nèi)核空間等待數(shù)據(jù)就緒,被喚醒或超時。未就緒的改成0內(nèi)核空間拷貝 到用戶空間,覆蓋用戶空間的用戶空間遍歷,找到就緒的fd,讀取其中數(shù)據(jù)
(3) 模式存在的問題 4、poll (1)底層框架
poll模式對模式進行了簡單改進,但性能提升不明顯。
(2)執(zhí)行流程 (3)對比 5、epoll (1)底層代碼
epoll模式是對和poll的改進32位程序最大尋址空間,它提供了三個函數(shù):
(2)執(zhí)行流程
1、調(diào)用(1),創(chuàng)建epoll實例
2、調(diào)用(…),添加要監(jiān)聽的FD,關聯(lián),當觸發(fā)時,把對應的FD加入到鏈表中。
3、(…, )等待FD就緒
6、總結(jié)
1、模式存在的三個問題:
7、事件通知機制
當FD有數(shù)據(jù)可讀時,我們調(diào)用就可以得到通知。事件通知的模式有兩種:
區(qū)別:
拷貝數(shù)據(jù)之前,會將鏈表中的fd從中斷開連接,然后拷貝。
假如數(shù)據(jù)沒有處理完,
當采用ET時,直接刪掉fd,再次調(diào)用,中沒有數(shù)據(jù);
當采用LT,會再次添加到 ,再次調(diào)用,中有數(shù)據(jù);
結(jié)論:
8、web服務流程
基于epoll模式的web服務的基本流程圖:
五、信號驅(qū)動IO
信號驅(qū)動IO是與內(nèi)核建立SIGIO的信號關聯(lián)并設置回調(diào),當內(nèi)核有FD就緒時,會發(fā)出SIGIO信號通知用戶,期間用戶應用可
以執(zhí)行其它業(yè)務,無需阻塞等待,
當有大量IO操作時,信號較多,SIGIO處理函數(shù)不能及時處理可能導致信號隊列溢出。
而且內(nèi)核空間與用戶空間的頻繁信號交互性能也較低。
六、異步IO
IO操作是同步還是異步,關鍵看數(shù)據(jù)在內(nèi)核空間與用戶空間的拷貝過程(數(shù)據(jù)讀寫的IO操作),也就是階段二是同步還是異步:
七、Redis網(wǎng)絡模型 面試題:Redis到底是單線程還是多線程? 在Redis版本迭代過程中,在兩個重要的時間點上引入了多線程支持: Redis為什么要選擇單線程? 1、單線程
Redis通過IO多路復用來提高網(wǎng)絡性能,并且支持各種不同的多路復用實現(xiàn),并且將這些實現(xiàn)進行封裝,提供了統(tǒng)一的高性能事件庫API庫AE:
ae.c文件判斷服務器類型,選擇執(zhí)行哪個ae.c文件
Redis單線程網(wǎng)絡模型的整個流程:
整個流程:
Redi6.0版本中引入了多線程,目的是為了提高IO讀寫效率。因此在解析客戶端命令、寫響應結(jié)果時采用了多線程。核心的命令執(zhí)行、IO多路復用模塊依然是由主線程執(zhí)行。