1. 内核栈的分配,即thread_info的分配,是在do_fork->dup_task_struct中分配(默认为2个pages),并赋值给task_struct->stack;
2. 用户栈的分配分两种:
一是pthread create会事先mmap分配好用户栈,传给do_fork->copy_thread:用户栈起始地址会赋值给pt_regs->sp,接着设置内核栈底p->thread.cpu_context.sp = (unsigned long)childregs;
二是fork时,借用了父进程的线程栈,若ret_from_user后应用层调用了exec->do_execuveat_common->exe_binprm->search_binary_handler->load_elf_binary->set_arg_pages,重新分配用户栈,接着调用start_thread配置pt_regs;
3. 内核态(SVC模式)下的寄存器上下文存放位置:task_struct的thread成员,类型为struct thread_struct;
4. 用户态(非异常模式)下的寄存器上下文存放位置:内核栈的底部。可如此获取(task_pt_regs(task_struct)):struct task_struct的stack成员指向struct thread_info,thread_info即为内核栈的起始地址,thread_info+内核栈的固定大小(2 page),即为struct pt_regs的结束地址,pt_regs即为用户态寄存器上下文的存放位置;
5. thread_info的task成员指向task_struct结构体;
6. 进程切换只会发生在内核态,即进程切换只需要考虑内核态的寄存器上下文切换,见schedule->__schedule->switch_to->cpu_switch_to中,将当前的regs保存到current->thread->context,同时恢复nex taskt的regs;
7. 发生系统调用/异常时,需要保存用户态/内核态的寄存器上下文,此时通过kernel_entry,将用户态/内核态的regs保存于异常模式的栈中,当异常完成后,调用kernel_exit,将用户态/内核态的regs恢复;
综上,只要知道了task_struct,即可知道内核栈的起始地址(即thread_info=task_struct->stack),也知道了内核态的寄存器上下文(即task_struct->thread->context);同时,也知道了用户态寄存器上下文(即task_struct->stack+kthread_stack_size-sizeof(pt_regs)),从而知道用户栈的当前地址(即pt_regs->sp)