参考博文:
Linux进程内核栈与thread_info结构详解--Linux进程的管理与调度(九)_OSKernelLAB(gatieme)-CSDN博客_thread_info浅析Linux下的task_struct结构体_lc_29503203的博客-CSDN博客_task_struct
ThreadInfo结构和内核栈的两种关系_半月旋空-CSDN博客
一. 内核进程栈是什么
1. 什么是进程
进程是程序的一个执行实例,进程是一种动态描述,但需要注意的是并不是所有进程都在运行(进程因策略与调度需求,会处于各种状态)
2. 怎样描述一个进程
PCB(进程控制块),操作系统管理进程,实则是将进程的有效信息提取出来然后通过管理这些信息来管理进程,而所有的进程信息被存放在一个叫做进程控制块的数据结构中(可以理解为进程属性的集合),这也就是即将要介绍的进程控制块(PCB).
linux内核是支持不同体系的,但是不同的体系结构可能进程需要存储的信息不尽相同,这就需要我们实现一种通用的方式,我们将体系结构相关的部分和无关的部分进行分离
struct task_struct存储通用的PCB信息,位于./include/linux/sched.h中
struct thread_info存储体系架构有关的PCB信息,以ARM架构为例:
arm32: 位于./arch/arm/include/asm/thread_info.h中
arm64: 位于./arch/arm64/include/asm/thread_info.h中
3. 为什么需要内核栈
进程在内核态运行时需要自己的堆栈信息, 因此linux内核为每个进程都提供了一个内核栈kernel stack
内核态的进程访问处于内核数据段的栈,这个栈不同于用户态的进程所用的栈。
用户态进程所用的栈,是在进程线性地址空间中;而内核栈是当进程从用户空间进入内核空间时,特权级发生变化,需要切换堆栈,那么内核空间中使用的就是这个内核栈。
再说明一下: 在每一个进程的生命周期中,经常会通过系统调用(SYSCALL)陷入内核。在执行系统调用陷入内核之后,这些内核代码所使用的栈并不是原先用户空间中的栈,而是一个内核空间的栈,这个称作进程的“内核栈”
进程内核栈的定义在: ./include/linux/sched.h中
联合体与下文要讲的两种架构有关
THREAD_SIZE表示内核栈的大小
通常,在arm32上面THREAD_SIZE为8K==4096<<1
在arm64上面THREAD_SIZE为16K==1<<14
./arch/arm64/include/asm/memory.h
二. thread info与task struct
第一节有讲到,struct thread_info与struct task_struct都用来描述进程的属性,都是PCB的一部分
区别在于struct thread_info与体系架构相关,而struct task_struct是通用描述
arm32:
arm64:
三.Linux内核进程栈的两种架构
参考上一节struct task_struct代码可以看到有一个配置宏CONFIG_THREAD_INFO_IN_TASK
-
当这个宏没有使能的时候: thread_info在内核栈中(arm32)
内核栈,thread_info与task_struct三者的关系如下图:
那如何获取一个进程的task_struct结构呢? 我们获得当前内核栈的sp指针的地址,然后根据THREAD_SIZE对齐就可以获取thread_info结构的基地址,然后从thread_info.task就可以获取当前task_struct结构的地址了,具体实现在
./include/asm-generic/current.h
./arch/arm/include/asm/thread_info.h
-
当这个宏使能的时候: thread_info内嵌于task_struct内部(arm64)
内核栈,thread_info与task_struct三者的关系如下图:
当thread_info和内核栈是这种关系的时候,内核如何获取当前进程的task_struct结构呢?
ARM64增加了很多通用寄存器,使用寄存器传递进程描述符显然效率更高。因此在ARM64架构里,current宏不再通过栈偏移量得到进程描述符地址,而是借用专门的寄存器sp_el0,在进程切换时暂存进程描述符地址
实现在:
./arch/arm64/include/asm/current.h
./include/linux/thread_info.h