Linux進程內核棧

在內核2.4中堆棧是這麼定義的:
union task_union {
        struct task_struct task;
        unsigned long stack[INIT_TASK_SIZE/sizeof(long)];
    };
INIT_TASK_SIZE只能是8K


 


內核爲每個進程分配一個task_struct結構時,實際上分配兩個連續的物理頁面(8192字節),如圖所示。底部用作task_struct結構(大小約爲1K字節),結構的上面用作內核堆棧(大小約爲7K字節)。訪問進程自身的task_struct結構,使用宏操作current, 2.4中定義如下:

#define current get_current()
static inline struct task_struct * get_current(void)
{
      struct task_struct *current;
      __asm__("andl %%esp,%0; ":"=r" (current) : "" (~8191UL));
      return current;
}

  ~8191UL
表示最低13位爲0, 其餘位全爲1 %esp指向內核堆棧中,當屏蔽掉%esp的最低13後,就得到這個兩個連續的物理頁面的開頭,而這個開頭正好是task_struct的開始,從而得到了指向task_struct的指針。


在內核2.6中堆棧這麼定義:
union thread_union {
      struct thread_info thread_info;
      unsigned long stack[THREAD_SIZE/sizeof(long)];
};

根據內核的配置,THREAD_SIZE既可以是4K字節(1個頁面)也可以是8K字節(2個頁面)thread_info52個字節長。

下圖是當設爲8KB時候的內核堆棧:Thread_info在這個內存區的開始處,內核堆棧從末端向下增長。進程描述符不是在這個內存區中,而分別通過taskthread_info指針使thread_info與進程描述符互聯。所以獲得當前進程描述符的current定義如下:


#define current get_current()
static inline struct task_struct * get_current(void)
{
      return current_thread_info()->task;
}


static inline struct thread_info *current_thread_info(void)
{
       struct thread_info *ti;
       __asm__("andl %%esp,%0; ":"=r" (ti) : "" (~(THREAD_SIZE - 1)));
       return ti;
}
   
根據THREAD_SIZE大小,分別屏蔽掉內核棧的12-bit LSB(4K)13-bit LSB(8K),從而獲得內核棧的起始位置。

struct thread_info {
      struct task_struct    *task;       /* main task structure */
      struct exec_domain    *exec_domain; /* execution domain */
      unsigned long           flags;       /* low level flags */
      unsigned long           status;       /* thread-synchronous flags */
      ... ..
}

 

fork系統調用中調用dup_task_struct,其執行:

1,  執行alloc_task_struct宏,爲新進程獲取進程描述符,並將描述符放在局部變量tsk中。

2,  執行alloc_thread_info宏以獲取一塊空閒的內存區,用以存放新進程的thread_info結構和內核棧,並將這塊內存區字段的地址放在局部變量ti中(8K 4K, 可配置)。

3,  current進程描述符的內容複製到tsk所指向的task_struct結構中,然後把tsk->thread_info置爲ti

4,  current進程的thread_info描述符的內容複製到ti中,然後把ti->task置爲tsk

5,  返回新進程的描述符指針tsk

  

 

static struct task_struct *dup_task_struct(struct task_struct *orig)
{
    
struct task_struct *tsk;
    
struct thread_info *ti;

    prepare_to_copy
(orig);

    tsk
= alloc_task_struct();
    
if (!tsk)
        
return NULL;

    ti
= alloc_thread_info(tsk);
    
if (!ti) {
        free_task_struct
(tsk);
        
return NULL;
    
}

    
*tsk = *orig;
    tsk
->thread_info = ti;
    setup_thread_stack
(tsk, orig);
    
    …
..
}
# define alloc_task_struct()    kmem_cache_alloc(task_struct_cachep, GFP_KERNEL)

#define alloc_thread_info(tsk) \
    
((struct thread_info *) __get_free_pages(GFP_KERNEL,THREAD_ORDER))
#endif


內核棧空間大小非常有限,故在內核中寫程序時,注意儘量不要定義大的局部變量,儘量不要使用遞歸(導致函數調用棧過大而導致棧溢出),當需要空間時,使用kmalloc在堆中申請。

閱讀(80) | 評論(0) | 轉發(0) |
0

上一篇:模擬linux內核哈希表

下一篇:問題記賬本-1

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