[linux][调度] 内核抢占入门 —— 线程调度次数与 CONFIG_PREEMPTION

在工作中,如果你正在做开发的工作,正在在写代码,这个时候测试同事在测试过程中测出了问题,需要你来定位解决,那么你就应该先暂停写代码的工作,转而来定位解决测试的问题;如果你正在定位测试的问题,这个时候线上系统出现了问题,你就需要先将测试的问题暂停,转而去定位线上的问题。这就是抢占,线上问题优先级比测试问题优先级高,所以线上问题可以抢占测试问题;测试问题比开发工作优先级高,所以测试问题可以抢占开发工作。

在非抢占式内核中,内核线程是不能被抢占的,只有线程主动调用 schedule(),或者显式睡眠以及发生阻塞时发生调度,否则内核其它线程是不能抢占这个线程的。

在抢占式内核中,即使一个线程在运行,没有主动调度 schedule() 或者睡眠以及阻塞,当一个更高优先级的线程被唤醒之后,也可以抢占当前这个线程。

1 线程调度次数

linux 中,每个进程在 /proc 文件夹下都有一个进程对应的文件夹,文件夹以进程 id(pid) 命名,如下图所示。每个进程的文件夹下包括这个进程的很多信息,其中 status 文件中保存着这个进程的基础信息,比如 pid, ppid,进程使用了多少内存,进程的调度次数。

如下截图,是进程 18414 的 status 文件的显示,使用 switch 进行了过滤。其中 voluntary_ctxt_switchs 是自愿调度,nonvoluntary_ctxt_switches 是非自愿调度。

自愿调度

① 调用 sleep() 的时候

② 读写文件或者网络收发包时阻塞

③ 使用互斥体加锁时,如果不能立即得到锁,那么线程会睡眠,属于自愿调度

非自愿调度非自愿调度,意思是当线程还在运行,没有主动触发调度。比如,对于普通调度策略来说,时间片用完时,可以被抢占,这样就会统计一次非自愿调度。

自愿调度次数和非自愿调度次数,在进程控制块 struct task_struct 中用两个成员属性来表示,分别是 nvcsw 和 nivcsw。

struct task_struct {.../* Context switch counts: */unsigned long			nvcsw;unsigned long			nivcsw;...
};

在调度函数 __schedule() 中对自愿调度统计和非自愿调度统计进行递增。如果不是抢占调度,并且进程的状态不是 TASK_RUNNING 的话,就是自愿调度;否则,为非自愿调度。

怎么上一个线程不是 TASK_RUNNING 呢,其实在切换的时候,线程还是处于运行状态的,只不过在调用 schedule() 之前,线程会将自己设置为其它状态。比如在使用 mutex_lock() 加锁的时候,会先将自己设置为 TASK_UNINTERRUPTIBLE 状态,然后再调用 schedule() 进行等待。

static void __sched notrace __schedule(bool preempt)
{struct task_struct *prev, *next;unsigned long *switch_count;switch_count = &prev->nivcsw;if (!preempt && prev_state) {...switch_count = &prev->nvcsw;}if (likely(prev != next)) {...++*switch_count;} else {rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);rq_unlock_irq(rq, &rf);}
}

1.1 用户线程,自愿调度

如下代码,主线程中是一个死循环,每次循环 sleep 1s,每次 sleep 的时候会增加自愿调度计数。这是线程主动睡眠的,而不是时间片用完了被动调度走的,所以是自愿调度。

#include <iostream>
#include <string>
#include <thread>
#include <unistd.h>int main() {while (1) {sleep(1);}return 0;
}

程序运行之后,查看调度次数统计,可以看到自愿调度次数一直在增长,线程没有发生过非自愿调度。

1.2 用户线程,非自愿调度

如下代码,是一个单纯的死循环,在循环中什么都没做。程序运行之后,因为会一直占用 cpu,所以当线程的时间片用完时,线程就会被调度,这种情况下的调度被统计为非自愿调度。

#include <iostream>
#include <string>
#include <thread>
#include <unistd.h>int main() {while (1) {}return 0;
}

程序运行之后,查看调度统计,可以看到非自愿调度计数一直在增长,自愿调度计数是 0。

1.3 非抢占内核,内核线程不会被抢占

如果内核是非抢占内核,那么内核线程在运行的时候就不会被抢占,即使线程一直占用着 cpu,物理时间片和虚拟时间片一直在增长,也不会被抢占。

如下是一个内核模块,在内核模块中使用 kthread_run() 创建了一个内核线程,线程中是一个死循环。在线程中打印了线程的 id。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/sched.h>static struct task_struct *my_thread;// 内核线程函数
static int my_thread_func(void *data)
{// 内核线程的逻辑处理代码printk(KERN_INFO "My kernel thread is running, pid = %d\n", current->pid);while (1);return 0;
}// 模块初始化函数
static int __init my_module_init(void)
{// 创建内核线程my_thread = kthread_run(my_thread_func, NULL, "my_thread");if (IS_ERR(my_thread)) {printk(KERN_ERR "Failed to create kernel thread!\n");return PTR_ERR(my_thread);}printk(KERN_INFO "Module loaded!\n");return 0;
}// 模块清理函数
static void __exit my_module_exit(void)
{// 停止内核线程kthread_stop(my_thread);printk(KERN_INFO "Module unloaded!\n");
}MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample kernel module with a kernel thread");module_init(my_module_init);
module_exit(my_module_exit);

编译脚本:

obj-m += hello.o
all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

线程一致在死循环,没有看到非自愿调度次数增长。

1.4 用户抢占和内核抢占

用户抢占,是指用户态的线程被抢占;内核抢占,是指内核态的线程被抢占。

linux 系统,默认情况下是支持用户抢占的。而是否支持内核抢占,需要看具体的内核配置,在一些嵌入式系统或者桌面系统,对实时性要求高,会打开内核抢占;而在服务器系统中,一般不会打开内核抢占。打开内核抢占的系统,使用 uname -a 可以看到 PREEMPT 标志,没有 PREEMPT 标志,说明没有打开内核抢占。如下是我笔记本上安装的 ubuntu 系统,没有打开内核抢占。

2 CONFIG_PREEMPTION 宏定义了什么内容

当打开内核抢占时,也就是定义了 CONFIG_PREEMPTION 这个宏。那么打开这个宏的时候,具体定义了那些内容呢 ?本人使用的源码版本是 5.10.186。

2.1 中断返回时

中断返回的时候,如果需要抢占调度,那么会调用函数 preempt_schedule_irq()。这段代码一般是使用汇编指令来实现的。如下是 arm 中的实现,下边这段代码,只有定义了 CONFIG_PREEMPTION 时,才会生效。

arch/arm/kernel/entry-armv.S

#ifdef CONFIG_PREEMPTION
svc_preempt:mov	r8, lr
1:	bl	preempt_schedule_irq		@ irq en/disable is done insideldr	r0, [tsk, #TI_FLAGS]		@ get new tasks TI_FLAGStst	r0, #_TIF_NEED_RESCHEDreteq	r8				@ go againb	1b
#endif

2.2 抢占计数器操作函数

preempt 是抢占的意思。linux 内核中有两个宏 preempt_enable() 和 preempt_disable() 分别时使能抢占和禁止抢占。当定义 CONFIG_PREEMOPTION 宏的时候,在 preempt_enable() 中会进行判断,如果当前条件满足,并且有更高优先级的线程需要抢占的话,那么就会进行抢占调度。如果没有定义 COFIG_PREEMPTION 宏,那么 preempt_enable() 中就不会做抢占调度的工作。


#ifdef CONFIG_PREEMPTION
#define preempt_enable() \
do { \barrier(); \if (unlikely(preempt_count_dec_and_test())) \__preempt_schedule(); \
} while (0)#define preempt_enable_notrace() \
do { \barrier(); \if (unlikely(__preempt_count_dec_and_test())) \__preempt_schedule_notrace(); \
} while (0)#define preempt_check_resched() \
do { \if (should_resched(0)) \__preempt_schedule(); \
} while (0)#else /* !CONFIG_PREEMPTION */
#define preempt_enable() \
do { \barrier(); \preempt_count_dec(); \
} while (0)#define preempt_enable_notrace() \
do { \barrier(); \__preempt_count_dec(); \
} while (0)#define preempt_check_resched() do { } while (0)
#endif /* CONFIG_PREEMPTION */

2.3 _cond_resched

从 _cond_resched 的定义来看,当没有定义 CONFIG_PREEMPTION 的时候,_cond_resched() 才会生效;当定义 CONFIG_PREEMPTION 的时候,直接返回 0。

_cond_resched() 主要是在非抢占内核中起作用,在一些消耗 cpu 的场景主动调用 _cond_resched() 来防止线程占用 cpu 太多。

#ifndef CONFIG_PREEMPTION
extern int _cond_resched(void);
#else
static inline int _cond_resched(void) { return 0; }
#endif#ifndef CONFIG_PREEMPTION
int __sched _cond_resched(void)
{if (should_resched(0)) {preempt_schedule_common();return 1;}rcu_all_qs();return 0;
}
EXPORT_SYMBOL(_cond_resched);
#endif

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

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

相关文章

mysql事务及存储引擎

目录 什么是事务 事务的ACIP特性 事务之间的影响 mysql隔离级别 事务隔离级别的作用范围 事务控制语句 mysql存储引擎 什么是事务 事务是一种机制、一个操作序列&#xff0c;包含了一组数据库操作命令&#xff0c;并且把所有的命令作为一个整体一起向系统提交或撤销操作…

AI预测福彩3D第15弹【2024年3月21日预测--第3套算法重新开始计算第4次测试】

今天咱们继续对第3套算法进行第4次测试&#xff0c;第3套算法加入了012路的权重。废话不多说了&#xff0c;直接上结果吧~ 最终&#xff0c;经过研判分析&#xff0c;2024年3月21日福彩3D的七码预测结果如下&#xff1a; 百位&#xff1a;4 5 7 1 0 6 2 十位&#xff1a;3 1 5 …

QML 布局管理器之GridLayout 项目demo

一.气体控制效果图 二.界面布局代码实现 //DottedLline.qml 虚线绘制 import QtQuick 2.12 import QtQuick.Shapes 1.12Shape {id:canvaswidth: parent.widthheight: parent.heightShapePath{strokeStyle: ShapePath.DashLinestartX: 8startY: 10dashPattern: [1, 3]PathLine{…

在Linux搭建Emlog博客结合内网穿透实现公网访问本地个人网站

文章目录 前言1. 网站搭建1.1 Emolog网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 3. 公网访问测试总结 前言 博客作为使…

Flink入门知识点汇总(二)

具体内容请看b站尚硅谷课程&#xff01; 32_Flink运行时架构_提交流程_Yarn应用模式_哔哩哔哩_bilibili 窗口 Flink的窗口并不是静态准备好的&#xff0c;而是动态创建的。数据流到达时不会准备24个或者其他完整数量的桶&#xff0c;而是当下桶接满了&#xff0c;才临时又拿新…

web前端之小功能聚集、简单交互效果

MENU 纯CSS实现可编辑文字霓虹灯闪烁效果css之实现流水文字、闪烁、荧光、炫酷web前端之文本擦除效果与下划线结合css之下划线动画 纯CSS实现可编辑文字霓虹灯闪烁效果 效果图 html <h1 contenteditable"true">Hello World</h1>style * {margin: 0;pa…

C/C++在线参考手册的使用技巧

cppreference.com是一个在线的C/C参考手册&#xff0c;是C/C学习者最常用的网站。 网址&#xff1a;cppreference.com 1&#xff0e;搜索 不知道为什么这个网站总是不能正常搜索&#xff0c;实在是太不方便了。 有两个退而求其次的方法&#xff1a; (1)通过搜索引擎指定域名…

C++常用的区块代码

很多人在刷题时都遇到过不会的情况 这篇文章希望可以帮到你&#xff01; 1.输入n将这个数倒着输出来&#xff1a; while(n!0){tn%10;printf("%d",t);nn/10; }只要会这条代码&#xff0c;很多题目都可以直接秒杀。 如&#xff1a; 输入一个整数n,算出它各个位数的乘积…

程序员想要搞钱不迷茫,这篇文章你可得码好啦!!!

年已经过完了&#xff0c;现在大家都已经返工返校了吧&#xff01;咱又要投入到新一年的战斗了&#xff01;春色恼人不等闲&#xff0c;相信咱都有一个实实在在的愿望和期许&#xff1a;身体健康&#xff0c;财源广进&#xff01;新的一年我们还得继续努力&#xff0c;多多搞钱…

利用IP地址查防止电子招投标串标行为

随着信息技术的快速发展&#xff0c;电子招投标已成为政府和企业采购的主要方式。然而&#xff0c;电子招投标中的串标问题也愈发突出&#xff0c;给公平竞争和资源分配带来了隐患。为了防止串标行为&#xff0c;利用IP地址查已成为一种有效手段。 IP地址查询&#xff1a;IP数…

002_avoid_for_loop_in_Matlab避免使用for循环

避免使用for循环 在程序设计思想中&#xff0c;循环是一个很有力的工具。在循环中&#xff0c;计算机很轻松地重复执行相同的操作。循环是汇编之上的编程中最重要的概念之一。Matlab的循环有两个语言构造&#xff0c;一个是for循环&#xff0c;另一个是while循环。在Matlab中&…

罗德与施瓦茨联合广和通全面验证RedCap模组FG132系列先进性能

近日&#xff0c;罗德与施瓦茨联合广和通完成Redcap(Reduce Capability)功能和性能验证。本次测试使用R&SCMX500 OBT(One Box Tester)无线通信测试仪&#xff0c;主要验证广和通RedCap模组FG132系列射频性能以及IP层吞吐量&#xff0c;包括RedCap上下行吞吐量和射频指标如矢…

【计算机网络篇】数据链路层(2)封装成帧和透明传输

文章目录 &#x1f95a;封装成帧和透明传输&#x1f388;封装成帧&#x1f388;透明传输&#x1f5d2;️面向字节的物理链路使用字节填充的方法实现透明传输。&#x1f5d2;️面向比特的物理链路使用比特填充的方法实现透明传输。 &#x1f6f8;练习 &#x1f95a;封装成帧和透…

Linux systemd详解

1、概念 1.1 systemd systemd 是一个用于管理 Linux 系统启动过程和系统服务的系统和服务管理器。它被设计为取代传统的 System V init 系统&#xff0c;提供了更快的启动时间、并行启动服务、更好的日志记录和更强大的管理功能。 1.2 unit Unit 是 systemd 中所有配置文件…

基于ssm校园美食交流系统论文

目 录 摘 要 1 前 言 3 第1章 概述 4 1.1 研究背景 4 1.2 研究目的 4 1.3 研究内容 4 第二章 开发技术介绍 5 2.1Java技术 6 2.2 Mysql数据库 6 2.3 B/S结构 7 2.4 SSM框架 8 第三章 系统分析 9 3.1 可行性分析 9 3.1.1 技术可行性 9 3.1.2 经济可行性 10 3.1.3 操作可行性 10…

字符函数与字符串函数

目录 一.字符分类函数 二.字符转化函数 三.strlen函数 函数的介绍 strlen函数的模拟实现 1.计算器法 2.递归 三.指针-指针的方式 四.strcpy函数 函数介绍 strcmp的模拟实现 五.strcat函数 函数介绍 strcat的模拟实现 六.strcmp函数 函数介绍 返回值 strcm…

Mysql学习--深入探究索引和事务的重点要点与考点

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

自动驾驶---Motion Planning之轨迹Path优化

1 背景 在之前的几篇文章中,不管是通过构建SL图《自动驾驶---Motion Planning之Path Boundary》,ST图《自动驾驶---Motion Planning之Speed Boundary》,又或者是构建SLT图《自动驾驶---Motion Planning之构建SLT Driving Corridor》,最终我们都是为了得到boundary的信息。 …

OpenCV实战:智能人脸识别打卡系统

1、介绍 目前&#xff0c;很多公司对员工的考勤同时通过打卡机或者钉钉之类的打卡软件&#xff0c;传统的员工打卡方式有很多&#xff0c;例如点名、签字、刷卡、指纹等等 然而随机机器视觉、计算机视觉的不断发展&#xff0c;算力的不断提升&#xff0c;人工智能对人脸检测的…

如何用联合(共用体)union验证系统大小端

一&#xff1a;思路 由联合体的特点&#xff0c;可知上图&#xff0c;char c 和 int i 共用四个字节&#xff0c;假设是小端&#xff0c;则由左到右是低地址到高地址&#xff0c;四个字节的内容如图所示01 00 00 00 代码展示&#xff1a; 如果第一个字节是1&#xff0c;则证明…