一篇解析context_switch进程切换(针对ARM体系架构)

news2024/11/20 3:35:10

一. 概述

在最近初学ebpf时,使用到了挂载点finish_task_switch统计内核线程的运行时间,遂进入内核源码对其进行学习分析。

finish_task_switchcontext_switch被调用,其功能是完成进程切换的收尾工作,比如地址空间的清理。而context_switch是进程切换的核心部分,其由两部分组成:

  1. 切换页全局目录到一个新的地址空间(switch_mm)。
  2. 切换内核态堆栈及硬件上下文(switch_to)。

context_switch的代码如下:

static __always_inline struct rq *context_switch(struct rq *rq, struct task_struct *prev,
        struct task_struct *next, struct rq_flags *rf)
{
 prepare_task_switch(rq, prev, next);//执行进程切换的准备工作。
 arch_start_context_switch(prev);
 if (!next->mm) {                       // to kernel
  enter_lazy_tlb(prev->active_mm, next);//通知处理器架构不需要切换用户虚拟地址空间,这种加速进程切换计数称为惰性TLB
  next->active_mm = prev->active_mm;//继承前一个进程的内存描述符
  if (prev->mm)                           // from user
   mmgrab(prev->active_mm);//增加前一个进程的活跃地址空间的引用计数,以确保地址空间在进程切换后仍然有效
  else  //from kernel
   prev->active_mm = NULL;
 } else {                                        // to user
  membarrier_switch_mm(rq, prev->active_mm, next->mm);
  switch_mm_irqs_off(prev->active_mm, next->mm, next);//切换地址空间
  if (!prev->mm) {                        // from kernel
   rq->prev_mm = prev->active_mm;
   prev->active_mm = NULL;
  }
 }
 rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
 prepare_lock_switch(rq, next, rf);
 switch_to(prev, next, prev);
 barrier();
 return finish_task_switch(prev);
}

在分析这段源码之前,我们首先需要知道的是,context_switch( )函数建立next的地址空间。在task struct结构体中有这样两个字段:mm和active_mm。进程描述符的active_mm字段指向进程所使用的内存描述符,而mm字段指向进程所拥有的内存描述符。对于一般的进程,这两个字段有相同的地址,但是,内核线程没有它自己的地址空间而且它的 mm字段总是被设置为 NULL

context_switch( )函数实现:如果next是一个内核线程,那么它使用prev所使用的地址空间。

具体实现流程:

  1. prepare_task_switch:执行进程切换的准备工作,包括处理器架构相关的准备工作。
  2. 判断next进程是否有内存描述符(即是否指向内核空间):
  • membarrier_switch_mm:切换地址空间,确保内存访问的一致性。
  • switch_mm_irqs_off:切换内存映射表,并关闭中断来保证原子性。
  • 如果前一个进程是内核线程,则将其活跃地址空间保存在rq->prev_mm中,并将prev->active_mm置为NULL。
  • enter_lazy_tlb:通知处理器架构不需要切换用户虚拟地址空间,使用惰性TLB加速进程切换,懒惰TLB模式是为了减少无用的TLB刷新。
  • next->active_mm = prev->active_mm:将上一个进程的内存描述符赋值给next,继承其用户虚拟地址空间。
  • 调用mmgrab增加mm->mm_count引用计数,以确保地址空间在进程切换后仍然有效。
  • 如果没有内存描述符,说明next是内核线程,需要借用上一个进程的用户虚拟地址空间。此时会执行以下操作:
  • 如果有内存描述符,说明next是用户进程。此时会执行以下操作:
  • switch_to:进行真正的进程切换,将控制权从prev进程切换到next进程,与体系架构相关。
  • finish_task_switch:完成进程切换的收尾工作,并返回前一个进程的task_struct结构,如果prev是内核线程,则调用mmdrop减少内存描述符引用计数。如果引用计数为0,则释放与页表相关的所有描述符和虚拟内存。

二.switch_mm

对于用户进程需要完成用户空间的切换,switch_mm_irqs_off函数完成了这个任务,其是与体系架构相关的。

ARM架构下的进程地址空间切换实际上是通过设置页表基址寄存器TTBR0来完成的。每个进程拥有整个系统的虚拟地址空间,但并不会真正占用所有的物理地址空间,而是需要通过页表转换来完成对物理地址的访问。页表转换的基址信息存放在页表基址寄存器TTBR0中。

TTBR0寄存器指示了进程页全局目录表(PGD)的基址,PGD又指示了页表项(PTE)的基址,而PTE则指示了对应的物理地址(PA)。由于每个进程的PGD是不同的,因此不同进程的虚拟内存对应的物理地址被隔离开来。实质上,进程切换就是完成了对TTBR0寄存器的重新设置,以切换到新进程的地址空间。

进程地址空间ASID

switch_mm_irqs_off函数中最主要的一个函数是check_and_switch_context(),完成与体系结构相关的硬件设置。MMU在做地址翻译时,需要访问物理内存中的页表映射,每一级页表映射都需要访问一次内存,而内存的访问对性能影响很大,因而效率很低。TLB(Translation Lookaside Buffer)是用于缓存MMU地址转换结果的cache,访问cache找到物理地址比访问内存找物理地址快的多,因而TLB加快内存的访问效率

从Linux 内核角度看,地址空间可以划分为内核地址空间和用户空间,TLB 可以分成全局类型和进程独有类型

全局类型的 TLB:内核空间是所有进程共享的空间,因此这部分空间的虚拟地址到物园理地址的翻译是不会变化的,可以理解为全局的。

进程独有类型的 TLB:用户地址空间是每个进程独立的地址空间。从 prev 进程切换到next 进程时,TLB 中缓存的 prev 进程的相关数据对于 next 进程是无用的,因此可以刷新。

为了支持进程独有类型的 TLB,ARM 架构出现了一种硬件解决方案,叫作进程地址空间**ASID(Address Space ID)**,通过使每个表项包含一个ASID,TLB 可以识别哪些 TLB 项是属于某个进程的。

ASID标识了每个TLB entry所属的进程,这样可以保证不同进程之间的TLB entry不会互相干扰,因而避免了切换进程时将TLB刷新的问题。所以ASID作用避免了进程切换时TLB的频繁刷新。

  资料直通车:Linux内核源码技术学习路线+视频教程内核源码

学习直通车:Linuxc/c++高级开发【直播公开课】

零声白金VIP体验卡:零声白金VIP体验卡(含基础架构/高性能存储/golang/QT/音视频/Linux内核)

三.switch_to

switch_to函数完成了内核空间及寄存器的切换,switch_to调用到__switch_to,其代码如下:

#define switch_to(prev,next,last)    
do {         
 __complete_pending_tlbi();     
 if (IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || is_smp()) 
  __this_cpu_write(__entry_task, next);   
 last = __switch_to(prev,task_thread_info(prev), task_thread_info(next)); 
} while (0)

__switch_to汇编实现如下,三个参数分别为:

**r0:移出进程prev的task_struct、r1:移出进程prev的thread_info、r2:**移入进程next的thread_info

ENTRY(__switch_to)
 UNWIND(.fnstart	)
 UNWIND(.cantunwind	)
	add	ip, r1, #TI_CPU_SAVE
 ARM(	stmia	ip!, {r4 - sl, fp, sp, lr} )	@ Store most regs on stack
 THUMB(	stmia	ip!, {r4 - sl, fp}	   )	@ Store most regs on stack
 THUMB(	str	sp, [ip], #4		   )
 THUMB(	str	lr, [ip], #4		   )
	ldr	r4, [r2, #TI_TP_VALUE]
	ldr	r5, [r2, #TI_TP_VALUE + 4]
#ifdef CONFIG_CPU_USE_DOMAINS
	mrc	p15, 0, r6, c3, c0, 0		@ Get domain register
	str	r6, [r1, #TI_CPU_DOMAIN]	@ Save old domain register
	ldr	r6, [r2, #TI_CPU_DOMAIN]
#endif
	switch_tls r1, r4, r5, r3, r7
#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP) && \
    !defined(CONFIG_STACKPROTECTOR_PER_TASK)
	ldr	r8, =__stack_chk_guard
	.if (TSK_STACK_CANARY > IMM12_MASK)
	add	r9, r2, #TSK_STACK_CANARY & ~IMM12_MASK
	ldr	r9, [r9, #TSK_STACK_CANARY & IMM12_MASK]
	.else
	ldr	r9, [r2, #TSK_STACK_CANARY & IMM12_MASK]
	.endif
#endif
	mov	r7, r2				@ Preserve 'next'
#ifdef CONFIG_CPU_USE_DOMAINS
	mcr	p15, 0, r6, c3, c0, 0		@ Set domain register
#endif
	mov	r5, r0
	add	r4, r2, #TI_CPU_SAVE
	ldr	r0, =thread_notify_head
	mov	r1, #THREAD_NOTIFY_SWITCH
	bl	atomic_notifier_call_chain
#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP) && \
    !defined(CONFIG_STACKPROTECTOR_PER_TASK)
	str	r9, [r8]
#endif
	mov	r0, r5
#if !defined(CONFIG_THUMB2_KERNEL) && !defined(CONFIG_VMAP_STACK)
	set_current r7, r8
	ldmia	r4, {r4 - sl, fp, sp, pc}	@ Load all regs saved previously
#else
	mov	r1, r7
	ldmia	r4, {r4 - sl, fp, ip, lr}	@ Load all regs saved previously
#ifdef CONFIG_VMAP_STACK
	ldr	r2, [ip]
#endif
	set_current r1, r2
	mov	sp, ip
	ret	lr
#endif
 UNWIND(.fnend		)
ENDPROC(__switch_to)

我们分析几句关键性的语句:

add ip, r1, #TI_CPU_SAVE

这句话将IP寄存器赋值为r1+ TI_CPU_SAVE,r1即刚刚传入的参数prev->thread_info,TI_CPU_SAVE是cpu_context成员在thread_info中的偏移,接下来要将当前的寄存器值保存在这里。

我们来看看cpu_context是什么,它描述了一个进程切换时,CPU所需要保存的寄存器,也称为硬件上下文,ARM体系下的cpu_context保存了以下寄存器,将上次next进程保存的cpu_context的值恢复到硬件寄存器中,就完成了进程的切换。

struct cpu_context {
 unsigned long x19;
 unsigned long x20;
 unsigned long x21;
 unsigned long x22;
 unsigned long x23;
 unsigned long x24;
 unsigned long x25;
 unsigned long x26;
 unsigned long x27;
 unsigned long x28;
 unsigned long fp;
 unsigned long sp;
 unsigned long pc;
};

接着往下看。

 ARM( stmia ip!, {r4 - sl, fp, sp, lr} )

这是将r4 - sl, fp, sp, lr寄存器中的内容保存到IP寄存器所指向的内存地址,即prev->thread_info->cpu_context,这相当于保存了prev进程运行时的寄存器上下文

接下来都是在做将寄存器保存到内存,内存地址不断递增,且回写到IP寄存器。

add r4, r2, #TI_CPU_SAVE

这句话实现r4寄存器保存了next->thread_info->cpu_context的地址。

ldmia r4, {r4 - sl, fp, sp, pc}
ldmia r4, {r4 - sl, fp, ip, lr}

这是将next->thread_info->cpu_context的数据加载到r4 - sl, fp, sp, lr,pc寄存器中。next->thread_info->cpu_context->sp存入寄存器,相当于内核栈切换完成,next->thread_info->cpu_context->pc存入寄存器PC,相当于跳转到next进程运行。

4.完成切换

在经历以上步骤后,CPU上执行的进程已经变成了next,由它执行finish_task_switch,完成切换后的清理工作,比如当之前的 mm 不再被引用时,将其释放掉,如果上一个进程的状态为 DEAD,需要释放掉上一个进程的相关资源,同时还会打开在 schedule 前期禁止的中断。

5.进程切换调用图示

小结

作为一个学习eBPF技术的初学者,我觉得要想深入了解操作系统的性能数据捕获机制,是必须要深入研究内核源码的。因为只有了解挂载点的执行时机,才能更好地理解eBPF在内核中的运行机制。

我发现将学习eBPF技术与深入研究内核知识相结合是非常明智的选择。通过这样的方式不仅可以掌握eBPF的技术细节,还可以深入了解操作系统的内部工作原理。这次学习让我获得了丰富的经验和知识,同时也加深了我对操作系统内部工作原理的认识。

原文作者:张新谊

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1294340.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

理解自我效能感:你的内在动力来源

1. 自我效能感:开启个人潜能的心理动力 想象一下,面对生活的挑战和机遇时,是什么内在力量驱使你去采取行动,或者让你犹豫不决?这种力量,与我们的心理状态紧密相关,其中一个关键因素就是我们的自…

【AIGC】prompt工程从入门到精通

注:本文示例默认“文心大模型3.5”演示,表示为>或w>(wenxin),有时为了对比也用百川2.0展示b>(baichuan) 有时候为了模拟错误输出,会用到m>(mock)表示(因为用的大模型都会…

uView框架的安装与Git管理

参考链接:Http请求 | uView - 多平台快速开发的UI框架 - uni-app UI框架 安装 打开我们项目的cmd进行下载: yarn add uview-ui 首先我们要确定,未下载前的文件目录以及下载后,是多了个文件目录node_modules 下载完成之后我们就…

MTU TCP-MSS(转载)

MTU MTU 最大传输单元(Maximum Transmission Unit,MTU)用来通知对方所能接受数据服务单元的最大尺寸,说明发送方能够接受的有效载荷大小。 是包或帧的最大长度,一般以字节记。如果MTU过大,在碰到路由器时…

汽车电子之深力科推荐安森美车规级芯片

车规级芯片 在汽车制造业,就可靠性要求而言,车规级芯片无疑是要高于商业级和工业级。 而车规级芯片:顾名思义,就是应用到汽车中的芯片,它不同于日常生活中的消费品和工业品,该类芯片对可靠性要求较高&…

vue2+datav可视化数据大屏(3)

接上一节所说,当我们将接口封装完了后,我们需要给大屏进行内容填充啦 1,新建组件 📓 我们在ser-views文件夹下新建9个vue组件,如下图所示,我给编号为1到9 📓在组件里写入内容我是第一块...一次类推&#x…

100. 相同的树(Java)

目录 解法: 官方解法: 方法一:深度优先搜索 复杂度分析 时间复杂度: 空间复杂度: 方法二:广度优先搜索 复杂度分析 时间复杂度: 空间复杂度: 给你两棵二叉树的根节点 p 和…

学习IO的第五天

作业 &#xff1a;使用两个线程完成文件的拷贝写入&#xff0c;分线程1写入前半段&#xff0c;分线程2写入后半段&#xff0c;主线程用来回收资源 #include <head.h>void *sork(void *arg); void *sork2(void *arg);int file_copy(int start,int len) //拷贝的函数 {i…

Vulnerability: File Upload(Medium)--MYSQL注入

选择难度&#xff1a; 1.打开DVWA&#xff0c;并登录账户 2.选择模式&#xff0c;这里我们选择 文件上载的中级模式&#xff08;Medium&#xff09; 准备工作 1.在vsc里面写个一句话木马 2.下载BurpSuiteCommunit软件&#xff1a;百度搜索“burp suite官网” 下载地址www…

Xilinx FPGA——ISE时序约束“建立时间不满足”问题解决记录

一、现象 最近使用赛灵思的FPGA设计项目时&#xff0c;出现时序约束失效问题。 点进去发现如下&#xff1a; 一个始终约束没有生效&#xff0c;有多处报错。 二、原因 出现这个问题的原因是&#xff0c;建立时间不满足。 时序违例的主要原因是建立时间和保持时间不满足要求&a…

智能化与数字化:开展企业合规工作的新价值与方法

在现代商业环境中&#xff0c;企业合规成为了一项至关重要的任务。随着法规和监管标准的增加以及对企业道德和责任的更高要求&#xff0c;开展合规工作不仅有助于保护企业的声誉和利益&#xff0c;还能提升企业的竞争力和可持续发展。本文将探讨通过智能化和数字化手段开展合规…

碳化硅MOS管在三相逆变器上的应用-REASUNOS瑞森半导体

一、前言 三相逆变是指转换出的交流电压为三相&#xff0c;即AC380V&#xff0c;三相电是由三个频率相同、振幅相等、相位依次互差120度的交流电势组成。 三相逆变器的定义是将直流电能转换为交流电能的转换器&#xff0c;其基本原理就是SPWM&#xff0c;硬件架构为四个功率模…

9.关于Java的程序设计-基于Springboot的家政平台管理系统设计与实现

摘要 随着社会的进步和生活水平的提高&#xff0c;家政服务作为一种重要的生活服务方式逐渐受到人们的关注。本研究基于Spring Boot框架&#xff0c;设计并实现了一种家政平台管理系统&#xff0c;旨在提供一个便捷高效的家政服务管理解决方案。系统涵盖了用户注册登录、家政服…

超静音的两相步进电机国产驱动芯片GC6609,为什么可替代TMC2208/2209/trinamic的数据分析

GC6609 是一款超静音的两相步进电机驱动芯片&#xff0c;内置最大 256 细分的步进驱动模式&#xff0c; 超静音&#xff0c;低振动。芯片可以工作在 4~36V 的宽工作电压范围内&#xff0c;平均工作电流可以达到 2A&#xff0c;峰值电流 4A。内置自动增益控制环路&#xff08;AG…

《算法竞赛进阶指南》------图论篇

文章目录 0x01 Telephone Lines POJ - 36620x02 P1073 [NOIP2009 提高组] 最优贸易0x03 道路和航线 BZOJ22000x04 Sorting It All Out POJ - 1094 topo0x05 Sightseeing trip POJ - 1734 最小环问题0x06 Cow Relays POJ - 3613 S到E经过k条边的最短路0x07 走廊泼水节 &#xff…

美颜SDK算法是什么?美肤、滤镜与实时处理技术讲解

美颜SDK的出现&#xff0c;为开发者提供了一种方便、高效的方式&#xff0c;使其能够轻松地将先进的美颜算法集成到各种应用中。本文将深入探讨美颜SDK算法的本质&#xff0c;以及其在美肤、滤镜与实时处理等方面的技术讲解。 一、美颜SDK算法简介 美颜SDK算法是一套通过计算…

网络攻击(一)--安全渗透简介

1. 安全渗透概述 目标 了解渗透测试的基本概念了解渗透测试从业人员的注意事项 1.1. 写在前面的话 在了解渗透测试之前&#xff0c;我们先看看&#xff0c;信息安全相关的法律是怎么样的 中华人民共和国网络安全法 《中华人民共和国网络安全法》由全国人民代表大会常务委员会…

SpringBoot+线程池实现高频调用http接口并多线程解析json数据

场景 SpringbootFastJson实现解析第三方http接口json数据为实体类(时间格式化转换、字段包含中文)&#xff1a; SpringbootFastJson实现解析第三方http接口json数据为实体类(时间格式化转换、字段包含中文)-CSDN博客 Java中ExecutorService线程池的使用(Runnable和Callable多…

【conda】利用Conda创建虚拟环境,Pytorch各版本安装教程(Ubuntu)

TOC conda 系列&#xff1a; 1. conda指令教程 2. 利用Conda创建虚拟环境&#xff0c;安装Pytorch各版本教程(Ubuntu) 1. 利用Conda创建虚拟环境 nolonolo:~/sun/SplaTAM$ conda create -n splatam python3.10查看结果&#xff1a; (splatam) nolonolo:~/sun/SplaTAM$ cond…

应用现代化加速企业数字化转型

目录 一、数字化转型的必要性 二、应用现代化的推动力 数字化时代&#xff0c;企业正面临着前所未有的挑战和机遇。为了保持竞争力&#xff0c;许多企业正在寻求数字化转型&#xff0c;以提升运营效率、优化客户体验、创新商业模式。本文将探讨如何通过应用现代化加速企业数字化…