linux内核调度浅析

news2024/7/6 19:34:25

目录

进程控制块PCB

就绪队列结构体

调度队列成员

下一个进程的选择

进程切换

加入就绪队列


        linux进程调度相关的知识再重新梳理一遍。抽取主要数据结构中的主要成员,以最简单的方式实现进程调度。

进程控制块PCB

task_struct

/* 进程PCB */
struct task_struct {
	enum task_state state;
	enum task_flags flags;
	int pid;
	struct cpu_context cpu_context;
	struct list_head run_list;
	int counter;
	int priority;
	int need_resched;
	int preempt_count;
	struct task_struct *next_task;
	struct task_struct *prev_task;
};

state进程的状态

flags进程的标志位

pid 进程的ID

cpu_context 进程的上下文信息,切换信息时需要保存与恢复

counter 进程调度时间片

prioity 进程优先级

need_resched是否需要调度,在时间片用完的时候会置位need_resched

next_task,prev_task 在队列中的进程,next_task是下一个进程,prev_task是上一个进程。

就绪队列结构体

进程添加到调度器中,需要借助与就绪队列,队列采用链式队列。

struct list_head {
	struct list_head *next, *prev;
};
struct run_queue {
	struct list_head rq_head;
	unsigned int nr_running;
	u64 nr_switches;
	struct task_struct *curr;
};

rq_head队列的头

nr_running 队列中进程的数量

nr_switches 统计计数,统计进程切换次数

curr当前进程

调度队列成员

struct sched_class {
	const struct sched_class *next;

	void (*task_fork)(struct task_struct *p);
	void (*enqueue_task)(struct run_queue *rq, struct task_struct *p);
	void (*dequeue_task)(struct run_queue *rq, struct task_struct *p);
	void (*task_tick)(struct run_queue *rq, struct task_struct *p);
	struct task_struct * (*pick_next_task)(struct run_queue *rq,
			struct task_struct *prev);
};

next指向下一个调度类

task_fork,进程创建时,对进程做调度相关的初始化

enqueue_task 加入就绪队列

dequeue_task 移除就绪队列

task_tick 与调度相关的时钟中断,在定时器中周期调度,用于维护时间变量counter。

pick_next_task选择下一个进程

enqueue_task的实现

static void enqueue_task_simple(struct run_queue *rq,
		struct task_struct *p)
{
	list_add(&p->run_list, &rq->rq_head);
	rq->nr_running++;
}

调用list_add方法 ,将task_struct 加入到run_queue里;将nr_running加1

以上结构体之间的关系

 

 

下一个进程的选择

 linux 0.11中的调度算法

static struct task_struct *pick_next_task_simple(struct run_queue *rq,
		struct task_struct *prev)
{
	struct task_struct *p, *next;
	struct list_head *tmp;
	int weight;
	int c;

repeat:
	c = -1000;
    //循环遍历就绪队列,找出时间片最大的进程作为next进程
	list_for_each(tmp, &rq->rq_head) {
		p = list_entry(tmp, struct task_struct, run_list);
		weight = goodness(p);//获取每个进程的权重
		if (weight > c) {
			c = weight;//权重跟新
			next = p;//选择下一个要运行的进程
		}
	}

	if (!c) {
		reset_score();
		goto repeat;
	}
	return next;
}

调度场景

1、自愿调度:进程调用seched()主动放弃CPU控制权 

static void __schedule(void)
{
	struct task_struct *prev, *next, *last;
	struct run_queue *rq = &g_rq;

	prev = current;

	/* 检查是否在中断上下文中发生了调度 */
	schedule_debug(prev);

	/* 关闭中断包含调度器*/
	raw_local_irq_disable();

	if (prev->state)
		dequeue_task(rq, prev);

	next = pick_next_task(rq, prev);
	clear_task_resched(prev);
	if (next != prev) {
		last = switch_to(prev, next);
		rq->nr_switches++;
		rq->curr = current;
	}
	schedule_tail(last);
}

//自愿调度的入口,先关闭抢占,避免发生嵌套抢占
void schedule(void)
{
	preempt_disable();
	__schedule();
	preempt_enable();
}
  • 如果prev->state为TASK_RUNNING (=0)说明当前进程在运行,发生抢占调用。
  • 如果当前进程处于其他状态(TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE),说明主动请求调度。
  • 如果主动调度了,则dequeue_task函数把当前进程移除就绪队列。什么时候加入就绪队列,下文分析。
  • 选择下一个进程pick_next_task,就是上文介绍的pick_next_task_simple
  • 清除当前进程的一些状态
  • 如果下一个进程不是当前进程,则需要切换switch_to
  • 调度收尾schedule_tail 打开本地中断

2、抢占调度:中断处理后会检查是否可以抢占当前进程的运行。在汇编程序中

.align 2
el1_irq:
	kernel_entry 1
	bl irq_handle

	get_thread_info tsk
	ldr  w24, [tsk, #TI_PREEMPT]
	cbnz w24, 1f
	ldr  w0, [tsk, #NEED_RESCHED]
	cbz w0, 1f
	bl el1_preempt
1:
	kernel_exit 1

el1_preempt:
	mov     x24, lr
	bl preempt_schedule_irq
	ret     x24

在中断处理完成后,调用get_thread_info宏来获取当前进程的task_struct数据结构。

读取进程抢占计数preempt_count值,如果大于0,说明是禁止抢占的,退出中断现场;否则允许抢占,读取need_resched值,来判断当前进程是否要抢占;如果need_resched为1,则进行抢占调度。

进程切换

switch_to(prev, next)

.align
.global cpu_switch_to
cpu_switch_to:
	add     x8, x0, #THREAD_CPU_CONTEXT
	mov     x9, sp
	stp     x19, x20, [x8], #16
	stp     x21, x22, [x8], #16
	stp     x23, x24, [x8], #16
	stp     x25, x26, [x8], #16
	stp     x27, x28, [x8], #16
	stp     x29, x9, [x8], #16
	str     lr, [x8]

	add     x8, x1, #THREAD_CPU_CONTEXT
	ldp     x19, x20, [x8], #16
	ldp     x21, x22, [x8], #16
	ldp     x23, x24, [x8], #16
	ldp     x25, x26, [x8], #16
	ldp     x27, x28, [x8], #16
	ldp     x29, x9, [x8], #16
	ldr     lr, [x8]
	mov     sp, x9
	ret
  • 保存进程上下文:需要保存x19~x29,sp,lr 寄存器值到 task_struct->cpu_context
  • 恢复next进程 .

x0寄存器是prev参数;x1寄存器是next参数

加入就绪队列

在创建进程的时候 fork->wake_up_process

void wake_up_process(struct task_struct *p)
{
	struct run_queue *rq = &g_rq;

	p->state = TASK_RUNNING;

	enqueue_task(rq, p);
}

设置TASK_RUNNING状态,添加到就绪队列

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

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

相关文章

人脸识别速度超高识别度超高项目,可实时进行检测,一看就会!

1.本项目属于pytorch-facenet项目,核心代码是facenet算法,经过1周的代码修改,可以进行入库和识别的连续操作,经过测试,识别效果很好,在GPU环境中可以进行实时摄像头的识别,同时项目将放在百度网…

知行之桥传输带附件的文件示例

在大多数的项目中,交易伙伴往往只要求传输报文消息,业务数据经由报文内容来进行传输。但有些交易伙伴也会要求传输带附件的文件,比如在与大众和延锋汽车YFAI对接的项目当中,交易伙伴要求传输VDA4951 ENGDAT报文,该业务…

vue3 销毁组件方法

问题描述:使用elementplus的dialog,当关闭弹窗后不刷新页面,直接再次打开发现弹窗中还存留上一次的数据。尝试定义关闭事件,或者使用api中提供的属性destroy-on-close 都不行。后来发现这是一个误区。弹窗关闭时并不代表这个组件已经被销毁了…

Linux测试主机之间连通性和端口是否开放的方法

文章目录测试主机之间的连通性测试端口是否开放(curl)测试端口是否开放(wget)测试端口是否开放(ssh)下面每一种测试方式都给出了成功通信的截图,如果与截图不相符可以根据你控制台的报错调试。测试主机之间的连通性 测试两个主机之间是否可以通信,通常使…

Odoo 16 企业版手册 - 库存管理之规则与路线

规则和路线 产品上定义的路线将帮助您理解和跟踪产品的每一次调拨。它是用于库存调拨的操作规则或路线。没有适当的策略,就很难监控和管理公司的库存变动。根据您的公司政策,您可以设置某些操作规则来定义库存中的产品调拨。使用这些规则,Odo…

何为 Vue3 组件标注 TS 类型,看这篇文章就够了!

文章目录前言一、为 props 标注类型使用 < script setup >非 < script setup >二、为 emits 标注类型使用 < script setup >非 < script setup >三、为 ref() 标注类型默认推导类型通过接口指定类型通过泛型指定类型四、为 reactive() 标注类型默认推导…

什么真无线蓝牙耳机值得入手?蓝牙耳机全方位挑选攻略

从我们的日常生活中可以看到&#xff0c;蓝牙耳机的使用频率真的是越来越高了&#xff0c;这主要得益于蓝牙耳机的使用便捷性以及近几年的快速发展。很多人在选择时不禁有些疑问&#xff0c;不知道哪款真无线蓝牙耳机值得入手&#xff1f; 都说买新不买旧&#xff0c;所以&…

黑马2022新版SSM框架教程(SpringMVC_day01)

SpringMVC_day01 文章目录SpringMVC_day011&#xff0c;SpringMVC简介1.1 SpringMVC概述2&#xff0c;SpringMVC入门案例2.1 需求分析2.2 案例制作步骤1:创建Maven项目&#xff0c;并导入对应的jar包步骤2:创建控制器类步骤3:创建配置类步骤4:创建Tomcat的Servlet容器配置类步骤…

网上流行短视频运营方法验证和试错,这些坑你踩过吗?

网上流行短视频运营方法验证和试错,这些坑你踩过吗&#xff1f; 人到中年&#xff0c;35岁以后找份工作不容易&#xff0c;这不刚刚有了一份短视频运营的工作。 在网上找了一些短视频运营技巧&#xff0c;看网上的评论有说有用的有说没用的。 只好自己去试一下错了&#xff…

升级win11后,此电脑中原来7个文件夹的恢复

目录前言问题描述解决方法新建一个.reg文件在.reg文件中添加代码执行.reg文件效果展示P.S. 添加部分文件夹参考文献链接前言 这个方法需要向注册表添加一些项。只需要新建一个.reg文件然后执行即可。 问题描述 更新win11后&#xff0c;以前的文件夹都消失不见了 解决方法 …

一文读懂JVM类加载机制过程及原理万字详解

JVM加载机制详解 文章目录JVM加载机制详解类装载子系统类加载子系统介绍类加载器ClassLoader角色类加载的执行过程加载链接初始化< cinit > 方法和 < init > 方法有什么区别&#xff1f;类加载器类加载器的作用类加载器分类启动类加载器扩展类加载器系统类加载器用…

STM32 TIM PWM高阶操作:刹车及状态约束

STM32 TIM PWM高阶操作&#xff1a;刹车及状态约束 刹车及状态约束是STM32 TIM PWM控制里面比较复杂的一部分&#xff0c;涉及到PWM波形产生前&#xff0c;中&#xff0c;后的管脚状态输出。 这里先引入两个描述&#xff0c;一个是“半高阻”&#xff0c;意思是STM32管脚输出…

我的基于 JamStack 的新博客

概述 今天心血来潮&#xff0c;介绍一下我的新博客站点 —— https://EWhisper.cn。 我是做基础平台 PaaS 运维和架构的&#xff0c;挺喜欢把工作中学到的新知识写下来、记笔记&#xff0c;突然有一天就抱着「资源共享、天下为公」的理念&#xff0c;分享我的学习心得&#x…

KDevelop详细Debug教程

KDevelop官方连接&#xff1a;https://www.kdevelop.org/ 感觉网上对KDevelop的使用介绍比较少&#xff0c;也没有一个完整的Debug教程&#xff0c;这里记录一下我的踩坑过程。当然首先你需要有一个Ubuntu系统&#xff0c;然后需要apt-get安装一下KDevelop。 首先CMakeLists.t…

基于springboot+mybatis+mysql+html实现宠物医院管理系统2(包含实训报告)

基于springbootmybatismysqlhtml实现宠物医院管理系统2&#xff08;包含实训报告&#xff09;一、需求背景二、系统简介二、系统主要功能界面1、用户登陆2、系统主页3、医生信息4、客户信息5、宠物信息6、浏览管理7、诊断管理8、医生管理9、用户管理三、其它系统四、获取源码一…

Dubbo相关概念

分布式系统中的相关概念 dubbo 概述 dubbo快速入门 dubbo的高级特性 2-相关概念 2.1-互联网项目架构-特点 互联网项目架构-特点 用户多 流量大&#xff0c;并发高 海量数据 易受攻击 功能繁琐 变更快 传统项目和互联网项目的不同 用户体验&#xff1a; 美观、功能…

【 Vue3 + Vite + setup语法糖 + Pinia + VueRouter + Element Plus 第四篇】(持续更新中)

在第三篇中&#xff0c;我们主要学习了组件的封装与使用以及 组件间传值和 Element Plus 表格、表单的用法 本期需要掌握的知识如下: mixin 公共方法封装和使用项目中导入 VueRouter使用 VueRouter 完成 路由跳转、获取路由信息VueRouter 模块化、路由拦截器权限路由配置 下期…

https 加密原理握手过程详解

HTTPS握手过程 HTTPS的握手过程比较繁琐&#xff0c;我们来回顾下。 先是建立TCP连接&#xff0c;毕竟HTTP是基于TCP的应用层协议。 在TCP成功建立完协议后&#xff0c;就可以开始进入HTTPS阶段。 HTTPS可以用TLS或者SSL啥的进行加密&#xff0c;下面我们以TLS1.2为例。 总…

自定义antd或element table 列设置组件(拖拽排序及控制是否展示)

需求 展示出所有的字段显示当前展示的是哪些字段可以全选、取消全选可以拖拽排序&#xff0c;更改字段的展示顺序&#xff0c;在前面还是在后面可以保存配置&#xff0c;刷新不失效 难点 如何进行拖拽排序&#xff0c;自己手写一个吗&#xff1f;如何得到拖拽后的顺序&#…

verilog学习笔记- 8)状态机

目录 概念&#xff1a; 状态机的模型&#xff1a; 状态机的设计&#xff1a; 根据状态机的实际写法&#xff0c;状态机可以分为一段式、二段式和三段式状态机。 三段式状态机的基本格式&#xff1a; 概念&#xff1a; 状态机&#xff0c;全称是有限状态机&#xff08;Fin…