1.進程的堆棧
核心在建立進程的時候,在建立task_struct的同事,會為進程建立相應的堆棧。每個進程會有兩個棧,一個使用者棧,存在於使用者空間,一個核心棧,存在於核心空間。當進程在使用者空間運行時,cpu堆棧指標寄存器裡面的內容是使用者堆棧地址,使用使用者棧;當進程在核心空間時,cpu堆棧指標寄存器裡面的內容是核心棧空間地址,使用核心棧。
2.進程使用者棧和核心棧的切換
當進程因為中斷或者系統調用而陷入核心態之行時,進程所使用的堆棧也要從使用者棧轉到核心棧。
進程陷入核心態後,先把使用者態堆棧的地址儲存在核心棧之中,然後設定堆棧指標寄存器的內容為核心棧的地址,這樣就完成了使用者棧向核心棧的轉換;當進程從核心態恢複到使用者態之行時,在核心態之行的最後將儲存在核心棧裡面的使用者棧的地址恢複到堆棧指標寄存器即可。這樣就實現了核心棧和使用者棧的互轉。
那麼,我們知道從核心轉到使用者態時使用者棧的地址是在陷入核心的時候儲存在核心棧裡面的,但是在陷入核心的時候,我們是如何知道核心棧的地址的呢?
關鍵在進程從使用者態轉到核心態的時候,進程的核心棧總是空的。這是因為,當進程在使用者態運行時,使用的是使用者棧,當進程陷入到核心態時,核心棧儲存進程在核心態啟動並執行相關信心,但是一旦進程返回到使用者態後,核心棧中儲存的資訊無效,會全部恢複,因此每次進程從使用者態陷入核心的時候得到的核心棧都是空的。所以在進程陷入核心的時候,直接把核心棧的棧頂地址給堆棧指標寄存器就可以了。
3.核心棧的實現
核心棧在kernel-2.4和kernel-2.6裡面的實現方式是不一樣的。
在kernel-2.4核心裡面,核心棧的實現是:
Union task_union {
Struct task_struct task;
Unsigned long stack[INIT_STACK_SIZE/sizeof(long)];
};
其中,INIT_STACK_SIZE的大小隻能是8K。
核心為每個進程分配task_struct結構體的時候,實際上分配兩個連續的物理頁面,底部用作task_struct結構體,結構上面的用作堆棧。使用current()宏能夠訪問當前正在啟動並執行進程描述符。
注意:這個時候task_struct結構是在核心棧裡面的,核心棧的實際能用大小大概有7K。
核心棧在kernel-2.6裡面的實現是(kernel-2.6.32):
Union thread_union {
Struct thread_info thread_info;
Unsigned long stack[THREAD_SIZE/sizeof(long)];
};
其中THREAD_SIZE的大小可以是4K,也可以是8K,thread_info佔52bytes。
當核心棧為8K時,Thread_info在這塊記憶體的起始地址,核心棧從堆棧末端向下增長。所以此時,kernel-2.6中的current宏是需要更改的。要通過thread_info結構體中的task_struct域來獲得於thread_info相關聯的task。更詳細的參考相應的current宏的實現。
struct thread_info {
struct task_struct *task;
struct exec_domain *exec_domain;
__u32 flags;
__u32 status;
__u32 cpu;
… ..
};
注意:此時的task_struct結構體已經不在核心棧空間裡面了。