用戶態與內核態詳解


當一個任務(進程)執行系統調用而陷入內核代碼中執行時,我們就稱進程處於內核運行態(或簡稱為內核態)。此時處理器處於特權級最高的(0級)內核代碼中執行。當進程處於內核態時,執行的內核代碼會使用當前進程的內核棧。每個進程都有自己的內核棧。當進程在執行用戶自己的代碼時,則稱其處於用戶運行態(用戶態)。即此時處理器在特權級最低的(3級)用戶代碼中運行。當正在執行用戶程序而突然被中斷程序中斷時,此時用戶程序也可以象征性地稱為處於進程的內核態。因為中斷處理程序將使用當前進程的內核棧。這與處於內核態的進程的狀態有些類似。

 1、用系統調用時進入核心態。Linux對硬件的操作只能在核心態,這可以通過寫驅動程序來控制。在用戶態操作硬件會造成core dump。
   2、要注意區分系統調用和一般的函數。系統調用由內核提供,如read()write()open()等。而一般的函數由軟件包中的函數庫提供,如sin()cos()等。在語法上兩者沒有區別。   
   3、一般情況:系統調用運行在核心態,函數運行在用戶態。但也有一些函數在內部使用了系統調用(如fopen),這樣的函數在調用系統調用是進入核心態,其他時候運行在用戶態。

大概是    當用戶程序調用系統的API時,就產生中斷,進入內核態的API,處理完成后,用中斷再退出,返回用戶態的調用函數。   
   user    api    -->    interrupt    -->    kernel    api    -->    interrupt

---------------------------------------------------------------------
簡單來講一個進程由於執行系統調用而開始執行內核代碼,我們稱該進程處於內核態中. 一個進程執行應用程序自身代碼則稱該進程處於用戶態.
intel x86 架構的 CPU 分為好幾個運行級別,從 0--3 , 0 為最高級別, 3 為最低級別
針對不同的級別,有很多的限制,比如說傳統的 in ,out 指令,就是端口的輸入輸出指令,在 0 級下是可以用的,但在 3 級下就不能用,你用就產生陷阱,告訴你出錯了,當然限制還有很多了,不只是這一點。
操作系統下是利用這個特點,當操作系統自己的代碼運行時, CPU 就切成 0 級,當用戶的程序運行是就只讓它在 3 級運行,這樣如果用戶的程序想做什么破壞系統的事情的話,也沒辦法做到
當然,低級別的程序是沒法把自己升到高級別的,也就是說 用戶程序運行在 3 級,他想把自己變成 0 級自己是做不到的,除非是操作系統幫忙,利用這個特性,操作系統就可以控制所有的程序的運行,確保系統的安全了. 平時把操作系統運行時的級別就叫內核態(因為是操作系統內核運行時的狀態),而且普通用戶程序運行時的那個級別叫用戶態...
當操作系統剛引導時, CPU 處於實模式,這時就相當於是 0 級,於是操作系統就自動得到最高權限,然后切到保護模式時就是0級,這時操作系統就占了先機,成為了最高級別的運行者,由於你的程序都是由操作系統來加載的,所以當它把你加載上來后,就把你的運行狀態設為 3 級,即最低級,然后才讓你運行,所以沒辦法,你只能在最低級運行了,因為沒辦法把自己從低級上升到高級, 這就是操作系統在內核態可以管理用戶程序,殺死用戶程序的原因。

1. 用戶態和內核態的概念區別

究竟什么是用戶態,什么是內核態,這兩個基本概念以前一直理解得不是很清楚,根本原因個人覺得是在於因為大部分時候我們在寫程序時關注的重點和着眼的角度放在了實現的功能和代碼的邏輯性上,先看一個例子:

1)例子

   void testfork(){

       if(0 = = fork()){

           printf(“create new process success!\n”);

       }

       printf(“testfork ok\n”);

   }

       這段代碼很簡單,從功能的角度來看,就是實際執行了一個fork(),生成一個新的進程,從邏輯的角度看,就是判斷了如果fork()返回的是0則打印相關語句,然后函數最后再打印一句表示執行完整個testfork()函數。代碼的執行邏輯和功能上看就是如此簡單,一共四行代碼,從上到下一句一句執行而已,完全看不出來哪里有體現出用戶態和進程態的概念。

如果說前面兩種是靜態觀察的角度看的話,我們還可以從動態的角度來看這段代碼,即它被轉換成CPU執行的指令后加載執行的過程,這時這段程序就是一個動態執行的指令序列。而究竟加載了哪些代碼,如何加載就是和操作系統密切相關了。

2)特權級

熟悉Unix/Linux系統的人都知道,fork的工作實際上是以系統調用的方式完成相應功能的,具體的工作是由sys_fork負責實施。其實無論是不是Unix或者Linux,對於任何操作系統來說,創建一個新的進程都是屬於核心功能,因為它要做很多底層細致地工作,消耗系統的物理資源,比如分配物理內存,從父進程拷貝相關信息,拷貝設置頁目錄頁表等等,這些顯然不能隨便讓哪個程序就能去做,於是就自然引出特權級別的概念,顯然,最關鍵性的權力必須由高特權級的程序來執行,這樣才可以做到集中管理,減少有限資源的訪問和使用沖突。

特權級顯然是非常有效的管理和控制程序執行的手段,因此在硬件上對特權級做了很多支持,就Intel x86架構的CPU來說一共有0~3四個特權級,0級最高,3級最低,硬件上在執行每條指令時都會對指令所具有的特權級做相應的檢查,相關的概念有CPL、DPL和RPL,這里不再過多闡述。硬件已經提供了一套特權級使用的相關機制,軟件自然就是好好利用的問題,這屬於操作系統要做的事情,對於Unix/Linux來說,只使用了0級特權級和3級特權級。也就是說在Unix/Linux系統中,一條工作在0級特權級的指令具有了CPU能提供的最高權力,而一條工作在3級特權級的指令具有CPU提供的最低或者說最基本權力。

3)用戶態和內核態

    現在我們從特權級的調度來理解用戶態和內核態就比較好理解了,當程序運行在3級特權級上時,就可以稱之為運行在用戶態,因為這是最低特權級,是普通的用戶進程運行的特權級,大部分用戶直接面對的程序都是運行在用戶態;反之,當程序運行在0級特權級上時,就可以稱之為運行在內核態。

雖然用戶態下和內核態下工作的程序有很多差別,但最重要的差別就在於特權級的不同,即權力的不同。運行在用戶態下的程序不能直接訪問操作系統內核數據結構和程序,比如上面例子中的testfork()就不能直接調用sys_fork(),因為前者是工作在用戶態,屬於用戶態程序,而sys_fork()是工作在內核態,屬於內核態程序。

當我們在系統中執行一個程序時,大部分時間是運行在用戶態下的,在其需要操作系統幫助完成某些它沒有權力和能力完成的工作時就會切換到內核態,比如testfork()最初運行在用戶態進程下,當它調用fork()最終觸發sys_fork()的執行時,就切換到了內核態。

2. 用戶態和內核態的轉換

1)用戶態切換到內核態的3種方式

a. 系統調用

   這是用戶態進程主動要求切換到內核態的一種方式,用戶態進程通過系統調用申請使用操作系統提供的服務程序完成工作,比如前例中fork()實際上就是執行了一個創建新進程的系統調用。而系統調用的機制其核心還是使用了操作系統為用戶特別開放的一個中斷來實現,例如Linux的int 80h中斷。

b. 異常

    當CPU在執行運行在用戶態下的程序時,發生了某些事先不可知的異常,這時會觸發由當前運行進程切換到處理此異常的內核相關程序中,也就轉到了內核態,比如缺頁異常。

c. 外圍設備的中斷

    當外圍設備完成用戶請求的操作后,會向CPU發出相應的中斷信號,這時CPU會暫停執行下一條即將要執行的指令轉而去執行與中斷信號對應的處理程序,如果先前執行的指令是用戶態下的程序,那么這個轉換的過程自然也就發生了由用戶態到內核態的切換。比如硬盤讀寫操作完成,系統會切換到硬盤讀寫的中斷處理程序中執行后續操作等。

這3種方式是系統在運行時由用戶態轉到內核態的最主要方式,其中系統調用可以認為是用戶進程主動發起的,異常和外圍設備中斷則是被動的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM