1、前言
最近在學習linux核心方面的知識,經常會看到使用者空間與核心空間及程式上下文與中斷上下文。看著很熟悉,半天又說不出到底是怎麼回事,有什麼區別。看書過程經常被感覺欺騙,似懂非懂的感覺,很是不爽,今天好好結合書和網上的資料總結一下,加深理解。
2、使用者空間與核心空間
我們知道現在作業系統都是採用虛擬儲存器,那麼對32位作業系統而言,它的定址空間(虛擬儲存空間)為4G(2的32次方)。操心繫統的核心是核心,獨立於普通的應用程式,可以訪問受保護的記憶體空間,也有訪問底層硬體裝置的所有許可權。為了保證使用者程式不能直接操作核心,保證核心的安全,操心繫統將虛擬空間劃分為兩部分,一部分為核心空間,一部分為使用者空間。針對linux作業系統而言,將最高的1G位元組(從虛擬地址0xC0000000到0xFFFFFFFF),供核心使用,稱為核心空間,而將較低的3G位元組(從虛擬地址0x00000000到0xBFFFFFFF),供各個程式使用,稱為使用者空間。每個程式可以透過系統呼叫進入核心,因此,Linux核心由系統內的所有程式共享。於是,從具體程式的角度來看,每個程式可以擁有4G位元組的虛擬空間。空間分配如下圖所示:
有了使用者空間和核心空間,整個linux內部結構可以分為三部分,從最底層到最上層依次是:硬體-->核心空間-->使用者空間。如下圖所示:
需要注意的細節問題:
(1) 核心空間中存放的是核心程式碼和資料,而程式的使用者空間中存放的是使用者程式的程式碼和資料。不管是核心空間還是使用者空間,它們都處於虛擬空間中。
(2) Linux使用兩級保護機制:0級供核心使用,3級供使用者程式使用。
核心態與使用者態:
(1)當一個任務(程式)執行系統呼叫而陷入核心程式碼中執行時,稱程式處於核心執行態(核心態)。此時處理器處於特權級最高的(0級)核心程式碼中執行。當程式處於核心態時,執行的核心程式碼會使用當前程式的核心棧。每個程式都有自己的核心棧。
(2)當程式在執行使用者自己的程式碼時,則稱其處於使用者執行態(使用者態)。此時處理器在特權級最低的(3級)使用者程式碼中執行。當正在執行使用者程式而突然被中斷程式中斷時,此時使用者程式也可以象徵性地稱為處於程式的核心態。因為中斷處理程式將使用當前程式的核心棧。
參考資料:
http://blog.csdn.net/f22jay/article/details/7925531
http://blog.csdn.net/zhangskd/article/details/6956638
http://blog.chinaunix.net/uid-26838492-id-3162146.html
3、程式上下文與中斷上下文
我在看《linux核心設計與實現》這本書的第三章程式管理時候,看到程式上下文。書中說當一個程式執行了系統呼叫或者觸發某個異常(軟中斷),此時就會陷入核心空間,核心此時代表程式執行,並處於程式上下文中。看後還是沒有弄清楚,什麼是程式上下文,如何上google上面狂搜一把,總結如下:
程式在執行過程中通常有使用者態和核心態兩種狀態,CPU對處於核心態根據上下文環境進一步細分,因此有了下面三種狀態:
(1)核心態,執行於程式上下文,核心代表程式執行於核心空間。
(2)核心態,執行於中斷上下文,核心代表硬體執行於核心空間。
(3)使用者態,執行於使用者空間。
上下文context: 上下文簡單說來就是一個環境。
使用者空間的應用程式,透過系統呼叫,進入核心空間。這個時候使用者空間的程式要傳遞 很多變數、引數的值給核心,核心態執行的時候也要儲存使用者程式的一些寄存 器值、變數等。所謂的“程式上下文”,可以看作是使用者程式傳遞給核心的這些引數以及核心要儲存的那一整套的變數和暫存器值和當時的環境等。
相對於程式而言,就是程式執行時的環境。具體來說就是各個變數和資料,包括所有的暫存器變數、程式開啟的檔案、記憶體資訊等。一個程式的上下文可以分為三個部分:使用者級上下文、暫存器上下文以及系統級上下文。
(1)使用者級上下文: 正文、資料、使用者堆疊以及共享儲存區;
(2)暫存器上下文: 通用暫存器、程式暫存器(IP)、處理器狀態暫存器(EFLAGS)、棧指標(ESP);
(3)系統級上下文: 程式控制塊task_struct、記憶體管理資訊(mm_struct、vm_area_struct、pgd、pte)、核心棧。
當發生程式排程時,進行程式切換就是上下文切換(context switch).作業系統必須對上面提到的全部資訊進行切換,新排程的程式才能執行。而系統呼叫進行的模式切換(mode switch)。模式切換與程式切換比較起來,容易很多,而且節省時間,因為模式切換最主要的任務只是切換程式暫存器上下文的切換。
硬體透過觸發訊號,導致核心呼叫中斷處理程式,進入核心空間。這個過程中,硬體的 一些變數和引數也要傳遞給核心,核心透過這些引數進行中斷處理。所謂的“ 中斷上下文”,其實也可以看作就是硬體傳遞過來的這些引數和核心需要儲存的一些其他環境(主要是當前被打斷執行的程式環境)。中斷時,核心不代表任何程式執行,它一般只訪問系統空間,而不會訪問程式空間,核心在中斷上下文中執行時一般不會阻塞。
摘錄Linux註釋的內容如下:
Process Context
-------------------------------------------
One of the most important parts of a process is the executing program code. This code is read in from an executable file and executed within the program's address space. Normal program execution occurs in user-space. When a program executes a system call or triggers an exception, it enters kernel-space. At this point, the kernel is said to be "executing on behalf of the process" and is in process context. When in process context, the current macro is valid[7]. Upon exiting the kernel, the process resumes execution in user-space, unless a higher-priority process has become runnable in the interim(過渡期), in which case the scheduler is invoked to select the higher priority process.
Other than process context there is interrupt context, In interrupt context, the system is not running on behalf of a process, but is executing an interrupt handler. There is no process tied to interrupt handlers and consequently no process context.
System calls and exception handlers are well-defined interfaces into the kernel. A process can begin executing in kernel-space only through one of these interfaces -- all access to the kernel is through these interfaces.
-------------------------------------------
Interrupt Context
-------------------------------------------
When executing an interrupt handler or bottom half, the kernel is in interrupt context. Recall that process context is the mode of operation the kernel is in while it is executing on behalf of a process -- for example, executing a system call or running a kernel thread. In process context, the current macro points to the associated task. Furthermore, because a process is coupled to the kernel in process context(因為程式是以程式上文的形式連線到核心中的), process context can sleep or otherwise invoke the scheduler.
Interrupt context, on the other hand, is not associated with a process. The current macro is not relevant (although it points to the interrupted process). Without a backing process(由於沒有程式的背景),interrupt context cannot sleep -- how would it ever reschedule?(否則怎麼再對它重新排程?) Therefore, you cannot call certain functions from interrupt context. If a function sleeps, you cannot use it from your interrupt handler -- this limits the functions that one can call from an interrupt handler.(這是對什麼樣的函式可以在中斷處理程式中使用的限制)
Interrupt context is time critical because the interrupt handler interrupts other code. Code should be quick and simple. Busy looping is discouraged. This is a very important point; always keep in mind that your interrupt handler has interrupted other code (possibly even another interrupt handler on a different line!). Because of this asynchronous nature, it is imperative(必須) that all interrupt handlers be as quick and as simple as possible. As much as possible, work should be pushed out from the interrupt handler and performed in a bottom half, which runs at a more convenient time.
The setup of an interrupt handler's stacks is a configuration option. Historically, interrupt handlers did not receive(擁有) their own stacks. Instead, they would share the stack of the process that they interrupted[1]. The kernel stack is two pages in size; typically, that is 8KB on 32-bit architectures and 16KB on 64-bit architectures. Because in this setup interrupt handlers share the stack, they must be exceptionally frugal(必須非常節省) with what data they allocate there. Of course, the kernel stack is limited to begin with, so all kernel code should be cautious.
A process is always running. When nothing else is schedulable, the idle task runs.
-------------------------------------------
LINUX完全註釋中的一段話:
當一個程式在執行時,CPU的所有暫存器中的值、程式的狀態以及堆疊中的內容被稱為該程式的上下文。當核心需要切換到另一個程式時,它需要儲存當前程式的所有狀態,即儲存當前程式的上下文,以便在再次執行該程式時,能夠必得到切換時的狀態執行下去。在LINUX中,當前程式上下文均儲存在程式的任務資料結構中。在發生中斷時,核心就在被中斷程式的上下文中,在核心態下執行中斷服務例程。但同時會保留所有需要用到的資源,以便中繼服務結束時能恢復被中斷程式的執行。
參考資料:
http://www.cnblogs.com/hustcat/articles/1505618.html