提出问题
内核常用的 current 指针是怎么实现的?
内核栈
要理解 current 指针如何实现的,就必须了解 内核栈。
什么是进程的内核栈?
在内核态(比如应用进程执行系统调用)时,进程运行需要自己的堆栈信息(不是原用户空间中的栈),而是使用内核空间中的栈,这个栈就是进程的内核栈
进程的内核栈在计算机中是如何描述的?
linux中进程使用task_struct数据结构描述,其中有一个stack指针:
struct task_struct
{
// ...
void *stack; // 指向内核栈的指针
// ...
};
task_struct数据结构中的stack成员指向 thread_union 结构(Linux内核通过thread_union联合体来表示进程的内核栈)
// 内核线程栈 可以通过内核配置成4K 或者 8K,此处是8K。
// 在X86体系结构上,32位的内核栈为8K,64位的为16K。
#define THREAD_SIZE 8192
union thread_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
thread_info 是记录部分进程信息的结构体,其中包括了进程上下文信息:
struct thread_info {
struct pcb_struct pcb; /* palcode state */
// 这里很重要!! task指针 指向的是所创建的进程的struct task_struct
struct task_struct *task; /* main task structure */
unsigned int flags; /* low level flags */
unsigned int ieee_state; /* see fpu.h */
struct exec_domain *exec_domain; /* execution domain */
mm_segment_t addr_limit; /* thread address space */
unsigned cpu; /* current CPU */
int preempt_count; /* 0 => preemptable, <0 => BUG */
int bpt_nsaved;
unsigned long bpt_addr[2]; /* breakpoint handling */
unsigned int bpt_insn[2];
struct restart_block restart_block;
};
也就是说:
- thread_info 中有个指针指向了 task_struct
- task_struct 中有个指针指向了 thread_info
如下图所示:
从用户态刚切换到内核态以后,进程的内核栈总是空的。因此,esp寄存器指向这个栈的顶端,一旦数据写入堆栈,esp的值就递减。
整个8K的空间,顶部供进程堆栈使用,最下部为thread_info。从用户态切换到内核态时,进程的内核栈还是空的,所以sp寄存器指向栈顶,一旦有数据写入,sp的值就会递减,内核栈按需扩展,理论上最大可扩展到 【8192- sizeof(thread_info) 】大小,考虑到函数的现场保护,往往不会有这么大的栈空间。内核在代表进程执行时和所有的中断服务程序执行时,共享8K的内核栈。
current 原理
现在就可以说明 current 原理了。
在内核中,可以通过current宏来获得当前执行进程的task_struct指针。现在来简要分析以下:
最原始的定义如下:
#define get_current() (current_thread_info()->task)
#define current get_current()
可以看出,current调用了 current_thread_info 函数(此函数的内核路径为: arch/arm/include/asm/thread_info.h,内核版本为2.6.32.65)
static inline struct thread_info *current_thread_info(void)
{
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}
当内核线程执行到此处时,其SP堆栈指针指向调用进程所对应的内核线程的栈顶。通过 sp & ~(THREAD_SIZE-1)向上对齐,达到栈底部。如下图所示:
将结果强制类型转换为thread_info类型,此类型中有一个成员为task_struct,它就是 当前正在运行进程的 task_struct指针。
至此,你应该明白了 current 是怎么来的了吧?
为什么要有 thread_info
thread_info 保存了进程描述符中频繁访问和需要快速访问的字段,内核依赖于该数据结构来获得当前进程的描述符(为了获取当前CPU上运行进程的task_struct结构,内核提供了current宏。
内核还需要存储每个进程的PCB信息, linux内核是支持不同体系的的, 但是不同的体系结构可能进程需要存储的信息不尽相同,这就需要我们实现一种通用的方式, 我们将体系结构相关的部分和无关的部门进行分离,用一种通用的方式来描述进程, 这就是struct task_struct, 而thread_info
就保存了特定体系结构的汇编代码段需要访问的那部分进程的数据,我们在thread_info中嵌入指向task_struct的指针, 则我们可以很方便的通过thread_info来查找task_struct.
参考:
https://www.cnblogs.com/cherishui/p/4255690.html
https://blog.youkuaiyun.com/tiankong_/article/details/75647488