網(wǎng)站添加視頻代碼網(wǎng)絡(luò)建站流程
文章目錄
- 計算機操作系統(tǒng)面試篇
- 用戶態(tài)和內(nèi)核態(tài)的區(qū)別?
- 用戶態(tài)和內(nèi)核態(tài)的區(qū)別?
- 進(jìn)程管理
- 線程和進(jìn)程的區(qū)別是什么?
- 進(jìn)程,線程,協(xié)程的區(qū)別是什么?
- 創(chuàng)建一個協(xié)程的過程
- 線程運行過程中申請到的東西在切換時是否全部要保存,比如線程中有個循環(huán),或者聲明了很多對象,這些是否都要保存,也存在線程私有區(qū)嗎?
- 多線程比單線程的優(yōu)勢,劣勢?
- 進(jìn)程切換和線程切換的區(qū)別?
- 進(jìn)程上下文有哪些?
- 進(jìn)程間通訊有哪些方式?
- 說一下同步和異步
- 異步過程中誰來通知誰
- 內(nèi)存管理
- 堆和棧的區(qū)別?
- 虛擬內(nèi)存
- 32位與64位總線的操作系統(tǒng)的理論虛擬內(nèi)存
- 分段,分頁對比
- 中斷和異常
- 硬中斷、軟中斷講一下
- 異常
- 網(wǎng)絡(luò)IO
- BIO、和NIO、AIO區(qū)別
- select、poll、epoll 的區(qū)別是什么?
- epoll過程
- epoll 的 邊緣觸發(fā)和水平觸發(fā)有什么區(qū)別?
- redis,nginx,netty 是依賴什么做的這么高性能?
- Redis
- Netty
- Nginx
- 零拷貝是什么?
計算機操作系統(tǒng)面試篇
用戶態(tài)和內(nèi)核態(tài)的區(qū)別?
用戶態(tài)和內(nèi)核態(tài)的區(qū)別?
- 內(nèi)核態(tài)(Kernel Mode):在內(nèi)核態(tài)下,CPU可以執(zhí)行所有的指令和訪問所有的硬件資源。這種模式下的操作具有更高的權(quán)限,主要用于操作系統(tǒng)內(nèi)核的運行。
- 用戶態(tài)(User Mode):在用戶態(tài)下,CPU只能執(zhí)行部分指令集,無法直接訪問硬件資源。這種模式下的操作權(quán)限較低,主要用于運行用戶程序。
進(jìn)程管理
線程和進(jìn)程的區(qū)別是什么?
- 定義:
- 進(jìn)程是操作系統(tǒng)資源分配的基本單位
- 線程是任務(wù)調(diào)度和執(zhí)行的基本單位。
- 調(diào)度及切換:
- 每個進(jìn)程都有獨立的代碼和數(shù)據(jù)空間(程序上下文),程序之間的切換會有較大的開銷。
- 線程可以看做輕量級的進(jìn)程,同一類線程共享代碼和數(shù)據(jù)空間,每個線程都有自己獨立的運行棧和程序計數(shù)器(PC),線程之間切換的開銷小。
- 內(nèi)存分配:
- 系統(tǒng)在運行的時候會為每個進(jìn)程分配不同的內(nèi)存空間。
- 對線程而言,除了CPU外,系統(tǒng)不會為線程分配內(nèi)存(線程所使用的資源來自其所屬進(jìn)程的資源),線程組之間只能共享資源。
- 穩(wěn)定性:
- 進(jìn)程中某個線程如果崩潰了,可能會導(dǎo)致整個進(jìn)程都崩潰。
- 進(jìn)程中的子進(jìn)程崩潰,并不會影響其他進(jìn)程。
- 包含關(guān)系:
- 沒有線程的進(jìn)程可以看做是單線程的。
- 如果一個進(jìn)程內(nèi)有多個線程,則執(zhí)行過程不是一條線的,而是多條線。
進(jìn)程,線程,協(xié)程的區(qū)別是什么?
- 進(jìn)程是操作系統(tǒng)中進(jìn)行資源分配和調(diào)度的基本單位,它擁有自己的獨立內(nèi)存空間和系統(tǒng)資源。每個進(jìn)程都有獨立的堆和棧,不與其他進(jìn)程共享。進(jìn)程間通信需要通過特定的機制,如管道、消息隊列、信號量等。由于進(jìn)程擁有獨立的內(nèi)存空間,因此其穩(wěn)定性和安全性相對較高,但同時上下文切換的開銷也較大,因為需要保存和恢復(fù)整個進(jìn)程的狀態(tài)。
- 線程是進(jìn)程內(nèi)的一個執(zhí)行單元,也是CPU調(diào)度和分派的基本單位。與進(jìn)程不同,線程共享進(jìn)程的內(nèi)存空間,包括堆和全局變量。線程之間通信更加高效,因為它們可以直接讀寫共享內(nèi)存。線程的上下文切換開銷較小,因為只需要保存和恢復(fù)線程的上下文,而不是整個進(jìn)程的狀態(tài)。然而,由于多個線程共享內(nèi)存空間,因此存在數(shù)據(jù)競爭和線程安全的問題,需要通過同步和互斥機制來解決。
- 協(xié)程是一種用戶態(tài)的輕量級線程,其調(diào)度完全由用戶程序控制,而不需要內(nèi)核的參與。協(xié)程擁有自己的寄存器上下文和棧,但與其他協(xié)程共享堆內(nèi)存。協(xié)程的切換開銷非常小,因為只需要保存和恢復(fù)協(xié)程的上下文,而無需進(jìn)行內(nèi)核級的上下文切換。協(xié)程需要程序員顯式地進(jìn)行調(diào)度和管理,相對于線程和進(jìn)程來說,其編程模型更為復(fù)雜。
創(chuàng)建一個協(xié)程的過程
使用 CompletableFuture
實現(xiàn)異步協(xié)程
在 Java 中,CompletableFuture
提供了對異步任務(wù)的支持。它允許你定義異步任務(wù)并在任務(wù)完成時做一些處理,從而實現(xiàn)類似協(xié)程的效果。
線程運行過程中申請到的東西在切換時是否全部要保存,比如線程中有個循環(huán),或者聲明了很多對象,這些是否都要保存,也存在線程私有區(qū)嗎?
- 線程運行過程中申請到的東西(如循環(huán)中的狀態(tài)、聲明的局部對象等)在切換時,不需要全部保存。線程切換時,操作系統(tǒng)只會保存和恢復(fù)線程的上下文信息(如棧指針、寄存器、程序計數(shù)器等)。
- 對于局部變量,它們存儲在棧中,線程切換時會自動保存和恢復(fù)。線程私有區(qū)中的數(shù)據(jù)會保留,因為它是與線程相關(guān)聯(lián)的。
多線程比單線程的優(yōu)勢,劣勢?
- 優(yōu)勢:
- 提高程序的運行效率,可以充分利用多核處理器的資源
- 同時處理多個任務(wù),加快程序的執(zhí)行速度。
- 劣勢:
- 存在多線程數(shù)據(jù)競爭訪問的問題,需要通過鎖機制來保證線程安全,增加了加鎖的開銷,還有死鎖的風(fēng)險。
進(jìn)程切換和線程切換的區(qū)別?
- 進(jìn)程切換:
- 進(jìn)程是由內(nèi)核管理和調(diào)度的,所以進(jìn)程的切換只能發(fā)生在內(nèi)核態(tài)。
- 操作系統(tǒng)需要保存當(dāng)前進(jìn)程的狀態(tài)(如寄存器值、程序計數(shù)器等),然后加載目標(biāo)進(jìn)程的狀態(tài)。這不僅包括虛擬內(nèi)存的切換,還包括內(nèi)核堆棧、文件描述符、信號處理等資源的切換。
- 線程切換:
- 同一進(jìn)程中的線程共享相同的 虛擬內(nèi)存,所以內(nèi)存切換的開銷要比進(jìn)程切換小。
- 線程切換只需要保存和加載線程的上下文(如寄存器、棧指針等),而不需要切換整個進(jìn)程的內(nèi)存空間。
進(jìn)程上下文有哪些?
進(jìn)程的上下文切換不僅包含了虛擬內(nèi)存、棧、全局變量等用戶空間的資源,還包括了內(nèi)核堆棧、寄存器等內(nèi)核空間的資源。
進(jìn)程間通訊有哪些方式?
- 管道pipe:用于在同一主機上的進(jìn)程間傳輸數(shù)據(jù)的一種通信方式。通常在父子進(jìn)程之間使用。
- 使用內(nèi)存緩沖區(qū),數(shù)據(jù)寫入一端后,從另一端讀取。
- 消息隊列
- 共享內(nèi)存:不同進(jìn)程間共享一塊內(nèi)存區(qū)域的通信方式,允許進(jìn)程直接讀寫這塊共享內(nèi)存。
- 需要使用 信號量 或 互斥鎖 等同步機制,避免并發(fā)訪問時出現(xiàn)數(shù)據(jù)沖突。
- 信號signal:信號是操作系統(tǒng)用來通知進(jìn)程某個事件發(fā)生的一種機制。
- 信號量(Semaphore):信號量是一種用于同步和控制對共享資源的訪問的計數(shù)器。它可以被視為一個整數(shù),用于表示資源的數(shù)量。
- socket:套接字是一種跨網(wǎng)絡(luò)的進(jìn)程間通信方式,通常用于不同主機之間的通信。它支持面向連接(如 TCP)和無連接(如 UDP)通信。支持全雙工通信。
說一下同步和異步
- 同步(Synchronous):同步是指任務(wù)的執(zhí)行是按順序進(jìn)行的,后續(xù)任務(wù)必須等前一個任務(wù)完成后才能開始執(zhí)行。
- 異步(Asynchronous):異步是指任務(wù)的執(zhí)行不需要等待前一個任務(wù)完成,任務(wù)可以并行執(zhí)行,后續(xù)任務(wù)可以在前一個任務(wù)未完成時開始。
異步過程中誰來通知誰
- 通常是由事件循環(huán)、回調(diào)函數(shù)或Future對象來“通知”A任務(wù)。具體來說,A任務(wù)可以通過
await
或回調(diào)機制來等待和接收結(jié)果。 - 如果A任務(wù)依賴于I/O操作或其他異步任務(wù)的完成,通常會使用上述機制來管理任務(wù)狀態(tài)的切換,確保任務(wù)完成時,能夠通知A任務(wù)并繼續(xù)執(zhí)行后續(xù)操作。
內(nèi)存管理
堆和棧的區(qū)別?
-
分配方式:堆是動態(tài)分配內(nèi)存,由程序員手動申請和釋放內(nèi)存,通常用于存儲動態(tài)數(shù)據(jù)結(jié)構(gòu)和對象。棧是靜態(tài)分配內(nèi)存,由編譯器自動分配和釋放內(nèi)存,用于存儲函數(shù)的局部變量和函數(shù)調(diào)用信息。
-
內(nèi)存管理:堆需要程序員手動管理內(nèi)存的分配和釋放,如果管理不當(dāng)可能會導(dǎo)致內(nèi)存泄漏或內(nèi)存溢出。棧由編譯器自動管理內(nèi)存,遵循后進(jìn)先出的原則,變量的生命周期由其作用域決定,函數(shù)調(diào)用時分配內(nèi)存,函數(shù)返回時釋放內(nèi)存。
-
大小和速度:堆通常比棧大,內(nèi)存空間較大,動態(tài)分配和釋放內(nèi)存需要時間開銷。棧大小有限,通常比較小,內(nèi)存分配和釋放速度較快,因為是編譯器自動管理。
虛擬內(nèi)存
- 傳統(tǒng)內(nèi)存:
- 一次性:作業(yè)必須一次性全部裝入內(nèi)存后才能開始運行。
- 駐留性:一旦作業(yè)被裝入內(nèi)存,就會一直駐留在內(nèi)存中,直至作業(yè)運行結(jié)束。
- 虛擬內(nèi)存:虛擬內(nèi)存的最大容量是由計算機的地址結(jié)構(gòu)(CPU尋址范圍)確定的。虛擬內(nèi)存的實際容量= min{內(nèi)存和外存容量之和,CPU尋址范圍}。32位計算機地址結(jié)構(gòu)為4GB。
- 多次性:無需在作業(yè)運行時一次性全部裝入內(nèi)存,而是允許被分成多次調(diào)入內(nèi)存。
- 對換性:在作業(yè)運行時無需一直常駐內(nèi)存,而是允許在作業(yè)運行過程中,將作業(yè)換入、換出。
- 虛擬性:從邏輯上擴充了內(nèi)存的容量,使用戶看到的內(nèi)存容量,遠(yuǎn)大于實際的容量。
虛擬內(nèi)存實現(xiàn)有3種方式:請求分頁存儲管理、請求分段存儲管理、請求段頁式存儲管理。
無論哪種方式都需要一定的硬件支持:包括一定容量的內(nèi)存與外存;頁表機制、段表機制作為主要的數(shù)據(jù)結(jié)構(gòu);中斷機制:當(dāng)用戶程序要訪問的部分會調(diào)入內(nèi)存時產(chǎn)生中斷;地址轉(zhuǎn)換機構(gòu)邏輯地址到物理地址的轉(zhuǎn)換。
32位與64位總線的操作系統(tǒng)的理論虛擬內(nèi)存
虛擬內(nèi)存大小=min{內(nèi)存+外存,總線位數(shù)}
-
32位總線大小為4G
-
Windows 64 位操作系統(tǒng)通常為每個進(jìn)程分配 8 TB 的虛擬地址空間。
-
Linux 在 x86_64 架構(gòu)下,默認(rèn)每個進(jìn)程可以使用 128 TB 的虛擬內(nèi)存。
分段,分頁對比
-
存儲特性:
- 頁是信息的物理單位。分頁的主要目的是為了實現(xiàn)離散分配,提高內(nèi)存利用率。分頁僅僅是系統(tǒng)管理上的需要,完全是系統(tǒng)行為,對用戶是不可見的。
- 段是信息的邏輯單位。分段的主要目的是更好地滿足用戶需求。一個段通常包含著一組屬于一個邏輯模塊的信息。
-
存儲大小:
- 頁的大小固定且由系統(tǒng)決定。
- 段的長度卻不固定,決定于用戶編寫的程序。
-
用戶進(jìn)程地址維度:
- 分頁的用戶進(jìn)程地址空間是一維的,程序員只需給出一個記憶符即可表示一個地址。
- 分段的用戶進(jìn)程地址空間是二維的,程序員在標(biāo)識一個地址時,既要給出段名,也要給出段內(nèi)地址。
-
碎片問題:
- 分頁的內(nèi)存利用率高,不會產(chǎn)生外部碎片,只會有少量內(nèi)部碎片。
- 如果段長過大,會產(chǎn)生外部碎片。
中斷和異常
硬中斷、軟中斷講一下
硬中斷(Hardware Interrupt)和軟中斷(Software Interrupt)是計算機系統(tǒng)中中斷機制的兩種基本類型,它們都是用來打斷當(dāng)前的程序執(zhí)行,轉(zhuǎn)而處理一些重要任務(wù)或事件。
- 硬中斷(Hardware Interrupt):由外部硬件設(shè)備發(fā)出的信號,用來通知CPU有緊急的事件或任務(wù)需要處理。
- 定時器中斷:CPU通過定時器定期中斷自己,以便執(zhí)行操作系統(tǒng)的任務(wù)調(diào)度。
- I/O設(shè)備中斷:例如,網(wǎng)絡(luò)接口卡接收到數(shù)據(jù)包時,觸發(fā)中斷通知CPU進(jìn)行處理。
- 軟中斷(Software Interrupt):軟中斷是由程序中的軟件指令觸發(fā)的中斷,它通常用于程序之間的通訊、系統(tǒng)調(diào)用或?qū)崿F(xiàn)某些特定功能。
- 系統(tǒng)調(diào)用:用戶程序通過觸發(fā)軟中斷向操作系統(tǒng)請求資源,如文件讀寫、進(jìn)程管理等。
異常
異常(Exception),陷入,也成為內(nèi)中斷,來自于CPU內(nèi)部的事件。
如非法操作碼、地址越界、算術(shù)溢出、虛存系統(tǒng)缺頁等等,異常不能被屏蔽,一旦出現(xiàn)應(yīng)當(dāng)立即處理。
網(wǎng)絡(luò)IO
BIO、和NIO、AIO區(qū)別
特性 | BIO | NIO | AIO |
---|---|---|---|
工作模式 | 阻塞 I/O,數(shù)據(jù)流模式 | 非阻塞 I/O,緩沖區(qū) + 通道 + 選擇器 | 異步 I/O,回調(diào)通知 |
線程模型 | 每個連接一個線程 | 多個通道由單線程管理(通過 Selector) | 異步 I/O,無需阻塞或輪詢 |
I/O 操作 | 阻塞,直到 I/O 操作完成 | 非阻塞,可以輪詢多個通道的事件 | 異步,不會阻塞,完成時回調(diào)通知 |
適用場景 | 并發(fā)連接數(shù)少,低性能需求 | 高并發(fā)、大量連接,I/O 密集型應(yīng)用 | 超高并發(fā)、大數(shù)據(jù)量 I/O 操作 |
性能問題 | 并發(fā)連接數(shù)多時性能差(線程開銷大) | 比 BIO 性能好,但需要輪詢(高并發(fā)時更有效) | 性能非常高,尤其適用于高并發(fā)應(yīng)用 |
select、poll、epoll 的區(qū)別是什么?
- select:
select
是最早出現(xiàn)的一種 I/O 多路復(fù)用機制,它允許在多個文件描述符上監(jiān)視是否有 I/O 事件發(fā)生(如可讀、可寫等)。select
調(diào)用會阻塞并等待直到文件描述符集中的某個或某些文件描述符準(zhǔn)備就緒,之后就返回。- 缺點:
- 文件描述符限制:
select
限制了每次監(jiān)視的文件描述符數(shù)量,通常是 1024(可以通過修改編譯時的宏來增大,但還是有限制)。 - 性能問題:每次調(diào)用
select
都需要遍歷所有的文件描述符集合,這對于大規(guī)模的文件描述符集會導(dǎo)致性能瓶頸。 - 重新設(shè)置:每次調(diào)用
select
時,必須重設(shè)文件描述符集,帶來了額外的開銷。
- 文件描述符限制:
- poll:
poll
是對select
的改進(jìn),具有與select
相似的功能,但沒有文件描述符數(shù)量限制。poll
用一個pollfd
結(jié)構(gòu)體數(shù)組來表示多個文件描述符及其事件,它返回時會告知哪些文件描述符有 I/O 事件發(fā)生。- 優(yōu)點:
- 沒有文件描述符限制:相較于
select
,poll
不再有最大文件描述符數(shù)量的限制。 - 動態(tài)文件描述符集合:
poll
可以動態(tài)地管理文件描述符集合,而不需要像select
一樣每次都重設(shè)整個集合。
- 沒有文件描述符限制:相較于
- 缺點:
- 性能瓶頸:盡管去除了文件描述符的數(shù)量限制,但
poll
仍然需要遍歷整個文件描述符集合,每次調(diào)用時都需要遍歷整個數(shù)組,這對于大量文件描述符來說性能較差。 - 返回值不夠直觀:
poll
只是返回哪些文件描述符有事件發(fā)生,沒有直接告訴應(yīng)用程序事件發(fā)生的位置,因此需要遍歷pollfd
數(shù)組進(jìn)行判斷。
- 性能瓶頸:盡管去除了文件描述符的數(shù)量限制,但
- epoll:
epoll
是 Linux 中引入的 I/O 多路復(fù)用機制,是對select
和poll
的進(jìn)一步優(yōu)化,專門為高并發(fā)場景設(shè)計。epoll
使用事件驅(qū)動的機制,提供了更高效的事件通知方式。epoll
會通過內(nèi)核級別的事件通知,不需要每次遍歷所有的文件描述符,只有在某些文件描述符準(zhǔn)備就緒時,內(nèi)核才會通知用戶進(jìn)程。- 優(yōu)點:
- 無文件描述符數(shù)量限制:
epoll
不受文件描述符數(shù)量限制,可以處理成千上萬的連接。 - 高效的性能:
epoll
使用基于回調(diào)的機制,只在事件發(fā)生時通知用戶程序,不需要每次都遍歷文件描述符集合。 - 邊緣觸發(fā)和水平觸發(fā):
epoll
支持兩種觸發(fā)模式,水平觸發(fā)(Level Triggered)和邊緣觸發(fā)(Edge Triggered),提供了更靈活的控制。 - 內(nèi)存管理:
epoll
在內(nèi)核中維護(hù)一個事件通知隊列,通過epoll_wait
來通知應(yīng)用程序哪些文件描述符有事件發(fā)生,效率較高。
- 無文件描述符數(shù)量限制:
- 缺點:
- 只能在 Linux 上使用:
epoll
是 Linux 特有的系統(tǒng)調(diào)用,無法在其他操作系統(tǒng)上使用。 - 復(fù)雜度較高:相較于
select
和poll
,epoll
需要更復(fù)雜的設(shè)置和管理,尤其是在使用邊緣觸發(fā)模式時。
- 只能在 Linux 上使用:
epoll過程
lfd=socket()
創(chuàng)建lfd套接字—>bind()
綁定地址—>listen()
設(shè)置監(jiān)聽上限—>epoll_create()
創(chuàng)建監(jiān)聽紅黑樹—>epoll_ctl()
把lfd加入紅黑樹—>while(1) 服務(wù)器端上線等待連接。epoll_wait()
服務(wù)器監(jiān)聽—>有事件發(fā)生—>epoll_wait()
返回監(jiān)聽滿足數(shù)組—>一旦有事件發(fā)生則lfd一定在滿足數(shù)組中,lfd
進(jìn)行accept()
—>用lfd進(jìn)行accept()
返回的連接套接字cfd
放到紅黑樹中—>執(zhí)行讀操作—>進(jìn)行大小寫轉(zhuǎn)換操作—>把cfd
節(jié)點的屬性從EPOLLIN
變?yōu)?code>EPOLLOUT—>再把cfd
重新掛上紅黑樹去監(jiān)聽寫事件—>等待epoll_wait()
返回—>有返回說明能寫—>執(zhí)行寫操作—>把cfd
從紅黑樹中拿下來—>再把cfd
節(jié)點的屬性從EPOLLIN
變?yōu)?code>EPOLLOUT—>再把cfd重新掛上紅黑樹去監(jiān)聽讀事件—>epoll_wait()
服務(wù)器監(jiān)聽。從而形成一個完美的閉環(huán)。
epoll 的 邊緣觸發(fā)和水平觸發(fā)有什么區(qū)別?
epoll 支持兩種事件觸發(fā)模式,分別是邊緣觸發(fā)(edge-triggered,ET)和水平觸發(fā)(level-triggered,LT)。
特性 | 水平觸發(fā) (LT) | 邊緣觸發(fā) (ET) |
---|---|---|
通知方式 | 當(dāng)文件描述符的事件條件滿足時,每次調(diào)用都會返回 | 只有文件描述符的事件從未就緒變?yōu)榫途w時才通知一次 |
事件處理 | 只要事件沒有處理完,epoll 會繼續(xù)返回該文件描述符 | 事件一旦通知后,必須完全處理,否則不會再次通知 |
是否重復(fù)通知 | 會重復(fù)通知,直到事件完全處理 | 不會重復(fù)通知,只有狀態(tài)變化時才通知一次 |
適用場景 | 適合大多數(shù)簡單應(yīng)用,處理事件比較慢時使用 | 適合高性能、高并發(fā)應(yīng)用,要求迅速處理事件 |
性能 | 性能較低,重復(fù)通知會增加開銷 | 性能較高,因為不重復(fù)通知 |
實現(xiàn)難度 | 簡單,容易實現(xiàn) | 稍復(fù)雜,要求處理得當(dāng),否則可能錯過事件通知 |
- 使用水平觸發(fā)模式,當(dāng)內(nèi)核通知文件描述符可讀寫時,接下來還可以繼續(xù)去檢測它的狀態(tài),看它是否依然可讀或可寫。
- 使用邊緣觸發(fā)模式,I/O 事件發(fā)生時只會通知一次,而且我們不知道到底能讀寫多少數(shù)據(jù),所以在收到通知后應(yīng)盡可能地讀寫數(shù)據(jù),以免錯失讀寫的機會。因此,我們會循環(huán)從文件描述符讀寫數(shù)據(jù),那么如果文件描述符是阻塞的,沒有數(shù)據(jù)可讀寫時,進(jìn)程會阻塞在讀寫函數(shù)那里,程序就沒辦法繼續(xù)往下執(zhí)行。所以,邊緣觸發(fā)模式一般和非阻塞 I/O 搭配使用,程序會一直執(zhí)行 I/O 操作,直到系統(tǒng)調(diào)用返回錯誤,錯誤類型為 EAGAIN 或 EWOULDBLOCK。
redis,nginx,netty 是依賴什么做的這么高性能?
主要是依賴Reactor 模式實現(xiàn)了高性能網(wǎng)絡(luò)模式,這個是在i/o多路復(fù)用接口基礎(chǔ)上實現(xiàn)的了網(wǎng)絡(luò)模型。
Reactor 模式主要由 Reactor 和處理資源池這兩個核心部分組成,它倆負(fù)責(zé)的事情如下:
- Reactor 負(fù)責(zé)監(jiān)聽和分發(fā)事件,事件類型包含連接事件、讀寫事件;
- 處理資源池負(fù)責(zé)處理事件,如 read -> 業(yè)務(wù)邏輯 -> send;
Redis
Redis 6.0 之前使用的 Reactor 模型就是單 Reactor 單進(jìn)程模式。
- 因為只有一個進(jìn)程,無法充分利用 多核 CPU 的性能;
- Handler 對象在業(yè)務(wù)處理時,整個進(jìn)程是無法處理其他連接的事件的,如果業(yè)務(wù)處理耗時比較長,那么就造成響應(yīng)的延遲
單 Reactor 單進(jìn)程的方案不適用計算機密集型的場景,只適用于業(yè)務(wù)處理非??焖俚膱鼍?/strong>。所以Redis的瓶頸不在CPU上,網(wǎng)絡(luò)IO才是瓶頸。
因此在Redis6.0后,Redis 在啟動的時候,默認(rèn)情況下會額外創(chuàng)建 6 個線程(這里的線程數(shù)不包括主線程):
- Redis-server : Redis的主線程,主要負(fù)責(zé)執(zhí)行命令。
- bio_close_file、bio_aof_fsync、bio_lazy_free:三個后臺線程,分別異步處理關(guān)閉文件任務(wù)、AOF刷盤任務(wù)、釋放內(nèi)存任務(wù);
- io_thd_1、io_thd_2、io_thd_3:三個 I/O 線程,io-threads 默認(rèn)是 4 ,所以會啟動 3(4-1)個 I/O 多線程,用來分擔(dān) Redis 網(wǎng)絡(luò) I/O 的壓力。
Netty
Netty 是采用了多 Reactor 多線程方案
每個客戶端通過通道(Channel)與服務(wù)端進(jìn)行數(shù)據(jù)交互,客戶端通過端口向服務(wù)端發(fā)送連接請求。服務(wù)端使用一個線程,通過多路復(fù)用器(Selector)來監(jiān)聽多個客戶端的連接請求和數(shù)據(jù)事件,服務(wù)端會將每個客戶端的通道注冊到 Selector 上進(jìn)行管理。
- 主線程和子線程分工明確,主線程只負(fù)責(zé)接收新連接,子線程負(fù)責(zé)完成后續(xù)的業(yè)務(wù)處理。
- 主線程和子線程的交互很簡單,主線程只需要把新連接傳給子線程,子線程無須返回數(shù)據(jù),直接就可以在子線程將處理結(jié)果發(fā)送給客戶端。
Netty特點:
- 事件驅(qū)動和異步機制:通過回調(diào)機制處理I/O結(jié)果
- boss-worker:boss只負(fù)責(zé)管理,具體處理交給worker,還有更加智能的線程管理
- 內(nèi)存管理優(yōu)化:采用內(nèi)存池化區(qū)別于傳統(tǒng)每次讀取都會分配新的字節(jié)數(shù)組
- 支持多種協(xié)議:不僅有tcp、udp還有http、https、websocket等等
Nginx
nginx 是多 Reactor 多進(jìn)程方案,不過方案與標(biāo)準(zhǔn)的多 Reactor 多進(jìn)程有些差異。
- 主進(jìn)程中僅僅用來初始化 socket,并沒有創(chuàng)建 mainReactor 來 accept 連接
- 由子進(jìn)程的 Reactor 來 accept 連接,通過鎖來控制一次只有一個子進(jìn)程進(jìn)行 accept(為了避免多個子進(jìn)程同時響應(yīng)同一個請求,也就是驚群現(xiàn)象),子進(jìn)程 accept 新連接后就放到自己的 Reactor 進(jìn)行處理,不會再分配給其他子進(jìn)程。
零拷貝是什么?
-
傳統(tǒng) IO 的工作方式,從硬盤讀取數(shù)據(jù),然后再通過網(wǎng)卡向外發(fā)送,我們需要進(jìn)行 4 上下文切換,和 4 次數(shù)據(jù)拷貝,其中 2 次數(shù)據(jù)拷貝發(fā)生在內(nèi)存里的緩沖區(qū)和對應(yīng)的硬件設(shè)備之間,這個是由 DMA 完成,另外2 次則發(fā)生在內(nèi)核態(tài)和用戶態(tài)之間,這個數(shù)據(jù)搬移工作是由 CPU 完成的。
-
零拷貝技術(shù),它通過一次系統(tǒng)調(diào)用(sendfile 方法)合并了磁盤讀取與網(wǎng)絡(luò)發(fā)送兩個操作,降低了上下文切換次數(shù)。另外,拷貝數(shù)據(jù)都是發(fā)生在內(nèi)核中的,天然就降低了數(shù)據(jù)拷貝的次數(shù)。使用
sendfile()
,內(nèi)核直接將文件內(nèi)容從磁盤緩沖區(qū)傳輸?shù)骄W(wǎng)絡(luò)接口,無需通過用戶空間。
適用場景:
- Web 服務(wù)器:在 Web 服務(wù)器中,零拷貝可以用來提高大文件(如視頻、圖片等)的傳輸效率。例如,Nginx 使用了
sendfile()
實現(xiàn)零拷貝。 - 高性能網(wǎng)絡(luò)應(yīng)用:零拷貝特別適用于需要頻繁進(jìn)行大規(guī)模數(shù)據(jù)傳輸?shù)膽?yīng)用,如文件傳輸、數(shù)據(jù)庫備份、視頻流等。
- 數(shù)據(jù)庫管理系統(tǒng):數(shù)據(jù)庫的磁盤和網(wǎng)絡(luò)傳輸操作常常使用零拷貝技術(shù),以提高數(shù)據(jù)讀取和寫入的效率。