arch/x86/kernel/head_32.S的336行,进入分页后的内核代码段,执行lss stack_start,%esp指令,立即为进程0建立内核态堆栈。stack_start定义在657行:
657 ENTRY(stack_start)
658 .long init_thread_union+THREAD_SIZE
659 .long __BOOT_DS
我们看到内核态堆栈由init_thread_union表示,其在include/linux/sched.h中被定义成一个全局变量:
extern union thread_union init_thread_union;
union thread_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
#define THREAD_SIZE (PAGE_SIZE << THREAD_ORDER)
由于PAGE_SIZE是4096,THREAD_ORDER在32位x86体系中是1,所以THREAD_SIZE的值为8k。所以这个thread_union的大小也为8k。关于内核栈的详细内容请参考博客“进程相关的数据结构” .
那么,对于一个进程,最重要的是什么呢?两个东西,一个是thread_info,另一个就是著名的task_struct。这个0号进程的thread_info,我们已经看到了,是在init_thread_union中。那么这个task_struct又在哪儿呢?看到arch/x86/kernel/init_task.c:
23 union thread_union init_thread_union __init_task_data =
24 { INIT_THREAD_INFO(init_task) };
扎眼一看,很奇怪,怎么多了个__init_task_data?别紧张,其实,它只是一个宏,不是什么变了,来自include/linux/init_task.h的186行:
#define __init_task_data __attribute__((__section__(".data.init_task")))
那么,上面的内容就展开成了:
union thread_union init_thread_union __attribute__((__section__(".data.init_task"))) =
{ INIT_THREAD_INFO(init_task) };
这是一条赋值语句,对联合体init_thread_union赋初始值。晕了吧,c语言的功底是多么重要啊,通过内核的学习,你也学会了如何对联合体赋初值并且把数据放进指定的数据段.data.init_task了吧。
继续,具体到如何赋值呢?先讲讲init_task这个全局变量,定义在arch/x86/kernel/init_task.c的第31行:
struct task_struct init_task = INIT_TASK(init_task);
这不,0号进程的task_struct出现了,就是init_task。在对init_thread_union赋初值的时候,同时也通过调用INIT_TASK宏对init_task赋初值:
110#define INIT_TASK(tsk) / 111{ / 112 .state = 0, / 113 .stack = &init_thread_info, / 114 .usage = ATOMIC_INIT(2), / 115 .flags = PF_KTHREAD, / 116 .lock_depth = -1, / 117 .prio = MAX_PRIO-20, / 118 .static_prio = MAX_PRIO-20, / 119 .normal_prio = MAX_PRIO-20, / 120 .policy = SCHED_NORMAL, / 121 .cpus_allowed = CPU_MASK_ALL, / 122 .mm = NULL, / 123 .active_mm = &init_mm, / 124 .se = { / 125 .group_node = LIST_HEAD_INIT(tsk.se.group_node), / 126 }, / 127 .rt = { / 128 .run_list = LIST_HEAD_INIT(tsk.rt.run_list), / 129 .time_slice = HZ, / 130 .nr_cpus_allowed = NR_CPUS, / 131 }, / 132 .tasks = LIST_HEAD_INIT(tsk.tasks), / 133 .pushable_tasks = PLIST_NODE_INIT(tsk.pushable_tasks, MAX_PRIO), / 134 .ptraced = LIST_HEAD_INIT(tsk.ptraced), / 135 .ptrace_entry = LIST_HEAD_INIT(tsk.ptrace_entry), / 136 .real_parent = &tsk, / 137 .parent = &tsk, / 138 .children = LIST_HEAD_INIT(tsk.children), / 139 .sibling = LIST_HEAD_INIT(tsk.sibling), / 140 .group_leader = &tsk, / 141 .real_cred = &init_cred, / 142 .cred = &init_cred, / 143 .cred_guard_mutex = / 144 __MUTEX_INITIALIZER(tsk.cred_guard_mutex), / 145 .comm = "swapper", / 146 .thread = INIT_THREAD, / 147 .fs = &init_fs, / 148 .files = &init_files, / 149 .signal = &init_signals, / 150 .sighand = &init_sighand, / 151 .nsproxy = &init_nsproxy, / 152 .pending = { / 153 .list = LIST_HEAD_INIT(tsk.pending.list), / 154 .signal = {{0}}}, / 155 .blocked = {{0}}, / 156 .alloc_lock = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock), / 157 .journal_info = NULL, / 158 .cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), / 159 .fs_excl = ATOMIC_INIT(0), / 160 .pi_lock = __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock), / 161 .timer_slack_ns = 50000, /* 50 usec default slack */ / 162 .pids = { / 163 [PIDTYPE_PID] = INIT_PID_LINK(PIDTYPE_PID), / 164 [PIDTYPE_PGID] = INIT_PID_LINK(PIDTYPE_PGID), / 165 [PIDTYPE_SID] = INIT_PID_LINK(PIDTYPE_SID), / 166 }, / 167 .dirties = INIT_PROP_LOCAL_SINGLE(dirties), / 168 INIT_IDS / 169 INIT_PERF_EVENTS(tsk) / 170 INIT_TRACE_IRQFLAGS / 171 INIT_LOCKDEP / 172 INIT_FTRACE_GRAPH / 173 INIT_TRACE_RECURSION / 174 INIT_TASK_RCU_PREEMPT(tsk) / 175} |
至于task_struct的详细介绍,请查看博客“进程相关的数据结构”,这里只挑几个重要的来说说。0号进程的stack就是刚才init_thread_info的地址;parent是他自己;thread是INIT_THREAD。还有一些是常量,如init_fs。等我们用到了再回过来看它。
现在,0号进程task_struct有了,下面就该来讲讲INIT_THREAD_INFO宏了,在arch/x86/include/asm/thread_info.h:
46#define INIT_THREAD_INFO(tsk) / 47{ / 48 .task = &tsk, / 49 .exec_domain = &default_exec_domain, / 50 .flags = 0, / 51 .cpu = 0, / 52 .preempt_count = INIT_PREEMPT_COUNT, / 53 .addr_limit = KERNEL_DS, / 54 .restart_block = { / 55 .fn = do_no_restart_syscall, / 56 }, / 57} |
执行完这个宏以后,init_thread_union就被初始化成以上内容了,至此,0号进程的task_struct和thread_info就初始化完毕了。注意,这个初始化不是在head_32.S中初始化的。大家也看到了,上面的内容都是在编译的时候作为一个数据全局数据被初始化的,并且把他们放在.data.init_task数据段中。head_32.S中只是将esp指针指向这个已经被初始化了的init_thread_union以上8k作为栈顶,并向下移动(回忆一下c程序的特点,并参考“进程相关的数据结构”中那个图)。