日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

作者:mosun,騰訊 PCG 后臺開發工程師

一、虛擬內存 1.1 虛擬內存引入

我們知道計算機由 CPU、存儲器、輸入/輸出設備三大核心部分組成,如下:

CPU 運行速度很快,在完全理想的狀態下,存儲器應該要同時具備以下三種特性:

  • 速度足夠快:這樣 CPU 的效率才不會受限于存儲器;
  • 容量足夠大:容量能夠存儲計算機所需的全部數據;
  • 價格足夠便宜:價格低廉,所有類型的計算機都能配備;

然而,出于成本考慮,當前計算機體系中,存儲都是采用分層設計的,常見層次如下:

上圖分別為寄存器、高速緩存、主存和磁盤,它們的速度逐級遞減、成本逐級遞減,在計算機中的容量逐級遞增。通常我們所說的物理內存即上文中的主存,常作為操作系統或其他正在運行中的程序的臨時資料存儲介質。在嵌入式以及一些老的操作系統中,系統直接通過物理尋址方式和主存打交道。然而,隨著科技發展,遇到如下窘境:

  • 一臺機器可能同時運行多臺大型應用程序;
  • 每個應用程序都需要在主存存儲大量臨時數據;
  • 早期,單個 CPU 尋址能力 2^32,導致內存最大 4G;

主存成了計算機系統的瓶頸。此時,科學家提出了一個概念:虛擬內存。

以 32 位操作系統為例,虛擬內存的引入,使得操作系統可以為每個進程分配大小為 4GB 的虛擬內存空間,而實際上物理內存在需要時才會被加載,有效解決了物理內存有限空間帶來的瓶頸。在虛擬內存到物理內存轉換的過程中,有很重要的一步就是進行地址翻譯,下面介紹。

1.2 地址翻譯

進程在運行期間產生的內存地址都是虛擬地址,如果計算機沒有引入虛擬內存這種存儲器抽象技術的話,則 CPU 會把這些地址直接發送到內存地址總線上,然后訪問和虛擬地址相同值的物理地址;如果使用虛擬內存技術的話,CPU 則是把這些虛擬地址通過地址總線送到內存管理單元(Memory Management Unit,簡稱 MMU),MMU 將虛擬地址翻譯成物理地址之后再通過內存總線去訪問物理內存:

虛擬地址(比如 16 位地址 8196=0010 000000000100)分為兩部分:虛擬頁號(Virtual Page Number,簡稱 VPN,這里是高 4 位部分)和偏移量(Virtual Page Offset,簡稱 VPO,這里是低 12 位部分),虛擬地址轉換成物理地址是通過頁表(page table)來實現的。頁表由多個頁表項(Page Table Entry, 簡稱 PTE)組成,一般頁表項中都會存儲物理頁框號、修改位、訪問位、保護位和 "在/不在" 位(有效位)等信息。

這里我們基于一個例子來分析當分析當頁面命中時,計算機各個硬件是如何交互的:

  • 第 1 步:處理器生成一個虛擬地址 VA,通過總線發送到 MMU;
  • 第 2 步:MMU 通過虛擬頁號得到頁表項的地址 PTEA,通過內存總線從 CPU 高速緩存/主存讀取這個頁表項 PTE;
  • 第 3 步:CPU 高速緩存或者主存通過內存總線向 MMU 返回頁表項 PTE;
  • 第 4 步:MMU 先把頁表項中的物理頁框號 PPN 復制到寄存器的高三位中,接著把 12 位的偏移量 VPO 復制到寄存器的末 12 位構成 15 位的物理地址,即可以把該寄存器存儲的物理內存地址 PA 發送到內存總線,訪問高速緩存/主存;
  • 第 5 步:CPU 高速緩存/主存返回該物理地址對應的數據給處理器。

在 MMU 進行地址轉換時,如果頁表項的有效位是 0,則表示該頁面并沒有映射到真實的物理頁框號 PPN,則會引發一個缺頁中斷,CPU 陷入操作系統內核,接著操作系統就會通過頁面置換算法選擇一個頁面將其換出 (swap),以便為即將調入的新頁面騰出位置,如果要換出的頁面的頁表項里的修改位已經被設置過,也就是被更新過,則這是一個臟頁 (Dirty Page),需要寫回磁盤更新該頁面在磁盤上的副本,如果該頁面是"干凈"的,也就是沒有被修改過,則直接用調入的新頁面覆蓋掉被換出的舊頁面即可。缺頁中斷的具體流程如下:

  • 第 1 步到第 3 步:和前面的頁面命中的前 3 步是一致的;
  • 第 4 步:檢查返回的頁表項 PTE 發現其有效位是 0,則 MMU 觸發一次缺頁中斷異常,然后 CPU 轉入到操作系統內核中的缺頁中斷處理器;
  • 第 5 步:缺頁中斷處理程序檢查所需的虛擬地址是否合法,確認合法后系統則檢查是否有空閑物理頁框號 PPN 可以映射給該缺失的虛擬頁面,如果沒有空閑頁框,則執行頁面置換算法尋找一個現有的虛擬頁面淘汰,如果該頁面已經被修改過,則寫回磁盤,更新該頁面在磁盤上的副本;
  • 第 6 步:缺頁中斷處理程序從磁盤調入新的頁面到內存,更新頁表項 PTE;
  • 第 7 步:缺頁中斷程序返回到原先的進程,重新執行引起缺頁中斷的指令,CPU 將引起缺頁中斷的虛擬地址重新發送給 MMU,此時該虛擬地址已經有了映射的物理頁框號 PPN,因此會按照前面『Page Hit』的流程走一遍,最后主存把請求的數據返回給處理器。
1.2.1 高速緩存

前面在分析虛擬內存的工作原理之時,談到頁表的存儲位置,為了簡化處理,都是默認把主存和高速緩存放在一起,而實際上更詳細的流程應該是如下的原理圖:

如果一臺計算機同時配備了虛擬內存技術和 CPU 高速緩存,那么 MMU 每次都會優先嘗試到高速緩存中進行尋址,如果緩存命中則會直接返回,只有當緩存不命中之后才去主存尋址。

通常來說,大多數系統都會選擇利用物理內存地址去訪問高速緩存,因為高速緩存相比于主存要小得多,所以使用物理尋址也不會太復雜;另外也因為高速緩存容量很小,所以系統需要盡量在多個進程之間共享數據塊,而使用物理地址能夠使得多進程同時在高速緩存中存儲數據塊以及共享來自相同虛擬內存頁的數據塊變得更加直觀。

1.2.2 加速翻譯&優化頁表

虛擬內存這項技術能不能真正地廣泛應用到計算機中,還需要解決如下兩個問題:

  • 虛擬地址到物理地址的映射過程必須要非??欤刂贩g如何加速。
  • 虛擬地址范圍的增大必然會導致頁表的膨脹,形成大頁表。

"計算機科學領域的任何問題都可以通過增加一個間接的中間層來解決"。雖然虛擬內存本身就已經是一個中間層了,但是中間層里的問題同樣可以通過再引入一個中間層來解決。加速地址翻譯過程的方案目前是通過引入頁表緩存模塊 -- TLB,而大頁表則是通過實現多級頁表或倒排頁表來解決。

1.2.2.1 TLB 加速

翻譯后備緩沖器(Translation Lookaside Buffer,TLB),也叫快表,是用來加速虛擬地址翻譯的,因為虛擬內存的分頁機制,頁表一般是保存在內存中的一塊固定的存儲區,而 MMU 每次翻譯虛擬地址的時候都需要從頁表中匹配一個對應的 PTE,導致進程通過 MMU 訪問指定內存數據的時候比沒有分頁機制的系統多了一次內存訪問,一般會多耗費幾十到幾百個 CPU 時鐘周期,性能至少下降一半,如果 PTE 碰巧緩存在 CPU L1 高速緩存中,則開銷可以降低到一兩個周期,但是我們不能寄希望于每次要匹配的 PTE 都剛好在 L1 中,因此需要引入加速機制,即 TLB 快表。

TLB 可以簡單地理解成頁表的高速緩存,保存了最高頻被訪問的頁表項 PTE。由于 TLB 一般是硬件實現的,因此速度極快,MMU 收到虛擬地址時一般會先通過硬件 TLB 并行地在頁表中匹配對應的 PTE,若命中且該 PTE 的訪問操作不違反保護位(比如嘗試寫一個只讀的內存地址),則直接從 TLB 取出對應的物理頁框號 PPN 返回,若不命中則會穿透到主存頁表里查詢,并且會在查詢到最新頁表項之后存入 TLB,以備下次緩存命中,如果 TLB 當前的存儲空間不足則會替換掉現有的其中一個 PTE。

下面來具體分析一下 TLB 命中和不命中。

TLB 命中

  • 第 1 步:CPU 產生一個虛擬地址 VA;
  • 第 2 步和第 3 步:MMU 從 TLB 中取出對應的 PTE;
  • 第 4 步:MMU 將這個虛擬地址 VA 翻譯成一個真實的物理地址 PA,通過地址總線發送到高速緩存/主存中去;
  • 第 5 步:高速緩存/主存將物理地址 PA 上的數據返回給 CPU。

TLB 不命中

  • 第 1 步:CPU 產生一個虛擬地址 VA;
  • 第 2 步至第 4 步:查詢 TLB 失敗,走正常的主存頁表查詢流程拿到 PTE,然后把它放入 TLB 緩存,以備下次查詢,如果 TLB 此時的存儲空間不足,則這個操作會汰換掉 TLB 中另一個已存在的 PTE;
  • 第 5 步:MMU 將這個虛擬地址 VA 翻譯成一個真實的物理地址 PA,通過地址總線發送到高速緩存/主存中去;
  • 第 6 步:高速緩存/主存將物理地址 PA 上的數據返回給 CPU。
1.2.2.2 多級頁表

TLB 的引入可以一定程度上解決虛擬地址到物理地址翻譯的開銷問題,接下來還需要解決另一個問題:大頁表。理論上一臺 32 位的計算機的尋址空間是 4GB,也就是說每一個運行在該計算機上的進程理論上的虛擬尋址范圍是 4GB。到目前為止,我們一直在討論的都是單頁表的情形,如果每一個進程都把理論上可用的內存頁都裝載進一個頁表里,但是實際上進程會真正使用到的內存其實可能只有很小的一部分,而我們也知道頁表也是保存在計算機主存中的,那么勢必會造成大量的內存浪費,甚至有可能導致計算機物理內存不足從而無法并行地運行更多進程。

這個問題一般通過多級頁表(Multi-Level Page Tables)來解決,通過把一個大頁表進行拆分,形成多級的頁表,我們具體來看一個二級頁表應該如何設計:假定一個虛擬地址是 32 位,由 10 位的一級頁表索引、10 位的二級頁表索引以及 12 位的地址偏移量,則 PTE 是 4 字節,頁面 page 大小是 2^12 = 4KB,總共需要 2^20 個 PTE,一級頁表中的每個 PTE 負責映射虛擬地址空間中的一個 4MB 的 chunk,每一個 chunk 都由 1024 個連續的頁面 Page 組成,如果尋址空間是 4GB,那么一共只需要 1024 個 PTE 就足夠覆蓋整個進程地址空間。二級頁表中的每一個 PTE 都負責映射到一個 4KB 的虛擬內存頁面,和單頁表的原理是一樣的。

多級頁表的關鍵在于,我們并不需要為一級頁表中的每一個 PTE 都分配一個二級頁表,而只需要為進程當前使用到的地址做相應的分配和映射。因此,對于大部分進程來說,它們的一級頁表中有大量空置的 PTE,那么這部分 PTE 對應的二級頁表也將無需存在,這是一個相當可觀的內存節約,事實上對于一個典型的程序來說,理論上的 4GB 可用虛擬內存地址空間絕大部分都會處于這樣一種未分配的狀態;更進一步,在程序運行過程中,只需要把一級頁表放在主存中,虛擬內存系統可以在實際需要的時候才去創建、調入和調出二級頁表,這樣就可以確保只有那些最頻繁被使用的二級頁表才會常駐在主存中,此舉亦極大地緩解了主存的壓力。

二、 內核空間 & 用戶空間

對 32 位操作系統而言,它的尋址空間(虛擬地址空間,或叫線性地址空間)為 4G(2 的 32 次方)。也就是說一個進程的最大地址空間為 4G。操作系統的核心是內核(kernel),它獨立于普通的應用程序,可以訪問受保護的內存空間,也有訪問底層硬件設備的所有權限。為了保證內核的安全,現在的操作系統一般都強制用戶進程不能直接操作內核。具體的實現方式基本都是由操作系統將虛擬地址空間劃分為兩部分,一部分為內核空間,另一部分為用戶空間。針對 linux 操作系統而言,最高的 1G 字節(從虛擬地址 0xC0000000 到 0xFFFFFFFF)由內核使用,稱為內核空間。而較低的 3G 字節(從虛擬地址 0x00000000 到 0xBFFFFFFF)由各個進程使用,稱為用戶空間。

為什么需要區分內核空間與用戶空間?在 CPU 的所有指令中,有些指令是非常危險的,如果錯用,將導致系統崩潰,比如清內存、設置時鐘等。如果允許所有的程序都可以使用這些指令,那么系統崩潰的概率將大大增加。所以,CPU 將指令分為特權指令和非特權指令,對于那些危險的指令,只允許操作系統及其相關模塊使用,普通應用程序只能使用那些不會造成災難的指令。區分內核空間和用戶空間本質上是要提高操作系統的穩定性及可用性。

2.1 內核態與用戶態

當進程運行在內核空間時就處于內核態,而進程運行在用戶空間時則處于用戶態。

在內核態下,進程運行在內核地址空間中,此時 CPU 可以執行任何指令。運行的代碼也不受任何的限制,可以自由地訪問任何有效地址,也可以直接進行端口的訪問。

在用戶態下,進程運行在用戶地址空間中,被執行的代碼要受到 CPU 的諸多檢查,它們只能訪問映射其地址空間的頁表項中規定的在用戶態下可訪問頁面的虛擬地址,且只能對任務狀態段(TSS)中 I/O 許可位圖(I/O Permission Bitmap)中規定的可訪問端口進行直接訪問。

對于以前的 DOS 操作系統來說,是沒有內核空間、用戶空間以及內核態、用戶態這些概念的??梢哉J為所有的代碼都是運行在內核態的,因而用戶編寫的應用程序代碼可以很容易的讓操作系統崩潰掉。

對于 Linux 來說,通過區分內核空間和用戶空間的設計,隔離了操作系統代碼(操作系統的代碼要比應用程序的代碼健壯很多)與應用程序代碼。即便是單個應用程序出現錯誤也不會影響到操作系統的穩定性,這樣其它的程序還可以正常的運行。

如何從用戶空間進入內核空間?

其實所有的系統資源管理都是在內核空間中完成的。比如讀寫磁盤文件,分配回收內存,從網絡接口讀寫數據等等。我們的應用程序是無法直接進行這樣的操作的。但是我們可以通過內核提供的接口來完成這樣的任務。比如應用程序要讀取磁盤上的一個文件,它可以向內核發起一個 “系統調用” 告訴內核:“我要讀取磁盤上的某某文件”。

其實就是通過一個特殊的指令讓進程從用戶態進入到內核態(到了內核空間),在內核空間中,CPU 可以執行任何的指令,當然也包括從磁盤上讀取數據。具體過程是先把數據讀取到內核空間中,然后再把數據拷貝到用戶空間并從內核態切換到用戶態。此時應用程序已經從系統調用中返回并且拿到了想要的數據,可以開開心心的往下執行了。簡單說就是應用程序把高科技的事情(從磁盤讀取文件)外包給了系統內核,系統內核做這些事情既專業又高效。

三、 IO

在進行 IO 操作時,通常需要經過如下兩個階段:

  • 數據準備階段:數據從硬件到內核空間
  • 數據拷貝階段:數據從內核空間到用戶空間

通常我們所說的 IO 的阻塞/非阻塞以及同步/異步,和這兩個階段關系密切:

  • 阻塞 IO 和非阻塞 IO 判定標準:數據準備階段,應用程序是否阻塞等待操作系統將數據從硬件加載到內核空間;
  • 同步 IO 和異步 IO 判定標準:數據拷貝階段,數據是否備好后直接通知應用程序使用,無需等待拷貝;
3.1 (同步)阻塞 IO

阻塞 IO :當用戶發生了系統調用后,如果數據未從網卡到達內核態,內核態數據未準備好,此時會一直阻塞。直到數據就緒,然后從內核態拷貝到用戶態再返回。

阻塞 IO 每個連接一個單獨的線程進行處理,通常搭配多線程來應對大流量,但是,開辟線程的開銷比較大,一個程序可以開辟的線程是有限的,面對百萬連接的情況,是無法處理。非阻塞 IO 可以一定程度上解決上述問題。

3.2 (同步)非阻塞 IO

非阻塞 IO :在第一階段(網卡-內核態)數據未到達時不等待,然后直接返回。數據就緒后,從內核態拷貝到用戶態再返回。

非阻塞 IO 解決了阻塞 IO每個連接一個線程處理的問題,所以其最大的優點就是 一個線程可以處理多個連接。然而,非阻塞 IO 需要用戶多次發起系統調用。頻繁的系統調用是比較消耗系統資源的。

3.3 IO 多路復用

為了解決非阻塞 IO 存在的頻繁的系統調用這個問題,隨著內核的發展,出現了 IO 多路復用模型。

IO 多路復用:通過一種機制一個進程能同時等待多個文件描述符,而這些文件描述符(套接字描述符)其中的任意一個進入讀就緒狀態,就可以返回。

IO 多路復用本質上復用了系統調用,使多個文件的狀態可以復用一個系統調用獲取,有效減少了系統調用。select、poll、epoll均是基于 IO 多路復用思想實現的。

select 和 poll 的工作原理比較相似,通過 select或者 poll將多個 socket fds 批量通過系統調用傳遞給內核,由內核進行循環遍歷判斷哪些 fd 上數據就緒了,然后將就緒的 readyfds 返回給用戶。再由用戶進行挨個遍歷就緒好的 fd,讀取或者寫入數據。所以通過 IO 多路復用+非阻塞 IO,一方面降低了系統調用次數,另一方面可以用極少的線程來處理多個網絡連接。select 和 poll 的最大區別是:select 默認能處理的最大連接是 1024 個,可以通過修改配置來改變,但終究是有限個;而 poll 理論上可以支持無限個。而 select 和 poll 則面臨相似的問題在管理海量的連接時,會頻繁的從用戶態拷貝到內核態,比較消耗資源。

epoll 有效規避了將 fd 頻繁的從用戶態拷貝到內核態,通過使用紅黑樹(RB-tree)搜索被監視的文件描述符(file deor)。在 epoll 實例上注冊事件時,epoll 會將該事件添加到epoll 實例的紅黑樹上并注冊一個回調函數,當事件發生時會將事件添加到就緒鏈表中。

3.3.1 epoll 數據結構 + 算法

epoll 的核心數據結構是:1 個 紅黑樹和 1 個 雙向鏈表,還有 3個核心API。

3.3.2 監視 socket 索引-紅黑樹

為什么采用紅黑樹呢?因為和 epoll 的工作機制有關。epoll 在添加一個 socket 或者刪除一個 socket 或者修改一個 socket 的時候,它需要查詢速度更快,操作效率最高,因此需要一個更加優秀的數據結構能夠管理這些 socket。我們想到的比如鏈表,數組,二叉搜索樹,B+樹等都無法滿足要求,

  • 因為鏈表在查詢,刪除的時候毫無疑問時間復雜度是 O(n);
  • 數組查詢很快,但是刪除和新增時間復雜度是 O(n);
  • 二叉搜索樹雖然查詢效率是 lgn,但是如果不是平衡的,那么就會退化為線性查找,復雜度直接來到 O(n);
  • B+樹是平衡多路查找樹,主要是通過降低樹的高度來存儲上億級別的數據,但是它的應用場景是內存放不下的時候能夠用最少的 IO 訪問次數從磁盤獲取數據。比如數據庫聚簇索引,成百上千萬的數據內存無法滿足查找就需要到內存查找,而因為 B+樹層高很低,只需要幾次磁盤 IO 就能獲取數據到內存,所以在這種磁盤到內存訪問上 B+樹更適合。

因為我們處理上萬級的 fd,它們本身的存儲空間并不會很大,所以傾向于在內存中去實現管理,而紅黑樹是一種非常優秀的平衡樹,它完全是在內存中操作,而且查找,刪除和新增時間復雜度都是 lgn,效率非常高,因此選擇用紅黑樹實現 epoll 是最佳的選擇。當然不選擇用 AVL 樹是因為紅黑樹是不符合 AVL 樹的平衡條件的,紅黑是用非嚴格的平衡來換取增刪節點時候旋轉次數的降低,任何不平衡都會在三次旋轉之內解決;而 AVL 樹是嚴格平衡樹,在增加或者刪除節點的時候,根據不同情況,旋轉的次數比紅黑樹要多。所以紅黑樹的插入效率更高。

3.3.2 就緒 socket 列表-雙向鏈表

就緒列表存儲的是就緒的 socket,所以它應能夠快速的插入數據。程序可能隨時調用 epoll_ctl 添加監視 socket,也可能隨時刪除。當刪除時,若該 socket 已經存放在就緒列表中,它也應該被移除。(事實上,每個 epoll_item 既是紅黑樹節點,也是鏈表節點,刪除紅黑樹節點,自然刪除了鏈表節點) 所以就緒列表應是一種能夠快速插入和刪除的數據結構。雙向鏈表就是這樣一種數據結構,epoll 使用 雙向鏈表來實現就緒隊列(rdllist)

3.3.3 三個 API 3.3.3.1 int epoll_create(int size)

功能:內核會產生一個 epoll 實例數據結構并返回一個文件描述符 epfd,這個特殊的描述符就是 epoll 實例的句柄,后面的兩個接口都以它為中心。同時也會創建紅黑樹和就緒列表,紅黑樹來管理注冊 fd,就緒列表來收集所有就緒 fd。size 參數表示所要監視文件描述符的最大值,不過在后來的 Linux 版本中已經被棄用(同時,size 不要傳 0,會報 invalid argument 錯誤)

3.3.3.2 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

功能:將被監聽的 socket 文件描述符添加到紅黑樹或從紅黑樹中刪除或者對監聽事件進行修改;同時向內核中斷處理程序注冊一個回調函數,內核在檢測到某文件描述符可讀/可寫時會調用回調函數,該回調函數將文件描述符放在就緒鏈表中。

3.3.3.3 int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

功能:阻塞等待注冊的事件發生,返回事件的數目,并將觸發的事件寫入 events 數組中。events: 用來記錄被觸發的 events,其大小應該和 maxevents 一致 maxevents: 返回的 events 的最大個數處于 ready 狀態的那些文件描述符會被復制進 ready list 中,epoll_wait 用于向用戶進程返回 ready list(就緒列表)。events 和 maxevents 兩個參數描述一個由用戶分配的 struct epoll event 數組,調用返回時,內核將就緒列表(雙向鏈表)復制到這個數組中,并將實際復制的個數作為返回值。注意,如果就緒列表比 maxevents 長,則只能復制前 maxevents 個成員;反之,則能夠完全復制就緒列表。另外,struct epoll event 結構中的 events 域在這里的解釋是:在被監測的文件描述符上實際發生的事件。

3.3.4 工作模式

epoll 對文件描述符的操作有兩種模式:LT(level trigger)和 ET(edge trigger)。

  1. LT 模式 LT(level triggered)是缺省的工作方式,并且同時支持 block 和 no-block socket.在這種做法中,內核告訴你一個文件描述符是否就緒了,然后你可以對這個就緒的 fd 進行 IO 操作。如果你不作任何操作,內核還是會繼續通知你。
  2. ET 模式 ET(edge-triggered)是高速工作方式,只支持 no-block socket。在這種模式下,當描述符從未就緒變為就緒時,內核通過 epoll 告訴你。然后它會假設你知道文件描述符已經就緒,并且不會再為那個文件描述符發送更多的就緒通知,直到你做了某些操作導致那個文件描述符不再為就緒狀態了(比如,你在發送,接收或者接收請求,或者發送接收的數據少于一定量時導致了一個 EWOULDBLOCK 錯誤)。注意,如果一直不對這個 fd 作 IO 操作(從而導致它再次變成未就緒),內核不會發送更多的通知(only once)ET 模式在很大程度上減少了 epoll 事件被重復觸發的次數,因此效率要比 LT 模式高。epoll 工作在 ET 模式的時候,必須使用非阻塞套接口,以避免由于一個文件句柄的阻塞讀/阻塞寫操作把處理多個文件描述符的任務餓死。
3.4 網絡 IO 模型

實際的網絡模型常結合 I/O 復用和線程池實現,如 Reactor 模式:

3.4.1 單 reactor 單線程模型

此種模型通常只有一個 epoll 對象,所有的接收客戶端連接、客戶端讀取、客戶端寫入操作都包含在一個線程內。

優點:模型簡單,沒有多線程、進程通信、競爭的問題,全部都在一個線程中完成 缺點:單線程無法完全發揮多核 CPU 的性能;I/O 操作和非 I/O 的業務操作在一個 Reactor 線程完成,這可能會大大延遲 I/O 請求的響應;線程意外終止,或者進入死循環,會導致整個系統通信模塊不可用,不能接收和處理外部消息,造成節點故障;使用場景:客戶端的數量有限,業務處理非??焖?,比如 redis 在業務處理的時間復雜度 O(1) 的情況

3.4.2 單 reactor 多線程模型

該模型將讀寫的業務邏輯交給具體的線程池來處理

優點:充分利用多核 cpu 的處理能力,提升 I/O 響應速度;缺點:在該模式中,雖然非 I/O 操作交給了線程池來處理,但是所有的 I/O 操作依然由 Reactor 單線程執行,在高負載、高并發或大數據量的應用場景,依然容易成為瓶頸。

3.4.3 multi-reactor 多線程模型

在這種模型中,主要分為兩個部分:mainReactor、subReactors。mainReactor 主要負責接收客戶端的連接,然后將建立的客戶端連接通過負載均衡的方式分發給 subReactors,subReactors 來負責具體的每個連接的讀寫 對于非 IO 的操作,依然交給工作線程池去做。

優點:父線程與子線程的數據交互簡單職責明確,父線程只需要接收新連接,子線程完成后續的業務處理。Reactor 主線程只需要把新連接傳給子線程,子線程無需返回數據。缺點:編程復雜度較高。

3.4.4 主流的中間件所采用的網絡模型 3.5 異步 IO

前面介紹的所有網絡 IO 都是同步 IO,因為當數據在內核態就緒時,在內核態拷貝用用戶態的過程中,仍然會有短暫時間的阻塞等待。而異步 IO 指:內核態拷貝數據到用戶態這種方式也是交給系統線程來實現,不由用戶線程完成,如 windows 的 IOCP ,Linux 的 AIO。

四、 零拷貝 4.1 傳統 IO 流程

傳統 IO 流程會經過如下兩個過程:

  • 數據準備階段:數據從硬件到內核空間
  • 數據拷貝階段:數據從內核空間到用戶空間

零拷貝指數據無需從硬件到內核空間或從內核空間到用戶空間。下面介紹常見的零拷貝實現

4.2 mmap + write

mmap 將內核中讀緩沖區(read buffer)的地址與用戶空間的緩沖區(user buffer)進行映射,從而實現內核緩沖區與應用程序內存的共享,省去了將數據從內核讀緩沖區(read buffer)拷貝到用戶緩沖區(user buffer)的過程,整個拷貝過程會發生 4 次上下文切換,1 次 CPU 拷貝和 2 次 DMA 拷貝。

4.3 sendfile

通過 sendfile 系統調用,數據可以直接在內核空間內部進行 I/O 傳輸,從而省去了數據在用戶空間和內核空間之間的來回拷貝,sendfile 調用中 I/O 數據對用戶空間是完全不可見的,整個拷貝過程會發生 2 次上下文切換,1 次 CPU 拷貝和 2 次 DMA 拷貝。

4.4 Sendfile + DMA gather copy

Linux2.4 引入 ,將內核空間的讀緩沖區(read buffer)中對應的數據描述信息(內存地址、地址偏移量)記錄到相應的網絡緩沖區(socketbuffer)中,由 DMA 根據內存地址、地址偏移量將數據批量地從讀緩沖區(read buffer)拷貝到網卡設備中,這樣就省去了內核空間中僅剩的 1 次 CPU 拷貝操作,發生 2 次上下文切換、0 次 CPU 拷貝以及 2 次 DMA 拷貝;

4.5 splice

Linux2.6.17 版本引入,在內核空間的讀緩沖區(read buffer)和網絡緩沖區(socket buffer)之間建立管道(pipeline),從而避免了兩者之間的 CPU 拷貝操作,2 次上下文切換,0 次 CPU 拷貝以及 2 次 DMA 拷貝。

4.6 寫時復制

通過盡量延遲產生私有對象中的副本,寫時復制最充分地利用了稀有的物理資源。

4.7 JAVA 中零拷貝

MAppedByteBuffer:基于內存映射(mmap)這種零拷貝方式的提供的一種實現。

FileChannel 基于 sendfile 定義了 transferFrom 和 transferTo 兩個抽象方法,它通過在通道和通道之間建立連接實現數據傳輸的。

五、參考

https://mp.weixin.qq.com/s/c81Fvws0J2tHjcdTgxvv6g

https://mp.weixin.qq.com/s/EDzFOo3gcivOe_RgipkTkQ

https://mp.weixin.qq.com/s/G6TfGbc4U8Zhv30wnN0HIg

https://mp.weixin.qq.com/s/r9RU4RoE-qrzXPAwiej5sw

分享到:
標簽:虛擬內存
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定