本来本节是要学习内核启动的第一个进程的建立,也就是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结构和内核栈的两种关系相关推荐

  1. 深度解析| CRM的三重境界、两种关系、两个问题

    在一次CRM客户关系管理系统的培训课上,笔者让学员做了一个游戏:写出以C.R.M打头的与CRM系统相关的英文单词,大家兴致勃勃地写了上百个,由此可见这个"三字经"的魅力.从中挑选了 ...

  2. 结构体变量的两种初始化方式

    结构体变量的初始化 定义时初始化 定义之后初始化 结构体白能量的初始化方式有两种,可以在定义的时候或定义之后对结构体变量进行初始化. 定义时初始化 一般情况下我们都是在定义的时候对它进行初始化,因为那 ...

  3. html分为哪两种,css伪类分为哪几种

    css伪类分为UI伪类和结构化伪类两种.UI伪类分为链接伪类.focus伪类和target伪类.结构化伪类分为[:first-child].[:last-child]和[:nth-child]. CS ...

  4. 《textanalytics》课程简单总结(1):两种word relations——Paradigmatic vs. Syntagmatic...

    coursera上的公开课<https://www.coursera.org/course/textanalytics>系列,讲的很不错哦. 1.两种关系:Paradigmatic vs. ...

  5. 食物链 (利用并查集的两种解决方法)

    食物链是并查集中的一道经典题, 第一次看<挑战程序设计竞赛>上懵懵懂懂, 最近又看见了发现还是一脸懵逼. 首先题目如下 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形 ...

  6. 面向对象类和类之间的几种关系

    一.继承关系      继承指的是一个类(称为子类.子接口)继承另外的一个类(称为父类.父接口)的功能,并可以增加它自己的新功能的能力.在Java中继承关系通过关键字extends明确标识,在设计时一 ...

  7. 类与类之间的几种关系

    http://www.cnblogs.com/liuling/archive/2013/05/03/classrelation.html 类与类之间的几种关系 一.继承关系      继承指的是一个类 ...

  8. mysql索引4种结构_mysql索引:四种类型,两种方法

    1. 普通索引:最基本的索引,它没有任何限制,用于加速查询. 2. 唯一索引unique:索引列的值必须唯一,但允许有空值.如果是组合索引,则列值的组合必须唯一. 3. 主键索引: 是一种特殊的唯一索 ...

  9. 数据结构图之一(基本概念,存储结构,两种遍历)

    [1]图的基本概念 (1)图是由顶点集合以及顶点间的关系集合组成的一种数据结构. Graph = (V,E)  V是顶点的又穷非空集合:E是顶点之间关系的有穷集合,也叫边集合. (2)有向图:顶点对& ...

  10. 最重要的两种思维:逻辑思维与结构化思维

    日常工作中,除了时间和精力管理 \ 目标.计划与执行2种方法,还会再谈思维方式.老话说:让你与众不同的不是努力,而是思维方式. 思维方式是个很大的话题,在一些营销号上会讲"掌握50个思维模型 ...

最新文章

  1. push代码到github时,每次都要输入用户名和密码的问题
  2. 一文介绍机器学习中的三种特征选择方法
  3. leetcode475. 供暖器(二分查找)
  4. 刷题刷题 ——网易CPP
  5. java第十版基础篇答案第九章_《Java语言程序设计》(基础篇原书第10版)第九章复习题答案...
  6. 2021-2025年中国超细氢氧化铝行业市场供需与战略研究报告
  7. 自动驾驶即将迎来下一个飞跃?
  8. python日程提醒小程序_小程序自动化测试之python版
  9. 帮你躲坑:pip install tensorflow 报错怎么办,import tensorflow 报错怎么办?
  10. git pull checkout后文件权限变更
  11. android scroll 中 scroll Bar 修改
  12. 华为p10点击六下android,要被口水喷到死机的华为P10 你用的怎么样
  13. 计算机文件图标怎么一致大小,电脑图标大小设置的几种方法
  14. 搜狗拼音输入法 V9.1.0.2589 最新去广告精简优化版
  15. 中科银谷:企业工商数据工商查询API接口应该如何选择?
  16. 本科计算机考研考英语,2016考研必需知道的10件事
  17. 笔记本显示服务器未能登录,win10系统出现User profile service服务未能登录的解决方案...
  18. 科普|2022数字调度机进网认证测试项,CTA认证、入网认证、调度机
  19. 近年来机器人主流抓取估计方法总结
  20. Adobe Audition 2022 v22.2 U2B macOS 专业的音频工作站

热门文章

  1. Centos7.0升级python 2.x到3.x
  2. BZOJ 1935: [Shoi2007]Tree 园丁的烦恼
  3. 2015暑假多校联合---Friends(dfs枚举)
  4. Java操作zip压缩和解压缩文件工具类
  5. C++类与对象实验(六)
  6. 成员函数指针有多态的效果吗?
  7. Java之Windows下环境搭建配置
  8. poj——3349 哈希加模拟
  9. python基础之列表生成式和生成器
  10. [C#]System.Timers.Timer(2)