Linux 内核 进程 0 (一)
所有进程的祖先叫做进程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 获得 进程的内核栈指针呢?(在高版本的内核中)接下来会有一篇文章用一个实例来介绍!