接前一篇文章:Linux内核进程管理子系统有什么第六十五回 —— 进程主结构详解(61)
本文内容参考:
Linux内核进程管理专题报告_linux rseq-CSDN博客
《趣谈Linux操作系统 核心原理篇:第三部分 进程管理》—— 刘超
《图解Linux内核 基于6.x》 —— 姜亚华 机械工业出版社
https://zhuanlan.zhihu.com/p/296750228
https://blog.csdn.net/xi_xix_i/article/details/142306000
https://blog.csdn.net/longwang155069/article/details/104346778
https://blog.csdn.net/weixin_45030965/article/details/132734258
特此致谢!
进程管理核心结构 —— task_struct
12. current
前几回讲了current在x86以及ARM上的实现,本回来看本部分剩余的一个知识点:通过task_struct定位内核栈stack和通过内核栈定位task_struct。
- 通过task_struct找内核栈stack
如果有一个task_struct的指针在手,就可以找到内核栈。如前文书所讲,根据CONFIG_THREAD_INFO_IN_TASK宏是否定义,分为两种情况(代码都位于include/linux/sched/task_stack.h中)。
1)CONFIG_THREAD_INFO_IN_TASK已定义的情况

#ifdef CONFIG_THREAD_INFO_IN_TASK
/** When accessing the stack of a non-current task that might exit, use* try_get_task_stack() instead. task_stack_page will return a pointer* that could get freed out from under you.*/
static __always_inline void *task_stack_page(const struct task_struct *task)
{return task->stack;
}
……
#endif
2)CONFIG_THREAD_INFO_IN_TASK未定义的情况

#ifdef CONFIG_THREAD_INFO_IN_TASK
……
#elif !defined(__HAVE_THREAD_FUNCTIONS)
#define task_stack_page(task) ((void *)(task)->stack)
……
#endif
这里要提到一个关键的结构体 —— struct pt_regs。
在内核栈的最高地址端,存放的是另一个结构pt_regs,这个结构体保存着进程从应用层进入到内核层时,用户态寄存器的状态。
内核栈是一个非常特殊的结构,如下图所示:

由上图可以看到,在结构 pt_regs上面还有一个内核栈预留空间,这在x86(32位)架构的一个遗留问题,在x86_64架构和arm64架构都没有该内核栈预留空间,如下图所示:

上边已提到,当Linux程序通过系统调用、中断、异常等手段从用户态切换到内核态时,内核态需要保存用户态的寄存器上下文。
在内核栈的最高地址端,存放的是另一个结构pt_regs,这个结构体保存着进程从应用层进入到内核层时,用户态寄存器的状态。
通常内核态会在内核态堆栈的最顶端保留一段空间来存储用户态的寄存器上下文,这段空间的存储格式为pt_regs。
当系统调用从用户态到内核态的时候,首先要做的第一件事情,就是将用户态运行过程中的CPU 上下文保存起来。其实主要就是保存在这个结构的寄存器变量里。这样当从内核系统调用返回的时候,才能让进程在刚才的地方接着运行下去。
当进程用内核态切换回用户态时,就会获取pt_regs结构体中的成员,这样就可以获取当进程用户态运行的寄存器上下文状态了。
struct pt_regs寄存器是体系结构相关的,在Linux内核源码中搜索,可以看到多个结果:



下一回重点讲解x86、x86_64、arm、arm64中的struct pt_regs的相关内容。