所有进程的祖先叫做进程0,idle 进程(swapper进程),它是在Linux 的初始化阶段从无到有创建的一个内核线程。这个祖先进程使用静态分配的数据结构(所有其他进程的数据结构都是动态分配的)。

下面着重介绍一下进程0 内核栈的初始化过程!

在head.S 中:

`</p>

lss stack_start,%esp
       ……
ENTRY(stack_start)
         .long init_thread_union+THREAD_SIZE
         .long __BOOT_DS
` 

表示加载内核栈,ss= __BOOT_DS,表示内核数据段,而 esp 指向从init_thread_union 开始的大小为THREAD_SIZE的内存处。

(init_thread_union 在C 代码中为初始化的全局变量,在汇编中被当作为label 也就是地址)

那么init_thread_union 是什么呢?

在 init_task.c中:

`</p>

union thread_union init_thread_union
      __attribute__((__section__(".data.init_task"))) =
               { INIT_THREAD_INFO(init_task) };`

表示在内核的 .data.init_task 段定义一个共用体变量,并为其初始化。按照THREAD_SIZE 对齐!!

thread_union 为:

`</p>

union thread_union {
      struct thread_info thread_info;
      unsigned long stack[THREAD_SIZE/sizeof(long)];
};`

THREAD_SIZE 定义为 4096

在.data.init_task 段中开辟了4096 字节大小空间用于内核栈。低地址处存放thread_info 结构,剩下的作为内核栈。

让我们想象一下现在内存中栈的情况:

此时 esp 位于下一个4KB 内存单元的开始处,此时栈为空。(push 是先减减esp哦!)因为进程0的数据结构由静态初始化,此时就可以获得当前进程(进程 0) current 进程描述符指针。

`</p>

#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) : "0" (~(THREAD_SIZE - 1)));
         return ti;
}`

因为此时内核栈并不为空,所以很容易就可以算出 thread_info 结构的起始地址,因为在一页内存中,esp 与 FFFFF000 相与 就可算出其基地址既thread_info 结构地址。thread_info 结构的第一项就为其指向tss_struct 的指针,所以很容易就可得到current!

PS:众所周知,内核中的每个进程都有它自己的内核栈(alloc_thread_info()),那么怎么根据 task_struct 获得 进程的内核栈指针呢?(在高版本的内核中)接下来会有一篇文章用一个实例来介绍!