自己做個記錄,也希望能幫助想要了解的人
最終能理解這個,得益於網絡上很多前輩的博客和自己粗看過《深入理解計算機系統》
涉及一些計算機基礎知識,會先提及,用一種簡單的方式讓大家有個基本的概念,以幫助理解最后需要討論的東西
基礎知識
操作系統
操作系統本身可以看做一個特殊的軟件,只有操作系統能直接接觸計算機硬件,其他軟件要訪問硬件都必須通過操作系統
操作系統對硬件做抽象,封裝接口給軟件調用
程序員
↓
軟件
↓
操作系統(在內存中)
↓
IO設備(網卡、磁盤、鍵盤、鼠標等)
常見的操作系統有:
Windows、Linux、Mac
內核和用戶空間
首先,內核和用戶空間都在內存中
內核:
只要計算機處於開啟狀態,操作系統(記住它是一種特殊的軟件)就會把它的程序和數據放入內存中,由於操作系統的重要性,它會獨占內存中的一塊區域,這塊區域就稱之為內核
即,內核就是操作系統常駐內存的區域
用戶空間:
而用戶空間,就是某個進程啟動后,被分配到的一塊內存區域
注:
具體的涉及到虛擬地址空間的概念,想要詳細了解的童鞋可以看下《深入理解計算機系統》第三版的第十章“虛擬存儲器”
IO
具體到 IO,操作系統提供了一些 IO 操作,用於操作 IO 設備的數據,然后應用軟件對其進行封裝
數據流轉:
網絡IO:網卡 → 內核 → 用戶空間(用戶內存)
磁盤IO:磁盤
→ 內核 → 用戶空間(用戶內存)
各個操作系統分別提供了哪些 IO 操作?有哪幾種 IO 模型?
IO 操作:
select、poll、epoll、kqueue、evport
IO 模型:
以 Linux 為例,它有五種 IO 模型:
阻塞IO模型、非阻塞IO模型、IO復用模型、信號驅動IO模型以及異步IO模型
可參考此鏈接中的內容,講的很細:
Redis
Redis 是怎么對操作系統提供的 IO 操作進行封裝的?
可參考:
為什么 Redis 使用了單線程 IO 多路復用?為什么那么快?
1、cpu 處理相比於 IO 可以忽略
每個客戶端建立連接時,都需要服務端為其 創建 socket 套接字,建立連接
然后該客戶端的每個請求都要經歷以下幾步:
(1)等待請求數據數據從客戶端發送過來
(2)將請求數據從內核復制到用戶進程的緩沖區(buffer)
(3)對請求數據進行處理(對於 redis 而言,一般就是簡單的 get/set)
由於操作簡單+只涉及內存,所以第(3)步的處理很簡單、很快,主要時間耗在(1)步,所以,如果采用普通 BIO
模式,每個請求都要經歷這幾步,那么處理十萬條數據,就要在(1)步花費大量的時間,這樣的話,qps 一定很低。所以就采用了更高效的 IO 多路復用模式,即,將(1)步統一交給第三方(也就是操作系統,操作系統提供了 select、poll、epoll、kqueue、iocp等系統調用函數),結合 redis 的單線程,現在整個處理流程是這樣的:
一下子來了一堆請求,線程將這些請求都交給操作系統去處理,讓操作系統幫忙完成第(1)步,等到這些請求里的一個或多個走完了第(1)步,就將一個集合交給這個線程,並說,我這里收集到了幾個走完第一步的請求,你去走(2)、(3)步吧,於是線程拿着這個集合去遍歷,等遍歷結束之后。又去檢查操作系統那兒有沒有(這個線程自己維護了一個 while 循環)走完第(1)步的請求,發現又有一些了,拿到后繼續遍歷進行(2)、(3)步,如此循環往復。
注:有些
IO 模式是將(1)(2)步都交給操作系統處理了,線程本身只需處理第(3)步
2、瓶頸在帶寬,而不在 cpu
由於 數據存放在內存中+處理邏輯簡單,導致即使是單線程,Redis 可支持的 qps 也相當大,而當 qps 相當大的時候,首先限制性能的是帶寬,即不需要把 cpu 的性能挖掘出來,因為在這之前,帶寬就不夠用了。所以沒有必要為了提高 cpu 利用率而使用多線程處理業務邏輯。
參考資料
1、https://mp.weixin.qq.com/s/XzLHy41JrCV_y3BZpeTgwQ
2、https://draveness.me/redis-io-multiplexing/
3、https://mp.weixin.qq.com/s/vrUiKnwjWb3esk8lfiiOfw
4、《深入理解計算機系統》第三版