網站首頁 編程語言 正文
前言
用戶態和內核態:用戶線程和內核態線程有什么區別?
面試題:用戶態和內核態:用戶線程和內核態線程有什么區別?
組合型問題:
- 用戶態和內核態是什么?
- 用戶級線程和內核線程是怎樣的對應關系?
- 內核相應系統調用是怎樣的過程?
- ......
而且這個問題還關聯到了我們后面要學習的多線程、I/O 模型、網絡優化等。 所以這是一道很不錯的面試題目,它不是簡單考某個概念,而是通過讓求職者比較兩種東西,從而考察你對知識整體的認知和理解。 今天就請你順著這個問題,深入學習內核的工作機制,和我一起去理解用戶態和內核態。
一.什么是用戶態和內核態?
Kernel運行在超級權限模式(Supervisor Mode)下,所以擁有很高的權限,按照權限管理原則,多數應用程序應該運行在最小權限下,因此很多操作系統將內存分為2個區域:
- 內核空間:只有內核程序可以訪問
- 用戶空間:專門給應用程序使用
二.用戶態和內核態
用戶空間中的代碼被限制了只能使用一個局部的內存空間,我們說這些程序在用戶態(User Mode)執行。內核空間中的代碼可以訪問所有內存,我們稱這些程序在內核態(Kernal Mode) 執行。
三.系統調用過程
如果用戶態程序需要執行系統調用,就需要切換到內核態執行。下面我們來講講這個過程的原理。
如上圖所示:內核程序執行在內核態(Kernal Mode),用戶程序執行在用戶態(User Mode)。當發生系統調用時,用戶態的程序發起系統調用。因為系統調用中牽扯特權指令,用戶態程序權限不足,因此會中斷執行,也就是 Trap(Trap 是一種中斷)。
發生中斷后,當前 CPU 執行的程序會中斷,跳轉到中斷處理程序。內核程序開始執行,也就是開始處理系統調用。內核處理完成后,主動觸發 Trap,這樣會再次發生中斷,切換回用戶態工作。
四.線程模型
上面我們學習了用戶態和內核態,接下來我們從進程和線程的角度進一步思考本課時開頭拋出的問題。
1.進程和線程
一個應用程序啟動后會在內存中創建一個執行副本,這就是進程。Linux 的內核是一個 MonolithicKernel(宏內核),因此可以看作一個進程。也就是開機的時候,磁盤的內核鏡像被導入內存作為一個執行副本,成為內核進程
進程可以分成用戶態進程和內核態進程兩類。用戶態進程通常是應用程序的副本,內核態進程就是內核本身的進程。如果用戶態進程需要申請資源,比如內存,可以通過系統調用向內核申請。 那么用戶態進程如果要執行程序,是否也要向內核申請呢? 程序在現代操作系統中并不是以進程為單位在執行,而是以一種輕量級進程(Light WeightedProcess),也稱作線程(Thread)的形式執行。一個進程可以擁有多個線程。進程創建的時候,一般會有一個主線程隨著進程創建而創建
如果進程想要創造更多的線程,就需要思考一件事情,這個線程創建在用戶態還是內核態。你可能會問,難道不是用戶態的進程創建用戶態的線程,內核態的進程創建內核態的線程嗎?其實不是,進程可以通過 API 創建用戶態的線程,也可以通過系統調用創建內核態的線程,接下來我們說說用戶態的線程和內核態的線程。
2.用戶態線程
用戶態線程也稱作用戶級線程(User Level Thread)。操作系統內核并不知道它的存在,它完全是在用戶空間中創建。 用戶級線程有很多優勢,比如。
- 管理開銷小:創建、銷毀不需要系統調用。
- 切換成本低:用戶空間程序可以自己維護,不需要走操作系統調度。
但是這種線程也有很多的缺點
- 與內核協作成本高:比如這種線程完全是用戶空間程序在管理,當它進行 I/O 的時候,無法利用到內核的優勢,需要頻繁進行用戶態到內核態的切換。
- 線程間協作成本高:設想兩個線程需要通信,通信需要 I/O,I/O 需要系統調用,因此用戶態線程需要支付額外的系統調用成本。
- 無法利用多核優勢:比如操作系統調度的仍然是這個線程所屬的進程,所以無論每次一個進程有多少用戶態的線程,都只能并發執行一個線程,因此一個進程的多個線程無法利用多核的優勢。
- 操作系統無法針對線程調度進行優化:當一個進程的一個用戶態線程阻塞(Block)了,操作系統無法及時發現和處理阻塞問題,它不會更換執行其他線程,從而造成資源浪費。
3.內核態線程
內核態線程也稱作內核級線程(Kernel Level Thread)。這種線程執行在內核態,可以通過系統調用創造一個內核級線程。 內核級線程有很多優勢。
- 可以利用多核 CPU 優勢:內核擁有較高權限,因此可以在多個 CPU 核心上執行內核線程。
- 操作系統級優化:內核中的線程操作 I/O 不需要進行系統調用;一個內核線程阻塞了,可以立即讓另一個執行。
當然內核線程也有一些缺點。
- 創建成本高:創建的時候需要系統調用,也就是切換到內核態。
- 擴展性差:由一個內核程序管理,不可能數量太多。
- 切換成本較高:切換的時候,也同樣存在需要內核操作,需要切換內核態。
五.用戶態線程和內核態線程之間的映射關系
線程簡單理解,就是要執行一段程序。程序不會自發的執行,需要操作系統進行調度。我們思考這樣一個問題,如果有一個用戶態的進程,它下面有多個線程。如果這個進程想要執行下面的某一個線程,應該如何做呢?
這時,比較常見的一種方式,就是將需要執行的程序,讓一個內核線程去執行。畢竟,內核線程是真正的線程。因為它會分配到 CPU 的執行資源。
如果一個進程所有的線程都要自己調度,相當于在進程的主線程中實現分時算法調度每一個線程,也就是所有線程都用操作系統分配給主線程的時間片段執行。這種做法,相當于操作系統調度進程的主線程;進程的主線程進行二級調度,調度自己內部的線程。 這樣操作劣勢非常明顯,比如無法利用多核優勢,每個線程調度分配到的時間較少,而且這種線程在阻塞場景下會直接交出整個進程的執行權限。 由此可見,用戶態線程創建成本低,問題明顯,不可以利用多核。內核態線程,創建成本高,可以利用多核,切換速度慢。因此通常我們會在內核中預先創建一些線程,并反復利用這些線程。這樣,用戶態線程和內核態線程之間就構成了下面 4 種可能的關系:
1.多對一(Many to One)
用戶態進程中的多線程復用一個內核態線程。這樣,極大地減少了創建內核態線程的成本,但是線程不可以并發。因此,這種模型現在基本上用的很少。我再多說一句,這里你可能會有疑問,比如:用戶態線程怎么用內核態線程執行程序? 程序是存儲在內存中的指令,用戶態線程是可以準備好程序讓內核態線程執行的。后面的幾種方式也是利用這樣的方法。
2.一對一(One to One)
該模型為每個用戶態的線程分配一個單獨的內核態線程,在這種情況下,每個用戶態都需要通過系統調用創建一個綁定的內核線程,并附加在上面執行。 這種模型允許所有線程并發執行,能夠充分利用多核優勢,Windows NT 內核采取的就是這種模型。但是因為線程較多,對內核調度的壓力會明顯增加。
3.多對多(Many to Many)
這種模式下會為 n 個用戶態線程分配 m 個內核態線程。m 通常可以小于 n。一種可行的策略是將 m 設置為核數。這種多對多的關系,減少了內核線程,同時也保證了多核心并發。Linux 目前采用的就是該模型。
4.兩層設計(Two Level)
這種模型混合了多對多和一對一的特點。多數用戶態線程和內核線程是 n 對 m 的關系,少量用戶線程可以指定成 1 對 1 的關系。
上圖所展現的是一個非常經典的設計。
六.總結
我們這節課講解的問題、考慮到的情況以及解決方法,將為你今后解決實際工作場景中的問題打下堅實的基礎。比如處理并發問題、I/O 性能瓶頸、思考數據庫連接池的配置等,要想完美地解決問題,就必須掌握這些模型,了解問題的本質上才能更好地思考問題衍生出來的問題。
這節課我們學習了用戶態和內核態,然后我們簡單學習了進程和線程的基礎知識。 最后,我們還討論了用戶線程和內核線程的映射關系,這是一種非常經典的設計和思考方式。關于這個場景我們討論了 1 對 1、1 對多以及多對 1,兩層模型 4 種方法。日后你在處理線程池對接;遠程 RPC調用;消息隊列時,還會反復用到今天的方法。 那么通過這節課的學習,你現在是否可以來回答本節關聯的面試題目?用戶態線程和內核態線程的區別?老規矩,請你先在腦海里構思下給面試官的表述,并把你的思考寫在留言區,然后再來看我接下來的分析。?【解析】?用戶態線程工作在用戶空間,內核態線程工作在內核空間。用戶態線程調度完全由進程負責,通常就是由進程的主線程負責。相當于進程主線程的延展,使用的是操作系統分配給進程主線程的時間片段。內核線程由內核維護,由操作系統調度。?用戶態線程無法跨核心,一個進程的多個用戶態線程不能并發,阻塞一個用戶態線程會導致進程的主線程阻塞,直接交出執行權限。這些都是用戶態線程的劣勢。內核線程可以獨立執行,操作系統會分配時間片段。因此內核態線程更完整,也稱作輕量級進程。內核態線程創建成本高,切換成本高,創建太多還會給調度算法增加壓力,因此不會太多。?實際操作中,往往結合兩者優勢,將用戶態線程附著在內核態線程中執行。
七.思考題
最后我再給你出一道需要查資料的思考題:JVM 的線程是用戶態線程還是內核態線程?
原文鏈接:https://juejin.cn/post/7140629563456880671
相關推薦
- 2022-07-11 Jenkins修改默認主目錄
- 2022-11-18 python標準庫?datetime的astimezone設置時區遇到的坑及解決_python
- 2022-05-14 Python學習之裝飾器與類的裝飾器詳解_python
- 2022-01-27 editor.md第一行解析失敗,解析成代碼模塊原始輸出
- 2022-08-17 yolov5中anchors設置實例詳解_python
- 2021-12-06 centos7.6批量增加修改刪除虛擬網卡操作介紹_Linux
- 2023-01-09 python數據分析之如何刪除value=0的行_python
- 2022-05-31 Python中的字典及其使用方法_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支