linux内核调度问题分析(代码片段)

为了维护世界和平_ 为了维护世界和平_     2023-03-09     511

关键词:

目录

一、调度场景分析

不支持内核抢占的内核

支持内核抢占

二、如何让新进程执行

三、调度的本质


一、调度场景分析

假如内核只有3个线程,线程0创建线程1和线程2.当系统时钟到来时,时钟中断处理函数会检查是否有进程需要调度。当有进程需要调度时,调度器会选择线程1或者线程2。

执行流程:start_kernel运行在线程0里,线程0创建线程1和线程2。函数调用关系start_kernel()->kernel_debug()->do_fork 创建新线程,并把新线程添加到调度器的就绪队列中。线程0创建线程1和线程2后,进入while线程,线程0不会退出,等待被调度出去。

1、产生时钟中断。处理器采用定时器来周期性地执行。调度器利用时钟中断来定时检测当前运行的线程是否需要调度。当需要调度时,设置need_resched标志位

2、当时钟中断返回,根据linux内核是否支持内核抢占来确定是否需要调度:

不支持内核抢占的内核

不会检查是否调度。即使线程0的need_resched标志位置位了,linux内核也不会调度线程1或者线程2。只有发生在用户态的中断返回或者系统调用返回用户空间时,才会检查是否需要调度。

1)发生时钟中断。触发时钟中断时 当前进程有可能在用户态执行,也可能在内核态执行。

如果进程运行在用户态发生了中断,那么会进入异常向量表的el0_irq汇编函数;

如果进程运行在内核态时发生了中断,会进入异常向量表的el1_irq汇编函数中。

进入中断时,CPU会自动关闭中断。

2)在el1_irq汇编函数里,首先会保存中断现场到当前进程的栈中,使用pt_regs数据结构来实现pt_regs栈,保存中断现场。

中断处理程序过程包括切换到linux内核中断栈、硬件中断号的查询、中断服务程序处理等

3)当确定中断源时时钟中断后,scheduler_tick()函数会检查当前进程是否需要调度。如果需要调度,设置当前进程need_resched标志位(TIF_NEED_RESCHED),

4)中断返回。这里需要给中断控制器返回一个中断结束信号

5)在el1_irq汇编函数恢复中断现场。2)的对应操作

在不支持内核抢占的系统里,汇编函数不会检查是否需要调度。在返回时,CPU打开中断,然后从中断的地方继续执行线程0

支持内核抢占

1)中断返回会检查当前进程是否设置了need_resched表示位,如果置位,调用preempt_schedule_irq函数以调度其他进程并运行。

2)在el1_irq汇编函数即将返回中断现场时,判断当前进程是否需要调度。如果需要调度,调度器会选择下一个进程,并且进行进程的切换。

3)如果选择现场1,则从线程1的pt_regs中恢复中断现场并打开中断,然后继续执行内核线程1的代码。

二、如何让新进程执行

如果线程1是新创建的,它的栈应该是空的,第一次运行时如何恢复中断现场呢?如果不能从线程1的栈中恢复中断现场,那是不是线程1一直在关闭中断的状态下运行?

对于内核线程来说,在创建时会对如下两部分内容进行设置与保存。copy_thread()函数

  1. 进程的硬件上下文。保存在进程的cpu_context数据结构。
  2. pt_regs
int copy_thread_tls(unsigned long clone_flags, unsigned long stack_start,
		unsigned long stk_sz, struct task_struct *p, unsigned long tls)



	 else 
		memset(childregs, 0, sizeof(struct pt_regs));
		childregs->pstate = PSR_MODE_EL1h;//5 处理器状态 第0位 栈指针选择符,1:选择栈之战寄存器SP_EL1 2:3 异常级别,值1表示异常级别1
		if (IS_ENABLED(CONFIG_ARM64_UAO) &&
		    cpus_have_const_cap(ARM64_HAS_UAO))
			childregs->pstate |= PSR_UAO_BIT;

		if (arm64_get_ssbd_state() == ARM64_SSBD_FORCE_DISABLE)
			set_ssbs_bit(childregs);

		if (system_uses_irq_prio_masking())
			childregs->pmr_save = GIC_PRIO_IRQON;

		p->thread.cpu_context.x19 = stack_start;//函数地址,用来创建内核线程的函数kernel_thread的第一参数
		p->thread.cpu_context.x20 = stk_sz;//参数 ,用来创建内核线程的函数kernel_thread的第二参数
	
	p->thread.cpu_context.pc = (unsigned long)ret_from_fork;//子进程的程序计数器,调度入口
	p->thread.cpu_context.sp = (unsigned long)childregs;//sp指向内核栈底部pt_regs起始位置

stack_start指向内核线程的回调函数

x20 指向回调函数的参数

PC寄存器 ret_from_fork  执行入口

三、调度的本质

系统中有一个用户进程A和一个内核线程B,在不考虑自愿调度系统调用情况下,请描述这两个进程是如何相互切换并运行的。

  • 进程A在用户空间运行;
  • 发生中断
  • CPU打断正在运行的用户进程A,处于异常模式。CPU会跳转到异常向量表的el0_irq里。在汇编函数el0_irq中,首先把中断现场保存到进程A的pt_regs
  • 处理中断
  • 调度滴答处理函数,返回el0_irq汇编函数里。即将返回现场前,ret_to_user汇编函数会检查当前进程是否需要调度。
  • 若当前进程需要调度,调用schedule()函数选择下一个进程并切换 ;(switch_to函数)
  • 切换函数返回,CPU开始运行内核线程B;进程需要为前一个进程做收尾工作,比如调用raw_spin_unlock_irq来释放锁并打开本地中断。见finish_task_switch函数。
  • CPU沿着内核线程B保存的栈帧回溯,一直返回。返回路径finish_task_switch->el1_preempt->el1_irq
  • 在el1_irq汇编函数里把上一次发生中断时保存在栈里的中断现场进行恢复,最后从上一次中断的地方开始执行内核线程B的代码。

以上涉及两个上下文切换,中断上下文(pt_regs),进程上下文(task_struct)

linux内核实时调度类⑦(实时调度类核心函数源码分析|dequeue_task_rt函数|从执行队列中移除进程)(代码片段)

...的各个函数指针指向的函数源码;rt_sched_class结构体在Linux内核源码的linux-5.6.18\\kernel\\sched\\rt.c源文件中定义,实时调度相关的核心 查看详情

linux内核调度器③(sched_class调度类结构体分析|next字段|enqueue_task函数|dequeue_task函数)(代码片段)

...执行队列)三、dequeue_task函数(从执行队列中删除进程)Linux内核源码linux-5.6.18\\kernel\\sched\\sched.h中,定义的structsched_class调度类结构体,就是"调度器"对应的类;一、next字段(指向链表中的下一个调度类)整个L 查看详情

linux内核源码分析之进程调度(代码片段)

文章目录一、进程优先级二、内核支持调度策略三、task_struct调度相关的成员四、调度类五、就绪队列六、调度实体调度策略通常在进程响应速度和最大系统利用率寻找平衡。一、进程优先级1)普通优先级:nice值范围-20~... 查看详情

linux内核实时调度类②(实时调度实体sched_rt_entity源码分析|run_listtimeoutwatchdog_stamptime_slice字段)(代码片段)

...q字段二、总结一、sched_rt_entity源码分析上一篇博客【Linux内核】实时调度类①(进程分类|实时进程、普通进程|Linux内核SCHED_FIFO、SCHED_RR调度策略|实时调度实体sched_rt_entity)引入了实时调度实体sched_rt_entity结构体源码,在Linux内核源... 查看详情

linux内核调度器④(sched_class调度类结构体分析|yield_task函数|heck_preempt_curr函数|task_struct函数)(代码片段)

...程是否可以被抢占)三、task_struct函数(选择运行进程)Linux内核源码linux-5.6.18\\kernel\\sched\\sched.h中,定义的structsched_class调度类结构体,就是"调度器"对应的类;一、yield_task函数(放弃CP 查看详情

linux内核实时调度类④(实时运行队列rt_rq源码分析|实时运行队列rt_rq结构体字段分析|activert_nr_runningcurrnext字段)(代码片段)

...3、curr字段4、next字段一、实时运行队列rt_rq源码在【Linux内核】实时调度类②(实时调度实体sched_rt_entity源码分析|run_list、timeout、watchdog_stamp、time_slice字段)博客中,简单介绍了在linux 查看详情

linux内核源码分析之实时调度(代码片段)

实时调度实时调度的优先级比普通进程高,相应的static_prio值总是比普通进程低。rt_task:宏通过检测其优先级来证实给定进程是否是实时进程task_has_rt_policy:检测进程是否是关联到实时调度策略SCHED_FIFO:没有时间... 查看详情

linux内核调度浅析(代码片段)

目录进程控制块PCB就绪队列结构体调度队列成员下一个进程的选择进程切换加入就绪队列        linux进程调度相关的知识再重新梳理一遍。抽取主要数据结构中的主要成员,以最简单的方式实现进程调度。进程控制块PCBt... 查看详情

linux内核线程kernelthread详解--linux进程的管理与调度(代码片段)

内核线程为什么需要内核线程Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求)。内核需要多个执行流并行,为了防止可能的阻塞,支持多线程是必要的。内核线程就是内核的分身,... 查看详情

linux网络协议栈之内核锁——内核抢占(代码片段)

一、内核抢占  早期的Linux核心是不可抢占的。它的调度方法是:一个进程可以通过schedule()函数自愿地启动一次调度。非自愿的强制性调度只能发生在每次从系统调用返回的前夕以及每次从中断或异常处理返回到用户空间的... 查看详情

linux用户抢占和内核抢占详解(概念,实现和触发时机)--linux进程的管理与调度(二十)(代码片段)

1非抢占式和可抢占式内核为了简化问题,我使用嵌入式实时系统uC/OS作为例子首先要指出的是,uC/OS只有内核态,没有用户态,这和Linux不一样多任务系统中,内核负责管理各个任务,或者说为每个任务分配CPU时间,并且负责任务之... 查看详情

perfettoforlinux-使用perfetto分析调度问题(代码片段)

title:Perfettoforlinux-使用Perfetto分析调度问题date:2020-11-2122:40author:gatiemetags:-scheduler-linux-debugcategories:-schedulerthumbnail:blogexcerpt:Perfetto工具是Android下一代全新的统一的trace收集和分析框架,在Android9.0(API级别 查看详情

threadx内核源码分析-定时器及线程时间片调度(arm)(代码片段)

1、线程时间片介绍(tx_thread_time_slice)ThreadX内核同优先级线程之间是按时间片调度的,tx_thread_new_time_slice记录线程的时间片(一次调度的总的时间片),tx_thread_time_slice记录线程的剩余时间片(ThreadX内核每次调度线程时,并... 查看详情

perfettoforlinux-使用perfetto分析调度问题(代码片段)

....CSDNGitHubBlog知乎掘金紫夜阑珊-青伶巷草debug/tools/perfettoOS内核实验室3589560936978869597248159757本作品采用知识共享署名-非商业性使用-相同方式共享4.0国际许可协议进行许可,转载请注明出处,谢谢合作因本人技术水平和知识面有限,内... 查看详情

linux内核分析实验五(代码片段)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~刘旸 + 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 查看详情

linux内核—进程调度时机(代码片段)

...的分类主动调度周期调度唤醒进程时抢占创建进程时抢占内核抢占调度时机的分类   主动调度   周期调度   唤醒进程的时候   创建进程的时候主动调度   进程在用户模式下运行,无法直接调用schedule(),只能通过系... 查看详情

08linux011进程调度(代码片段)

...请求调度(如调用pause);[2]进入系统调用时内核资源不够,则主动睡眠当前进程并调度其他进程运行,当内核资源可用时被唤醒,从而再次进入能被调度的状态;[3]在定时器中断中,当前进程时间... 查看详情

08linux011进程调度(代码片段)

...请求调度(如调用pause);[2]进入系统调用时内核资源不够,则主动睡眠当前进程并调度其他进程运行,当内核资源可用时被唤醒,从而再次进入能被调度的状态;[3]在定时器中断中,当前进程时间... 查看详情