3.2.5 宙之CPU的时分复用

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

本文主要是介绍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上去执行的呢?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

定位cpu占用过高的线程和对应的方法

如何定位cpu占用过高的线程和对应的方法? 主要是通过线程id找到对应的方法。 1 查询某个用户cpu占用最高的进程号 top -u 用户名 2 查询这个进程中占用cpu最高的线程号 top –p 进程号-H    3 查询到进程id后把进程相关的代码打印到jstack文件 jstack -l pid > jstack.txt 4 在jstack文件中通过16进制的线程id搜索到

CPU亲和性设置 代码示例 sched_setaffinity sched_getaffinity

视频教程在这: cpu亲和性设置,NCCL,sched_setaffinity sched_getaffinity,CPU_ZERO、SET、ISSET、linux_哔哩哔哩_bilibili 一、CPU亲和性简介 CPU亲和性(CPU Affinity)设置是操作系统中一个重要的性能优化手段,它允许程序或进程被绑定到特定的CPU核心上运行。这样做的好处包括减少缓存未命中、降低线程迁移(co

ubuntu16.04 caffe(github源码cpu)+python3.5+opencv3.4.5安装编译

https://www.cnblogs.com/hanjianjian90/p/10604926.html

Ubuntu 标题栏实时显示网速CPU内存

1.用 wget 下载 indicator-sysmonitor,终端执行命令: $ wget -c https://launchpad.net/indicator-sysmonitor/trunk/4.0/+download/indicator-sysmonitor_0.4.3_all.deb2.安装依赖: sudo apt-get install python python-psu

木马导致inetinfo.exe进程占100% CPU的解决方法

电脑进程inetinfo.exe主要用于支持微软Windows IIS网络服务的除错。正常情况下,inetinfo.exe 是 IIS admin Service 或 world wide web publishing service 。这个程序对你系统的正常运行是非常重要的。inetinfo.exe进程属于系统组件,请不要试图停止和删除!   但是如果inetinfo.exe占用CPU 10

cisvc.exe进程占用大量内存和CPU的解决方法

推荐:用电脑尽情K歌、练歌,一展歌喉。打造自己的MTV音乐专辑,分享给所有网友   我的电脑现在反映非常的慢,打开任务管理器发现cisvc.exe这个进程占用了大量的内存和CPU,才导致变慢的。   上网查询之后得知,这是微软Windows 操作系统自带的一个程序,CIDAEMON.EXE和CISVC.EXE都不是木马程序,用来监测CIDAEMON.EXE内存信息的,防止可用内存太低

CPU大小端字节序的检测

机器的字节序有两种,即大端字节序和小端字节序。 大端字节序:在内存中,低地址存放数据的 高位,高地址存放数据的 低位 小端字节序:在内存中,低地址存放数据的 低位,高地址存放数据的 高位 如例:定义数据  a = 0x01020304 小端方式:01 02 03 04 大端方式:04 03 02 01 那么如何判断呢,方式如下--> 一、 指

CPU 虚拟化技术概述

&nbsp; CPU 虚拟化 CPU虚拟化基础知识&nbsp; CPU虚拟化是一种技术,它允许多个操作系统在同一个物理CPU上运行。这种技术可以提高计算机的性能和安全性。 CPU 虚拟化是系统虚拟化技术中最核心的部分,因为 CPU 是计算机中最核心的组件,直接控制着整个系统的运行。同时内存访问(内存虚拟化)与 I/O 操作(I/O 虚拟化)也都直接依赖于 CPU,因此 CPU 虚拟化