3.2.5 宙之CPU的时分复用

2024-08-23 15:28
文章标签 cpu 3.2 时分复用 宙之

本文主要是介绍3.2.5 宙之CPU的时分复用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客
3.2.5.1 __primary_switched开始构建0号进程

        宙者,古往今来,时间为宙。盘古为了开天辟地,必须分开空间和时间。在时间维度,要对CPU的运行时间进行切分,即基于进程调度的时分复用。

        内核要支持多任务运行,本质上就是多个任务分享CPU的时间,轮流上阵占用CPU。接下来内核从__primary_switched开始构建0号进程。为了抓住核心,对其进行精简:     

__primary_switched:				adrp	x4, init_thread_union		add	sp, x4, #THREAD_SIZE		adr_l	x5, init_task		msr	sp_el0, x5	// Save thread_info	adr_l	x8, vectors	// load VBAR_EL0 with virtual	msr	vbar_el1, x8	// vector table address	isb			stp	xzr, x30, [sp, #-16]!		mov	x29, sp		add	sp, sp, #16		mov	x29, #0		mov	x30, #0		b	start_kernel		
ENDPROC(__primary_switched)				
3.2.5.2 0号进程内核栈的构建

        第2~3行,是内核从头开始后,第一次进行栈的初始化,让SP_EL1指向内核栈的栈底(内核栈高地址)。0号进程是内核进程,没有用户态,它只有内核栈。它的内核栈是静态定义的变量init_thread_union,类型是union thread_union。

include/linux/sched/task.h:
extern union thread_union init_thread_union;include/linux/sched.h:
union thread_union {
#ifndef CONFIG_ARCH_TASK_STRUCT_ON_STACK // ia64架构,可忽略struct task_struct task;
#endif
#ifndef CONFIG_THREAD_INFO_IN_TASK //默认配置为ystruct thread_info thread_info;
#endifunsigned long stack[THREAD_SIZE/sizeof(long)];
};arch/arm64/include/asm/memory.h:
#define THREAD_SIZE		(UL(1) << THREAD_SHIFT)

        CONFIG_ARCH_TASK_STRUCT_ON_STACK仅针对ia64架构,可以忽略。

        CONFIG_THREAD_INFO_IN_TASK在ARM64默认配置为yes,所以thread_info会放置在task_struct结构中。

        最终,union thread_union里面最需要关注的是unsigned long stack[]数组,这个数组的大小为THREAD_SIZE字节,通常为4KB或16KB字节,取决于THREAD_SHIFT。

        总之第2~3行执行完毕,内核栈如下图:

        第11行,进行压栈操作,在栈中FP的位置压入0,在栈中LR中的位置压入X30。第12行,X29(FP寄存器)指向SP_EL1。在第12~14行之间精简了代码,实际上可能会调用kasan_early_init和kaslr_early_init函数。

第14~16行,让SP_EL1重新指向栈底(高地址)。如下就是跳转到start_kernel前内核栈的样子:

3.2.5.3 0号进程的task_struct结构

        第4~5行代码,让SP_EL0指向0号进程的task_struct结构体变量init_task。它定义在init/init_task.c。为了方便分析,只留下感兴趣的初始化部分:

struct task_struct init_task
= {
#ifdef CONFIG_THREAD_INFO_IN_TASK.thread_info	= INIT_THREAD_INFO(init_task),.stack_refcount	= ATOMIC_INIT(1),
#endif
…….stack		= init_stack,
…….mm		= NULL,.active_mm	= &init_mm,
…….comm		= INIT_TASK_COMM,
……
};
EXPORT_SYMBOL(init_task);

        首先看.comm字段,它代表0号进程的名字,叫做“swapper”。”comm”应该是command name的缩写。

        include/linux/init_task.h: #define INIT_TASK_COMM "swapper"

        其次看.stack字段,它代表了进程的栈。按照理解,它应该指向上一章节分析的init_thread_union,为什么这里是指向init_stack?最终在include/asm-generic/vmlinux.lds.h找到玄机:init_thread_union的链接地址之后,紧接着是符号init_stack,所以init_stack的值刚好是init_thread_union的高地址即0号进程内核栈的栈底。

节选自include/asm-generic/vmlinux.lds.h:

3.2.5.4 0号进程的内存空间

        struct task_struct init_task中的成员.mm和.active_mm怎么理解?        正解在Documentation/vm/active_mm.rst中,好处见memory management - current->mm gives NULL in linux kernel - Stack Overflow。

        在这里,只要知道如下结论即可:内核进程的mm成员总是为NULL,它运行的时候总是使用active_mm。0号进程的active_mm指向init_mm,定义在mm/init-mm.c。这里特别关注成员.pgd,它指向就是内核镜像页表swapper_pg_dir

struct mm_struct init_mm = {.mm_rb		= RB_ROOT,.pgd		= swapper_pg_dir,.mm_users	= ATOMIC_INIT(2),.mm_count	= ATOMIC_INIT(1),.mmap_sem	= __RWSEM_INITIALIZER(init_mm.mmap_sem),.page_table_lock =  __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),.arg_lock	=  __SPIN_LOCK_UNLOCKED(init_mm.arg_lock),.mmlist		= LIST_HEAD_INIT(init_mm.mmlist),.user_ns	= &init_user_ns,.cpu_bitmap	= { [BITS_TO_LONGS(NR_CPUS)] = 0},INIT_MM_CONTEXT(init_mm)
};
3.2.5.5 进程调度的起搏器异常向量表

        中断截止到目前都是关闭的,这里只是加载异常向量表。关于异常的具体分析,详见第二章。

adr_l	x8, vectors			// load VBAR_EL1 with virtual
msr	vbar_el1, x8			// vector table address

        为什么说异常是进程调度的起搏器?

  • 只有异常打开的情况下,时钟中断才能开始运行,时间才开始流动,才能对进程的运行时间进行计量,对进程是否需要切换做出判断并标记。
  • Linux在中断和系统调用返回时,才会真正执行进程切换。

        中断真正开启的时机,是在接下来的start_kernel中。详见下文分解。

点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客

原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!

这篇关于3.2.5 宙之CPU的时分复用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1099762

相关文章

Java进程CPU使用率过高排查步骤详细讲解

《Java进程CPU使用率过高排查步骤详细讲解》:本文主要介绍Java进程CPU使用率过高排查的相关资料,针对Java进程CPU使用率高的问题,我们可以遵循以下步骤进行排查和优化,文中通过代码介绍... 目录前言一、初步定位问题1.1 确认进程状态1.2 确定Java进程ID1.3 快速生成线程堆栈二、分析

conda安装GPU版pytorch默认却是cpu版本

《conda安装GPU版pytorch默认却是cpu版本》本文主要介绍了遇到Conda安装PyTorchGPU版本却默认安装CPU的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录一、问题描述二、网上解决方案罗列【此节为反面方案罗列!!!】三、发现的根本原因[独家]3.1 p

Linux CPU飙升排查五步法解读

《LinuxCPU飙升排查五步法解读》:本文主要介绍LinuxCPU飙升排查五步法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录排查思路-五步法1. top命令定位应用进程pid2.php top-Hp[pid]定位应用进程对应的线程tid3. printf"%

判断PyTorch是GPU版还是CPU版的方法小结

《判断PyTorch是GPU版还是CPU版的方法小结》PyTorch作为当前最流行的深度学习框架之一,支持在CPU和GPU(NVIDIACUDA)上运行,所以对于深度学习开发者来说,正确识别PyTor... 目录前言为什么需要区分GPU和CPU版本?性能差异硬件要求如何检查PyTorch版本?方法1:使用命

Android如何获取当前CPU频率和占用率

《Android如何获取当前CPU频率和占用率》最近在优化App的性能,需要获取当前CPU视频频率和占用率,所以本文小编就来和大家总结一下如何在Android中获取当前CPU频率和占用率吧... 最近在优化 App 的性能,需要获取当前 CPU视频频率和占用率,通过查询资料,大致思路如下:目前没有标准的

Go使用pprof进行CPU,内存和阻塞情况分析

《Go使用pprof进行CPU,内存和阻塞情况分析》Go语言提供了强大的pprof工具,用于分析CPU、内存、Goroutine阻塞等性能问题,帮助开发者优化程序,提高运行效率,下面我们就来深入了解下... 目录1. pprof 介绍2. 快速上手:启用 pprof3. CPU Profiling:分析 C

MySQL的cpu使用率100%的问题排查流程

《MySQL的cpu使用率100%的问题排查流程》线上mysql服务器经常性出现cpu使用率100%的告警,因此本文整理一下排查该问题的常规流程,文中通过代码示例讲解的非常详细,对大家的学习或工作有一... 目录1. 确认CPU占用来源2. 实时分析mysql活动3. 分析慢查询与执行计划4. 检查索引与表

使用Python检查CPU型号并弹出警告信息

《使用Python检查CPU型号并弹出警告信息》本教程将指导你如何编写一个Python程序,该程序能够在启动时检查计算机的CPU型号,如果检测到CPU型号包含“I3”,则会弹出一个警告窗口,感兴趣的小... 目录教程目标方法一所需库步骤一:安装所需库步骤二:编写python程序步骤三:运行程序注意事项方法二

Java程序到CPU上执行 的步骤

相信很多的小伙伴在最初学习编程的时候会容易产生一个疑惑❓,那就是编写的Java代码究竟是怎么一步一步到CPU上去执行的呢?CPU又是如何执行的呢?今天跟随小编的脚步去化解开这个疑惑❓。 在学习这个过程之前,我们需要先讲解一些与本内容相关的知识点 指令 指令是指导CPU运行的命令,主要由操作码+被操作数组成。 其中操作码用来表示要做什么动作,被操作数是本条指令要操作的数据,可能是内存地址,也

win10不用anaconda安装tensorflow-cpu并导入pycharm

记录一下防止忘了 一、前提:已经安装了python3.6.4,想用tensorflow的包 二、在pycharm中File-Settings-Project Interpreter点“+”号导入很慢,所以直接在cmd中使用 pip install -i https://mirrors.aliyun.com/pypi/simple tensorflow-cpu下载好,默认下载的tensorflow