進程描述符的處理

轉自:http://edsionte.com/techblog/archives/2198/comment-page-1

對於每一個進程而言,內核爲其單獨分配了一個內存區域,這個區域存儲的是內核棧和該進程所對應的一個小型進程描述符——thread_info結構。

01 struct thread_info {
02     struct task_struct  *task;      /* main task structure */
03     struct exec_domain  *exec_domain;   /* execution domain */
04     unsigned long       flags;      /* low level flags */
05     unsigned long       status;     /* thread-synchronous flags */
06     __u32           cpu;        /* current CPU */
07     __s32           preempt_count; /* 0 => preemptable, <0 => BUG */
08     mm_segment_t        addr_limit;
09     struct restart_block    restart_block;
10     unsigned long           previous_esp;
11     __u8            supervisor_stack[0];
12 };

之所以將thread_info結構稱之爲小型的進程描述符,是因爲在這個結構中並沒有直接包含與進程相關的字段,而是通過task字段指向具體某個進程描述符。通常這塊內存區域的大小是8KB,也就是兩個頁的大小(有時候也使用一個頁來存儲,即4KB)。一個進程的內核棧和thread_info結構之間的邏輯關係如下圖所示:


從上圖可知,內核棧是從該內存區域的頂層向下(從高地址到低地址)增長的,而thread_info結構則是從該區域的開始處向上(從低地址到高地址)增長。內核棧的棧頂地址存儲在esp寄存器中。所以,當進程從用戶態切換到內核態後,esp寄存器指向這個區域的末端。

從代碼的角度來看,內核棧和thread_info結構是被定義在一個聯合體當中的:

1 //定義在linux/include/linux/sched.h中
2 union thread_union {
3         struct thread_info thread_info;
4         unsigned long stack[THREAD_SIZE/sizeof(long)];
5 };

其中,THREAD_SIZE的值取8192時,stack數組的大小爲2048;THREAD_SIZE的值取4096時,stack數組的大小爲1024。現在我們應該思考,爲何要將內核棧和thread_info(其實也就相當於task_struct,只不過使用thread_info結構更節省空間)緊密的放在一起?最主要的原因就是內核可以很容易的通過esp寄存器的值獲得當前正在運行進程的thread_info結構的地址,進而獲得當前進程描述符的地址。

1 //定義在/linux/include/asm-i386/thread_info.h中
2   static inline struct thread_info *current_thread_info(void)
3   {
4           struct thread_info *ti;
5           __asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~(THREAD_SIZE - 1)));
6           return ti;
7   }

這條內聯彙編語句會屏蔽掉esp寄存器中的內核棧頂地址的低13位(或12位,當THREAD_SIZE爲4096時)。此時ti所指的地址就是這片內存區域的起始地址,也就剛好是thread_info結構的地址。但是,thread_info結構的地址並不會對我們直接有用。我們通常可以輕鬆的通過current宏獲得當前進程的task_struct結構,這個宏是如何實現的?

1 //定義在linux/include/asm-i386/current.h中
2    static inline struct task_struct * get_current(void)
3    {
4           return current_thread_info()->task;
5   }
6   #define current get_current()

通過上述源碼可以發現,current宏返回的是thread_info結構task字段。而task正好指向與thread_info結構關聯的那個進程描述符。得到current後,我們就可以獲得當前正在運行進程的描述符中任何一個字段了,比如我們通常所做的:current->pid。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章