ThreadInfo结构和内核栈的两种关系
本来本节是要学习内核启动的第一个进程的建立,也就是0号进程,也称idle进程,也称swapper进程。但是在学习第一个进程建立之前需要先学习threadinfo和内核栈的关系。
目前内核存在两种threadinfo和内核的关系,接下来我们通过画图一一举例说明。
ThreadInfo结构在内核栈中
Threadinfo结构存储在内核栈中,这种方式是最经典的。因为task_struct结构从1.0到现在5.0内核此结构一直在增大。如果将此结构放在内核栈中则很浪费内核栈的空间,则在threadinfo结构中有一个task_struct的指针就可以避免。
struct thread_info {unsigned long flags; /* low level flags */mm_segment_t addr_limit; /* address limit */struct task_struct *task; /* main task structure */int preempt_count; /* 0 => preemptable, <0 => bug */int cpu; /* cpu */
};
可以看到thread_info结构中存在一个struct task_struct的指针。
我们接着看下struct task_struct结构体
struct task_struct {volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */void *stack;atomic_t usage;unsigned int flags; /* per process flags, defined below */unsigned int ptrace;#ifdef CONFIG_SMPstruct llist_node wake_entry;int on_cpu;unsigned int wakee_flips;unsigned long wakee_flip_decay_ts;struct task_struct *last_wakee;int wake_cpu;
#endifint on_rq;......
可以看到struct task_struct结构体重有一个stack的结构,此stack指针就是内核栈的指针。
接下来再看看内核stack和thread_info结构的关系
union thread_union {struct thread_info thread_info;unsigned long stack[THREAD_SIZE/sizeof(long)];
};#define THREAD_SIZE 16384
#define THREAD_START_SP (THREAD_SIZE - 16)
内核定义了一个thread_union的联合体,联合体的作用就是thread_info和stack共用一块内存区域。而THREAD_SIZE就是内核栈的大小,ARM64定义THREAD_SIZE的大小为16K
现在我们已经理清了这三个结构体的关系,下面通过一张图来说明下这三者的关系。
那如何获取一个进程的task_struct结构呢? 我们获得当前内核栈的sp指针的地址,然后根据THREAD_SIZE对齐就可以获取thread_info结构的基地址,然后从thread_info.task就可以获取当前task_struct结构的地址了。
current的实现
内核中经常通过current宏来获得当前进程对应的struct task_sturct结构,我们来看下具体的实现。
#define get_current() (current_thread_info()->task)
#define current get_current()/** how to get the current stack pointer from C*/
register unsigned long current_stack_pointer asm ("sp");/** how to get the thread information struct from C*/
static inline struct thread_info *current_thread_info(void) __attribute_const__;static inline struct thread_info *current_thread_info(void)
{return (struct thread_info *)(current_stack_pointer & ~(THREAD_SIZE - 1));
}
可以看出通过SP的地址通过对齐THREAD_SIZE,然后强转为thread_info结构,然后通过thread_info结构中的task就可以获取task_struct结构的值
ThreadInfo在task_struct结构中
上面的一种方式是thread_info结构和内核栈共用一块存储区域,而另一种方式是thread_info结构存储在task_struct结构中。
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK/** For reasons of header soup (see current_thread_info()), this* must be the first element of task_struct.*/struct thread_info thread_info;
#endif/* -1 unrunnable, 0 runnable, >0 stopped: */volatile long state;/** This begins the randomizable portion of task_struct. Only* scheduling-critical items should be added above here.*/randomized_struct_fields_startvoid *stack;atomic_t usage;/* Per task flags (PF_*), defined further below: */unsigned int flags;unsigned int ptrace;
可以看到必须打开CONFIG_THREAD_INFO_IN_TASK这个配置,这时候thread_info就会在task_struct的第一个成员。而task_struct中依然存在void* stack结构
接着看下thread_info结构,如下是ARM64架构定义的thread_info结构
struct thread_info {unsigned long flags; /* low level flags */mm_segment_t addr_limit; /* address limit */
#ifdef CONFIG_ARM64_SW_TTBR0_PANu64 ttbr0; /* saved TTBR0_EL1 */
#endifunion {u64 preempt_count; /* 0 => preemptible, <0 => bug */struct {
#ifdef CONFIG_CPU_BIG_ENDIANu32 need_resched;u32 count;
#elseu32 count;u32 need_resched;
#endif} preempt;};
};
从此结构中则没有struct task_struct的指针了。
接着再来看下内核栈的定义:
union thread_union {
#ifndef CONFIG_THREAD_INFO_IN_TASKstruct thread_info thread_info;
#endifunsigned long stack[THREAD_SIZE/sizeof(long)];
};
当CONFIG_THREAD_INFO_IN_TASK这个配置打开的时候,则thread_union结构中只存在stask成员了。
这时候我们再来看下当thread_info在task_struct结构中时,用一张图描述下。
当thread_info和内核栈是这种关系的时候,内核如何获取当前进程的task_struct结构呢?
还是和上面一样,通过分析current这个宏来分析
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;
}#define current get_current()
可以看到内核通过读取sp_el0的值,然后将此值强转成task_struct结构就可以获得。那sp_el0是什么东西?
sp:堆栈指针寄存器
el0: ARM64架构分为EL0,EL1,EL2,EL3。EL0则就是用户空间,EL1则是内核空间,EL2则是虚拟化,EL3则是secure层。
sp_el0: 则就是用户空间栈指针寄存器。
SP_EL0值存储的是什么?
这个内存其实在后面的进程切换中会涉及到,这里先简单说明了。当进程发生切换时,需要将上一个进程的上下文保存到内核堆栈中,然后去恢复下一个进程的堆栈。
/** Thread switching.*/
__notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev,struct task_struct *next)
{struct task_struct *last;fpsimd_thread_switch(next);tls_thread_switch(next);hw_breakpoint_thread_switch(next);contextidr_thread_switch(next);entry_task_switch(next);uao_thread_switch(next);ptrauth_thread_switch(next);/** Complete any pending TLB or cache maintenance on this CPU in case* the thread migrates to a different CPU.* This full barrier is also required by the membarrier system* call.*/dsb(ish);/* the actual thread switch */last = cpu_switch_to(prev, next);return last;
}
当两个进程发生切换时,最终会调用到这里。然后最终会通过cpu_switch_to函数发生切换,cpu_switch_to函数是用汇编实现的,其中参数传递x0=prev, x1=next
/** Register switch for AArch64. The callee-saved registers need to be saved* and restored. On entry:* x0 = previous task_struct (must be preserved across the switch)* x1 = next task_struct* Previous and next are guaranteed not to be the same.**/
ENTRY(cpu_switch_to)mov x10, #THREAD_CPU_CONTEXTadd x8, x0, x10mov x9, spstp x19, x20, [x8], #16 // store callee-saved registersstp x21, x22, [x8], #16stp x23, x24, [x8], #16stp x25, x26, [x8], #16stp x27, x28, [x8], #16stp x29, x9, [x8], #16str lr, [x8]add x8, x1, x10ldp x19, x20, [x8], #16 // restore callee-saved registersldp x21, x22, [x8], #16ldp x23, x24, [x8], #16ldp x25, x26, [x8], #16ldp x27, x28, [x8], #16ldp x29, x9, [x8], #16ldr lr, [x8]mov sp, x9msr sp_el0, x1ret
ENDPROC(cpu_switch_to)
这段汇编的意思是将prev进程的x19到x29, x9, lr寄存器都存储在内核堆栈中,然后将next进程的x19-x29, x9, lr寄存器从堆栈中恢复。
我们关心的msr sp_el0, x1。其中x1就是next进程的struct task_struct结构。则sp_el0存储的是当前进程的task_struct结构。
当知道了当前进程的task_struct结构的地址,则thread_info结构的地址也就知道了。
#ifdef CONFIG_THREAD_INFO_IN_TASK
/** For CONFIG_THREAD_INFO_IN_TASK kernels we need <asm/current.h> for the* definition of current, but for !CONFIG_THREAD_INFO_IN_TASK kernels,* including <asm/current.h> can cause a circular dependency on some platforms.*/
#include <asm/current.h>
#define current_thread_info() ((struct thread_info *)current)
#endif
通过将current强制转为struct thread_info结构就可以了。
至此我们已经分析了thread_info和内核栈的两种关系。而ARM64架构使用的是第二种。
ThreadInfo结构和内核栈的两种关系相关推荐
- 深度解析| CRM的三重境界、两种关系、两个问题
在一次CRM客户关系管理系统的培训课上,笔者让学员做了一个游戏:写出以C.R.M打头的与CRM系统相关的英文单词,大家兴致勃勃地写了上百个,由此可见这个"三字经"的魅力.从中挑选了 ...
- 结构体变量的两种初始化方式
结构体变量的初始化 定义时初始化 定义之后初始化 结构体白能量的初始化方式有两种,可以在定义的时候或定义之后对结构体变量进行初始化. 定义时初始化 一般情况下我们都是在定义的时候对它进行初始化,因为那 ...
- html分为哪两种,css伪类分为哪几种
css伪类分为UI伪类和结构化伪类两种.UI伪类分为链接伪类.focus伪类和target伪类.结构化伪类分为[:first-child].[:last-child]和[:nth-child]. CS ...
- 《textanalytics》课程简单总结(1):两种word relations——Paradigmatic vs. Syntagmatic...
coursera上的公开课<https://www.coursera.org/course/textanalytics>系列,讲的很不错哦. 1.两种关系:Paradigmatic vs. ...
- 食物链 (利用并查集的两种解决方法)
食物链是并查集中的一道经典题, 第一次看<挑战程序设计竞赛>上懵懵懂懂, 最近又看见了发现还是一脸懵逼. 首先题目如下 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形 ...
- 面向对象类和类之间的几种关系
一.继承关系 继承指的是一个类(称为子类.子接口)继承另外的一个类(称为父类.父接口)的功能,并可以增加它自己的新功能的能力.在Java中继承关系通过关键字extends明确标识,在设计时一 ...
- 类与类之间的几种关系
http://www.cnblogs.com/liuling/archive/2013/05/03/classrelation.html 类与类之间的几种关系 一.继承关系 继承指的是一个类 ...
- mysql索引4种结构_mysql索引:四种类型,两种方法
1. 普通索引:最基本的索引,它没有任何限制,用于加速查询. 2. 唯一索引unique:索引列的值必须唯一,但允许有空值.如果是组合索引,则列值的组合必须唯一. 3. 主键索引: 是一种特殊的唯一索 ...
- 数据结构图之一(基本概念,存储结构,两种遍历)
[1]图的基本概念 (1)图是由顶点集合以及顶点间的关系集合组成的一种数据结构. Graph = (V,E) V是顶点的又穷非空集合:E是顶点之间关系的有穷集合,也叫边集合. (2)有向图:顶点对& ...
- 最重要的两种思维:逻辑思维与结构化思维
日常工作中,除了时间和精力管理 \ 目标.计划与执行2种方法,还会再谈思维方式.老话说:让你与众不同的不是努力,而是思维方式. 思维方式是个很大的话题,在一些营销号上会讲"掌握50个思维模型 ...
最新文章
- push代码到github时,每次都要输入用户名和密码的问题
- 一文介绍机器学习中的三种特征选择方法
- leetcode475. 供暖器(二分查找)
- 刷题刷题 ——网易CPP
- java第十版基础篇答案第九章_《Java语言程序设计》(基础篇原书第10版)第九章复习题答案...
- 2021-2025年中国超细氢氧化铝行业市场供需与战略研究报告
- 自动驾驶即将迎来下一个飞跃?
- python日程提醒小程序_小程序自动化测试之python版
- 帮你躲坑:pip install tensorflow 报错怎么办,import tensorflow 报错怎么办?
- git pull checkout后文件权限变更
- android scroll 中 scroll Bar 修改
- 华为p10点击六下android,要被口水喷到死机的华为P10 你用的怎么样
- 计算机文件图标怎么一致大小,电脑图标大小设置的几种方法
- 搜狗拼音输入法 V9.1.0.2589 最新去广告精简优化版
- 中科银谷:企业工商数据工商查询API接口应该如何选择?
- 本科计算机考研考英语,2016考研必需知道的10件事
- 笔记本显示服务器未能登录,win10系统出现User profile service服务未能登录的解决方案...
- 科普|2022数字调度机进网认证测试项,CTA认证、入网认证、调度机
- 近年来机器人主流抓取估计方法总结
- Adobe Audition 2022 v22.2 U2B macOS 专业的音频工作站
热门文章
- Centos7.0升级python 2.x到3.x
- BZOJ 1935: [Shoi2007]Tree 园丁的烦恼
- 2015暑假多校联合---Friends(dfs枚举)
- Java操作zip压缩和解压缩文件工具类
- C++类与对象实验(六)
- 成员函数指针有多态的效果吗?
- Java之Windows下环境搭建配置
- poj——3349 哈希加模拟
- python基础之列表生成式和生成器
- [C#]System.Timers.Timer(2)