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

相关文章

SysMain服务可以关吗? 解决SysMain服务导致的高CPU使用率问题

《SysMain服务可以关吗?解决SysMain服务导致的高CPU使用率问题》SysMain服务是超级预读取,该服务会记录您打开应用程序的模式,并预先将它们加载到内存中以节省时间,但它可能占用大量... 在使用电脑的过程中,CPU使用率居高不下是许多用户都遇到过的问题,其中名为SysMain的服务往往是罪魁

MySQL中优化CPU使用的详细指南

《MySQL中优化CPU使用的详细指南》优化MySQL的CPU使用可以显著提高数据库的性能和响应时间,本文为大家整理了一些优化CPU使用的方法,大家可以根据需要进行选择... 目录一、优化查询和索引1.1 优化查询语句1.2 创建和优化索引1.3 避免全表扫描二、调整mysql配置参数2.1 调整线程数2.

Linux进程CPU绑定优化与实践过程

《Linux进程CPU绑定优化与实践过程》Linux支持进程绑定至特定CPU核心,通过sched_setaffinity系统调用和taskset工具实现,优化缓存效率与上下文切换,提升多核计算性能,适... 目录1. 多核处理器及并行计算概念1.1 多核处理器架构概述1.2 并行计算的含义及重要性1.3 并

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

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