本文轉載自:http://www.cnblogs.com/Anker/p/3269106.html
1、前言
2、使用者空間與核心空間

有了使用者空間和核心空間,整個linux内部結構可以分為三部分,從最底層到最上層依次是:硬體-->核心空間-->使用者空間。如下圖所示:
需要注意的細節問題:
(1) 核心空間中存放的是核心代碼和資料,而程序的使用者空間中存放的是使用者程式的代碼和資料。不管是核心空間還是使用者空間,它們都處于虛拟空間中。
(2) Linux使用兩級保護機制:0級供核心使用,3級供使用者程式使用。
核心态與使用者态:
(1)當一個任務(程序)執行系統調用而陷入核心代碼中執行時,稱程序處于核心運作态(核心态)。此時處理器處于特權級最高的(0級)核心代碼中執行。當程序處于核心态時,執行的核心代碼會使用目前程序的核心棧。每個程序都有自己的核心棧。
(2)當程序在執行使用者自己的代碼時,則稱其處于使用者運作态(使用者态)。此時處理器在特權級最低的(3級)使用者代碼中運作。當正在執行使用者程式而突然被中斷程式中斷時,此時使用者程式也可以象征性地稱為處于程序的核心态。因為中斷處理程式将使用目前程序的核心棧。
核心态與使用者态的切換:
當程序運作在使用者态,此時無論發生中斷、異常、系統調用(本質是中斷),都會通過異常向量表進入系統狀态(核心态),執行完中斷服務程式時又恢複原狀,傳回到使用者态。主要步驟:
[1] 從目前程序的描述符中提取其核心棧的ss0及esp0資訊。
[2] 使用ss0和esp0指向的核心棧将目前程序的cs,eip,eflags,ss,esp資訊儲存起來,這個過程也完成了由使用者棧到核心棧的切換過程,同時儲存了被暫停執行的程式的下一條指令。
[3] 将先前由中斷向量檢索得到的中斷處理程式的cs,eip資訊裝入相應的寄存器,開始執行中斷處理程式,這時就轉到了核心态的程式執行了。
其實寫過linux核心驅動程式的同學應該就知道,實作一個字元裝置驅動,在write方法和read方法中,核心态和使用者态之間的橋梁就是copy_to_user和copy_form_user這兩個接口,在高執行級别下,代碼可以執行特權指令,通路任意的實體位址,這種CPU執行級别就對應着核心态,而在相應的低級别執行狀态下,代碼的掌控範圍會受到限制。隻能在對應級别允許的範圍内活動。其實正好說明了這個問題,程式員或者軟體應用工程師在編寫應用程式去控制裝置驅動的時候,首先肯定是會打開相應的檔案描述符,然後對相應的檔案描述符進行讀寫,ioctl,lseek之類的操作。當在應用層編寫程式即是屬于使用者态,在應用程式不能通路任意的硬體實體位址,是以當使用者需要讀取檔案描述符的内的内容時,就需要調用read,當使用者需要寫資料進檔案描述符時,就需要用write,在使用者态調用這兩個接口,進而就會進行系統調用,産生相應的系統調用号,然後核心會根據系統調用号找到相應的驅動程式,此時系統就處在核心态中,在驅動程式中,首先進行驅動程式初始化,然後注冊,産生驅動程式最重要主裝置号和次裝置号。初始化的過程中由于對相應的read方法和wirte方法進行初始化,故使用者态執行read操作,就會進而使CPU産生異常,然後進入核心态找到相應的read,write當然也是一樣的。
參考資料:
<a href="http://blog.csdn.net/f22jay/article/details/7925531" target="_blank">http://blog.csdn.net/f22jay/article/details/7925531</a>
<a href="http://blog.csdn.net/zhangskd/article/details/6956638" target="_blank">http://blog.csdn.net/zhangskd/article/details/6956638</a>
<a href="http://blog.chinaunix.net/uid-26838492-id-3162146.html" target="_blank">http://blog.chinaunix.net/uid-26838492-id-3162146.html</a>
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完全注釋中的一段話:
<a href="http://www.cnblogs.com/hustcat/articles/1505618.html" target="_blank">http://www.cnblogs.com/hustcat/articles/1505618.html</a>
<a href="http://mprc.pku.edu.cn/~zhengyansong/blog/?p=199" target="_blank">http://mprc.pku.edu.cn/~zhengyansong/blog/?p=199</a>
<a href="http://blog.chinaunix.net/uid-26980210-id-3235544.html" target="_blank">http://blog.chinaunix.net/uid-26980210-id-3235544.html</a>
本文轉自張昺華-sky部落格園部落格,原文連結:http://www.cnblogs.com/sky-heaven/p/7074458.html,如需轉載請自行聯系原作者