current() macro
current在内核中通常以宏的形式存在,其实现方式依赖于具体的架构和内核版本,有些实现是从堆栈中获取当前运行任务的task_struct结构指针,有些则从寄存器中获取。
- current的实现
拿 arm64架构 + linux-5.4版本 为背景举例, current实际上是从sp_el0这个寄存器中读取当前任务的task_struct结构指针,这个宏的具体实现如下:
/* current实际上是get_current()函数 */ #define current get_current() /* get_current()函数实际上是通过mrs指令从sp_el0寄存器取出当前任务的task_struct指针 */ /* * We don't use read_sysreg() as we want the compiler to cache the value where * possible. */ static __always_inline struct task_struct *get_current(void) { unsigned long sp_el0; asm ("mrs %0, sp_el0" : "=r" (sp_el0)); return (struct task_struct *)sp_el0; }
- sp_el0值的来历
这个sp_el0存放当前任务的task_struct,它是怎么来的呢?
内核在发生任务切换动作前会将切换的next任务写入percpu变量__entry_task,这样正在cpu上运行的任务的task_struct保存在percpu变量__entry_task中;
同时,由于内核中会频繁使用当前任务"current",而通过读取percpu变量来获取当前任务还是有一定开销;为此,正在运行的任务(也就是current)从用户态陷入到内核态时,会将这个percpu变量存放到sp_el0寄存器(该寄存器是用户态堆栈指针,在内核态暂时没有使用)中,这样每次获取当前任务时,可直接使用sp_el0来获取。
- 每次任务切换时更新__entry_task为当前任务
__switch_to()-->entry_task_switch(next) static void entry_task_switch(struct task_struct *next) { __this_cpu_write(__entry_task, next); }
- 任务从用户态进入到内核态时将__entry_task取出到sp_el0中
.macro kernel_entry, el, regsize = 64 ...... //取percpu变量__entry_task到tsk ldr_this_cpu tsk, __entry_task, x20 ...... .if el == 0 msr sp_el0, tsk //如果异常发生在用户态,则将tsk取到sp_el0 .endif
对于sp_el0是如何/何时存放当前任务的,实际上就是在当前任务从用户态进入内核态时动态从percpu变量__entry_task获取的;而这个percpu变量__entry_task则是在前一个任务调用__switch_to()切换到当前任务切换运行时设置的。
from:
https://www.cnblogs.com/liuhailong0112/p/14921228.html