Linux如何实现进程O(1)调度

【Linux】深入理解进程的优先级(Linux 2.6版本O(1)调度算法) 然后CPU运行活动队列,不需要管新加入的进程,统一将新加入的进程放到过期队列中,过期队列其实就相当于是活动队列的一个镜像,新加入的进程就根据优先级映射到对应的数组下标,然后添加进对应位置的哈希桶里面,当CPU把运行队列里的内容都执行完了。而一个CPU通常一次只能调度一个进程,而操作系统里是有很多的进程需要被调度的,所以这就意味着,这些等待被调度的进程就要进行排队,而排队就要有先后的顺序,也就是进程的优先级,但是这样线性遍历的时间复杂度是一个O(N)级别的,我们操作系统响应是非常快的,这显然不合适。 阅读详情

      从运行队列的结构中,我们发现了两个有关优先级的数组,定义在kernel/sched.c中,名字叫prio_array,活跃的和过去的。

struct rt_prio_array {
    DECLARE_BITMAP(bitmap, MAX_RT_PRIO+1); /* include 1 bit for delimiter */
    struct list_head queue[MAX_RT_PRIO];
};

#define DECLARE_BITMAP(name,bits) \
    unsigned long name[BITS_TO_LONGS(bits)]


#define BITS_TO_LONGS(nr)    DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))


#define    DIV_ROUND_UP(x,y)    (((x) + ((y) - 1)) / (y))


DECLARE_BITMAP(bitmap, MAX_RT_PRIO+1);

等价于:unsigned longbitmap[MAX_RT_PRIO/8 + 1];

这两个数组是实现O(1)级算法的数据结构。优先级数组使可运行处理器的每一种优先级都包含一个相应的队列,而这些队列包含对应优先级上的可执行进程链表。结构中的优先级位图主要是为了帮助提高查找当前系统内拥有最高优先级的可执行进程的效率而设计的。关于结构中的定义常量,MAX_PRIO定义了系统拥有的优先级个数,默认值是140,BITMAP_SIZE是优先级位图数组的大小,值是5,这个是可以计算出来的。比如:位图数组的类型是unsigned long类型,长是32位,假定每一位代表一个优先级(4*32 = 128<140),就需要5个这样的长整型才能表示。所以,bitmap就正好含有5个数组项。

        开始时,位图成员的所有位都被清0,当某一优先级的进程开始准备执行时,位图中对应的位就置1,这样,查找系统中最高的优先级就变成了查找位图中被设置的第一位。鉴于优先级个数的定值性,查找的时间就不受系统到底有多少可执行进程的影响,是个定值。关于对位图的快速查找算法对应的函数是sched_find_first_bit().

        优先级数组的list_head队列是一个链表,这个链表包含了该处理器上相应优先级的全部可运行线程。要找到下一个要运行的任务,直接从该链表中选择下一个元素。对于给定的优先级,按轮转方式调度任务。优先级数组的计数器nr_active,它保存了该优先级数组内可执行进程的数目。

         下面是时间片的话题,一旦所有进程的时间片都用完会怎样了,总不能系统宕掉吧,好在操作系统提供了一种显式的方法来重新计算每个进程的时间片。典型就是循环访问每个进程。借用书上的一个例子:

for (系统中的每个任务){
           重新计算优先级
           重新计算时间片
}

       这种方法简单,但弊端很多:

1.可能耗费相当长的时间。
2.为了保护任务队列和每个进程的描述符,必要要用到锁的机制。这样做很明显会加剧对锁的争用,影响系统性能。
3.重算时间片的时机不确定。
4.实现显得很粗糙。

       现在采用的是新的调度方法,每个处理器拥有上面提到的两个优先级数组,活动数组上的进程都有时间片剩余,而过期数组中的进程都耗尽了时间片,当一个进程的时间片耗尽时,它会移至过期数组,但在此之前,时间片已经给它重新计算好了,重新计算时间片现在变的非常简单,只要在活动和过期数组之间来回切换就可以了。又因为数组是通过指针进行访问的,所以交换它们用的时间就是交换指针需要的时间。这个动作由schedule()完成:

struct prio_array *array = rq->active;
if(!array->nr_active){
        rq->active = rq->expired;
        rq->expired = array;
}

       这种交换是O(1)级调度程序的核心。O(1)级调度程序根本不需要从头到尾都忙着重新计算时间片,它只要完成一个两个步骤就能实现数组的切换,这种实现完美地解决了前面列举的所有弊端。linux的调度程序是在schedule()函数实现的。当内核代码想要休眠时,会直接调用该函数,如果哪个进程将被抢占,也会被唤起执行。调度的第一步是判断谁是优先级最高的进程:

struct task_struct *prev, *next;
struct list_head *queue;
struct prio_array array;
int idx;
prev = current;
array = rq->active;
idx = sched_find_first_bit(array->bitmpa);
queue = array->queue + idx;
next = list_entry(queue->next, struct task_struct, run_list);

       分析上面这段代码,首先在活动优先级数组中找到第一个被设置的bit位,该位对应着最高的可执行进程。然后,调度程序选择这个级别链表里的头一个进程。这个进程就是系统中优先级最高的可执行程序,也是马上就会被调度执行的进程。上面中的prev和next不等,说明被选中的进程不是当前进程,这时就要调用context_switch来进程切换。最后强调一点,不存在任何影响schedule()时间长短的因素,因为前边说过,所用的时间是恒定的。


O(n)、O(1)和CFS调度器(Linux内核调度) 一、前言 随着内核版本的演进,其源代码的膨胀速度也在递增,这让Linux的学习曲线变得越来越陡峭了。这对初识内核的同学而言当然不是什么好事情,满腔热情很容易被当头浇灭。我有一个循序渐进的方法,那就是先不要看最新的内核,首先找到一个古老版本的内核(一般都会比较简单),将其吃透,然后一点点的迭代,理解每个版本变更背后的缘由和目的,最终推进到最新内核版本。 本文就是从2.4时代的任务调度器开始,详细描述其实现并慢慢向前递进。当然,为了更好的理解Linux调度器设计和实现,我们在第二章给出了一些通用的概念。之后,我 阅读详情

相关推荐

linux系统的O(1)调度算法

该word详细介绍了O(1)调度的数据结构和调度算法以及完整的调度过程。可是我们对linux2.6.11的调度有个系统的认识

O(1)调度器:Linux2.6版本的核心算法

全局优先级,范围为0~139,数值越低,优先级越高将进程拆分成实时进程(0 ~ 99)和普通进程(100 ~ 139),更高优先级任务获得更多的时间片支持抢占,当任务状态变成TASK_RUNNING时,内核会检查其优先级是否比当前任务的优先级更高,如果是的话,则抢占当前正在运行的任务,切换到该任务实时进程使用静态优先级普通进程使用动态优先级,任务优先级会在其使用完自己的时间片后重新计算,内核会考虑它过去的行为,决定它的交互等级,交互型任务更容易得到调度。

嵌入式Linux内核的博客 795

从大疆智图到Cesium:3D Tiles模型加载与Web三维场景构建实战

本文详细介绍了如何利用大疆智图生成3D Tiles数据,并通过Cesium在Web端实现高效加载与三维场景构建。从大疆智图参数设置到Cesium环境配置,再到3D Tiles性能调优与高级功能实现,提供了一套完整的实战指南,帮助开发者快速掌握3D Tiles模型加载与Web三维场景构建技术。

weixin_30520015的博客 1285

O(1)调度算法

1.进程有两个优先级,一个是静态优先级,一个是动态优先级.静态优先级是用来计算进程运行的时间片长度的,动态优先级是在调度器进行调度时用到的,调度器每次都选取动态优先级最高的进程运行.静态优先级的计算:nice值和静态优先级之间的关系是:静态优先级=100+nice+20 而nice值的范围是-19~20,所以普通进程的静态优先级的范围是100~139 进程运行的时间片长度的计算:静态优先级<120,

liuxiaowu19911121的专栏 6412

从光耦隔离到稳定驱动:信捷伺服与STM32的硬件设计陷阱与救赎

本文深入探讨了信捷伺服驱动器与STM32微控制器配合时的硬件设计关键问题,包括光耦隔离电路选型、电源电压选择、拨码开关设置等核心环节。针对伺服驱动器的稳定驱动需求,提供了从电路设计到抗干扰策略的完整解决方案,帮助工程师规避常见陷阱,提升工业控制系统的可靠性。

gold8的专栏 312

超详细!新手必看STM32-DMA配置与实验(串口使用DMA发送数据)

DMA就是:直接存储器访问。DMA传输数据从一个地址空间复制到另一个地址空间,提供在外设和存储器或者存储器和存储器之间的高速数据传输。

qq_1033275663的博客 2万+

Linux源码剖析——O(1)调度算法

当一个普通进程被添加到运行队列时,会先计算其动态优先级,然后按照动态优先级的值来添加到对应优先级的队列中。Linux2.4版本使用的调度算法的时间复杂度为O(n),其主要原理是通过轮询所有可运行任务列表,然后挑选一个最合适的任务运行,所以其时间复杂度与可运行任务队列的长度成正比。每个进程都要一个动态优先级和静态优先级,静态优先级不会变化(进程创建时被设置),而动态优先级会随着进程的睡眠时间而发生变化。--p->time_slice) { // 减少时间片, 如果时间片用完。

白话机器学习 724

Windows非分页缓冲池内存泄漏实战:PoolMon与RAMMap高效排查指南

本文详细介绍了Windows系统中非分页缓冲池内存泄漏的实战排查方法。当系统出现卡顿且内存占用异常增长时,通过RAMMap工具快速定位非分页缓冲池异常,并利用PoolMon监控内核内存分配标签,精准锁定泄漏源。文章提供了从确认症状、安装工具到使用findstr命令溯源驱动文件的完整五步排查流程,并总结了常见泄漏标签与解决方案,帮助用户高效解决深层系统内存问题。

weixin_28455801的博客 1011

Linux内核--进程管理(十三)O(1)调度算法

Linux是一个支持多任务的操作系统,而多个任务之间的切换是通过 调度器 来完成,调度器 使用不同的调度算法会有不同的效果。Linux2.4版本使用的调度算法的时间复杂度为O(n),其主要原理是通过轮询所有可运行任务列表,然后挑选一个最合适的任务运行,所以其时间复杂度与可运行任务队列的长度成正比。而Linux2.6开始替换成名为 O(1)调度算法,顾名思义,其时间复杂度为O(1)。虽然在后面的版本开始使用 CFS调度算法(完全公平调度算法),但了解 O(1)调度算法 对学习Linux调度器还是有很大帮助的,

文艺小少年的博客 1090

PHP实现SM2算法(附完整源码)

PHP实现SM2算法(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 4025

PCL Trimmed ICP实现点云精配准【2025最新版】

博客长期更新,本文最新更新时间为:2025年6月25日。抄袭狗记得来抄最新代码哦,抄都抄成错的,不够丢人钱。

点云侠的博客 37万+

Linux进程调度算法之O(1)算法

Linux进程调度算法之O(1)算法 一.概述     在一个运行操作系统中的进程数一般都是大于CPU的数目,但是用户看起来好像这些进程是同时在运行的,其原因就是这些进程通过调度算法使每个进程每隔一段时间就放入CPU运行,该时间段非常短,给人的感觉就是这些进程在同时运行。因此需要一些规则来确定调度的时机,即调度策略。     调度策略有多种,常见的如:分时技术,给

lieye_leaves的专栏 3045

UltraScale系列底层结构(3)——URAM概述

在UltraScale+™ 器件中,除了我们在常规熟悉的BRAM资源以外,还额外提供了URAM(UltraRAM)资源。 UltraRAM 是一种单时钟同步操作的高密度存储器,每个 UltraRAM 块可存储 288 Kb 数据,配置为 4K x 72 位的存储单元,其容量是 Block RAM 的八倍。虽然 UltraRAM 内部使用单端口存储单元,但通过在一个时钟周期内顺序执行端口 A 和端口 B 的操作,实现了双端口功能。

apple_53311083的博客 2833

【Linux进程(四)】深入理解 Linux O(1) 调度器:双队列轮转与进程优先级机制——如何避免进程饥饿,实现公平且高效的进程调度

(–,一直到减为0,swap交换指针),Linux操作系统中的选择逻辑。先拿active指针,找到active——找到现在是在哪一个队列当中,nr_active确定当前有无进程:如果有,查bitmap表,确认数组下标,根据数组下标,找到一个进程。整个过程,时间复杂度几乎是O(1),这就是Linux内核之O(1)调度算法(结构决定算法)。

艾莉丝努力练剑的博客,感谢大家一直以来的对艾莉丝的支持和鼓励,uu们如果对C/C++方向感兴趣的话,可以通过艾莉丝主页的微信名片加艾莉丝的好友,和努力练剑的艾莉丝一起加油! 1461

图形推理2-四大类六提示

图形推理笔记2

小y的学习笔记 6744

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Linux进程第十四讲——进程调度器底层揭秘:位图与O(1)机制如何实现高效优先级调度

本文深入解析了Linux O(1)进程调度器的底层机制,重点介绍了位图(Bitmap)和优先级数组如何协同工作实现高效调度。传统调度器采用O(n)遍历方式,而O(1)调度器通过位图标记队列状态、优先级数组分类存储进程,结合双队列设计,实现了与进程数量无关的恒定调度时间。文章通过代码示例演示了位图如何快速定位最高优先级进程,并分析了优先级数组的结构设计,揭示了"调整优先级本质是改变进程在数组中的位置"这一核心原理,为理解Linux高效进程调度提供了底层视角。

Maple_land的博客 931

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

linux下的进程调度,Linux如何实现O(1)进程调度

Linux调度主要是在一个runqueue结构体上操作。runqueue结构体有一个prio_array结构体数组,该数组中有个两个prio_array结构体。prio_array结构体的定义如下:struct prio_array{int nr_active /* number of tasks in the queue */;unsigned long bitmap[BITMAP_SIZE];...

weixin_35947010的博客 208

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

吃透Linux进程(三):Linux 进程优先级与 O (1) 调度——从切换原理到内核实现

本文深入解析Linux进程调度与切换机制。进程切换通过保存/恢复上下文(TSS)实现,由时间片、进程退出或优先级变化触发。O(1)调度算法采用双队列(active/expired)、bitmap位图和双链表结构,确保切换时间恒定,同时解决进程饥饿问题。优先级机制并非绝对优先,而是兼顾效率与公平性,使高优先级进程更易获得CPU,但低优先级进程仍有机会执行。这些设计共同实现了Linux内核高效、公平的进程调度。

M1nat0_的博客 540

Linux如何实现O(1)进程调度

Linux调度主要是在一个runqueue结构体上操作。runqueue结构体有一个prio_array结构体数组,该数组中有个两个prio_array结构体。prio_array结构体的定义如下: struct prio_array { int nr_active /* number of tasks in the queue */; unsigned long ...

weixin_30532837的博客 91

第一次作业:关于Linux 2.6.20进程模型和O(1)调度器算法的分析

1.写在最前 本文基于 Linux Kernel 2.6.20 的源代码,分析的是本版本linux的进程模型和其O(1) 调度器的基本算法。 源码浏览地址: https://elixir.bootlin.com/linux/v2.6.20/source 2.关于进程 2.1进程的定义 从不同的角度,进程可以有不同的定义,比较经典的定义有: 1) 进程是程序的一次执行过程 2) 进程是一个...

dfh1539的博客 329

【Linux】深入理解“进程”:进程状态 -> Linux内核O(1)调度算法

在Linux系统中 “进程” 无处不在,那什么是进程呢?本篇文章将带你深入地理解进程。看看和你以前对进程的理解有什么出入。

Miun123的博客 1673

【Linux仓库】进程优先级及进程调度【进程·肆】

本文深入探讨了Linux系统的进程优先级和调度机制。文章首先介绍了PRI(进程优先级)和NI(nice值)的概念及其关系,解释了通过调整nice值(-20到19)来改变进程优先级的原理。随后分析了Linux内核的O(1)调度算法实现,重点讲解了活动队列和过期队列的双队列设计,以及通过bitmap提高调度效率的机制。文章还讨论了分时操作系统如何避免进程饥饿问题,详细阐述了通过交换活动队列和过期队列指针来保证公平调度的巧妙设计。最后,文章简要提及了进程调度的上下文切换过程和时间片机制。

egoist2023的博客 3556

【linux】进程调度:优先级、时间片与O(1)算法

进程为什么要把优先级设置为80+NI呢,我们更改进程后,直接放在当前的queue不太合适,调度器的效率会降低,与其它进程的公平性会失衡,所以设置一个NI,将进程所在的queue状态变为expired时,在改进程的优先级,将其链入相应的队列。

prettyxian的博客 1177

linux中O(1)调度算法与完全公平(CFS)调度算法

linux中O(1)调度算法与完全公平(CFS)调度算法

fangjian1204的专栏 6462

(7)Linux进程调度-O(1)调度算法

《(1)Linux进程调度》 《(2)Linux进程调度器-CPU负载》 《(3)Linux进程调度-进程切换》 《(4)Linux进程调度-组调度及带宽控制》 《(5)Linux进程调度-CFS调度器》 《(6)Linux进程调度-实时调度器》 《(7)Linux进程调度-O(1)调度算法》 目录 O(1)调度算法原理 prio_array优先级队列结构 runqueue结构 实时进程调度 普通进程调度 运行时间片计算 O(1)调度算法实现 时钟中断 任务调度 ...

RToax 1370
上一篇: 关于死锁产生的原因以及解决的办法
下一篇: C递归实现单向链表的反转
kernel_learner
博客等级 码龄15年 60粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值