linux内核栈的地址,Linux内核栈与thread_info

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

内核栈与thread_info

Linux内核在x86平台下,PAGE_SIZE为4KB(32位和64位相同),THREAD_SIZE为8KB(32位)或者16KB(64位)。THREAD_SIZE表示了整个内核栈的大小,栈可以向下增长(栈低在高地址)或者向上增长(栈低在低地址),后面的分析都是基于向下增长的方式。如图中所示,整个内核栈可分为四个部分,从低地址开始依次为:thread_info结构体

溢出标志

从溢出标志开始到kernel_stack之间的实际可用栈内存空间,kernel_stack为percpu变量,通过它可间接找到内核栈的起始地址

从kernel_stack到栈底的长度为KERNEL_STACK_OFFSET的保留空间

66ae87fe0f0faefd714b317b19627443.png

内核引入thread_info的一大原因是方便通过它直接找到进(线)程的task_struct指针,x86 平台的thread_info结构体定义在arch/x86/include/asm/thread_info.h。// Linux 3.19.3 x86平台的thread_info

struct thread_info {

struct task_struct*task;/* main task structure */

struct exec_domain*exec_domain;/* execution domain */

__u32flags;/* low level flags */

__u32status;/* thread synchronous flags */

__u32cpu;/* current CPU */

intsaved_preempt_count;

mm_segment_taddr_limit;

void __user*sysenter_return;

unsigned intsig_on_uaccess_error:1;

unsigned intuaccess_err:1;/* uaccess failed */

};

由于thread_info结构体恰好位于内核栈的低地址开始处,所以只要知道内核栈的起始地址,就可以通过其得到thread_info,进而得到task_struct,后面会分析这个过程的实现。

current宏

current宏在Linux 内核中负责获取当前cpu上的task_struct,通常是借助thread_info和内核栈实现,这种方式的主要逻辑是:定义kernel_stack percpu变量,通过kernel_stack + KERNEL_STACK_OFFSET - THREAD_SIZE即可获得内核栈的起始地址

内核栈的起始地址即是thread_info的起始地址,所以通过thread_info->task即可获得task_struct指针

在每次上下文切换时,更新kernel_stack percpu变量// 以Linux 3.19.3 x86的源码为例

// 注意,arch/xxx/include/asm/...下的头文件是对应

// include/asm-generic/...下的平台相关实现,若arch

// 目录下没有相同的头文件,则使用asm-generic目录下

// 的,arch目录下的头文件可能直接include asm-generic

// 目录下的相关头文件。

// include/asm-generic/current.h

#define get_current() (current_thread_info()->task)

#define current get_current()

// arch/x86/include/asm/thread_info.h

DECLARE_PER_CPU(unsigned long, kernel_stack);

static inline struct thread_info *current_thread_info(void)

{

struct thread_info *ti;

ti = (void *)(this_cpu_read_stable(kernel_stack) +

KERNEL_STACK_OFFSET - THREAD_SIZE);

return ti;

}

// arch/x86/kernel/process_64.c

__visible __notrace_funcgraph struct task_struct *

__switch_to(struct task_struct *prev_p, struct task_struct *next_p)

{

/* ... */

this_cpu_write(kernel_stack,

(unsigned long)task_stack_page(next_p) +

THREAD_SIZE - KERNEL_STACK_OFFSET);

/* ... */

}

但是Linux内核引入percpu变量之后,逐渐通过percpu变量来实现current宏,并且从Linux 4.1开始,x86移除了kernel_stack,并逐渐开始简化thread_info结构体,直到Linux 4.9彻底不再通过thread_info获取task_struct指针,而是直接通过current_struct percpu变量存放task_struct的指针,具体可参见此commit// 以Linux 3.19.3 x86的源码为例

// arch/x86/include/asm/current.h

DECLARE_PER_CPU(struct task_struct *, current_task);

static __always_inline struct task_struct *get_current(void)

{

return this_cpu_read_stable(current_task);

}

#define current get_current()

// arch/x86/kernel/process_64.c

__visible __notrace_funcgraph struct task_struct *

__switch_to(struct task_struct *prev_p, struct task_struct *next_p)

{

/* ... */

this_cpu_write(current_task, next_p);

/* ... */

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值