ucore lab6 调度管理机制

news2024/9/29 7:30:27

练习0:填写已有实验

具体更改的地方如下:

proc.c 中alloc_proc新增加内容

        proc->rq = NULL;
        list_init(&proc->run_link);
        //proc->run_link.next = proc->run_link.prev = NULL ;
        proc->time_slice = 0;
        proc->lab6_run_pool.left = proc->lab6_run_pool.right = proc->lab6_run_pool.parent = NULL;
        proc->lab6_stride = 0;
        proc->lab6_priority = 1;

链表初始化应该用list_init而不是空

priority要设置成1,不能设置为0,因为后续有的代码会除以这个数字,设置为0可能有bug

练习1: 使用 Round Robin 调度算法(不需要编码)

  • 请理解并分析sched_class中各个函数指针的用法,并结合Round Robin 调度算法描ucore的调度执行过程

    struct sched_class {
        // 算法名称
        const char *name;
        //初始化算法用数据
        void (*init)(struct run_queue *rq);
        // 当有进程设置为可调度时调用该函数放入就绪队列
        void (*enqueue)(struct run_queue *rq, struct proc_struct *proc);
        //在就绪队列中选出一个去运行然后在就绪队列中一出该信息
        void (*dequeue)(struct run_queue *rq, struct proc_struct *proc);
        //查看就绪队列中下一个是谁可以运行 
        struct proc_struct *(*pick_next)(struct run_queue *rq);
        //每次时钟中断时调用该函数,该函数需要处理,占用cpu时间片的剩余以及是否可调度
        void (*proc_tick)(struct run_queue *rq, struct proc_struct *proc);
    };
    

    ucore调度过程:

    void
    schedule(void) {
        bool intr_flag;
        struct proc_struct *next;
        local_intr_save(intr_flag);
        {
            current->need_resched = 0;
            if (current->state == PROC_RUNNABLE) {
             //   cprintf("insert into %p\n" , current);
                sched_class_enqueue(current);
            }
            if ((next = sched_class_pick_next()) != NULL) {
              //  cprintf("dequeue out %p\n" , next);
                sched_class_dequeue(next);
            }
            if (next == NULL) {
                next = idleproc;
            }
            next->runs ++;
            if (next != current) {
             //   cprintf("run %p \n" , next);
                proc_run(next);
            }
        }
        local_intr_restore(intr_flag);
    }
    

    ​ 首先就是创建俩个内核线程,一个执行初始化后是死循环调度,另一个则是创建子进程然后调度

    ​ 子线程通过do_fork 一出生就被挂到proc_list和hash_list这个链表上了,顺带调用wakeup_proc这个函数然 后就通过进程 调度算法的入队方式挂上去了

    void
    wakeup_proc(struct proc_struct *proc) {
        assert(proc->state != PROC_ZOMBIE);
        bool intr_flag;
        local_intr_save(intr_flag);
        {
            if (proc->state != PROC_RUNNABLE) {
                proc->state = PROC_RUNNABLE;
                proc->wait_state = 0;
                if (proc != current) {
                    sched_class_enqueue(proc);//这个函数就是挂到调度算法列表上
                }
            }
            else {
                warn("wakeup runnable process.\n");
            }
        }
        local_intr_restore(intr_flag);
    }
    
    

    接下来就是,要么时钟中断,要么进程自己放弃cpu然后同过调度算法切换另一个进程,而该算法逻辑是给进程分配一个时间片,然后每一个时钟中断检测一下进程的时间片,要是用完了,就通过调度算法,将进程放到就绪列表尾部。,然后等待下次调度

    static void
    RR_enqueue(struct run_queue *rq, struct proc_struct *proc) {
        assert(list_empty(&(proc->run_link)));
        list_add_before(&(rq->run_list), &(proc->run_link));//放到尾部
        if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice) {
            proc->time_slice = rq->max_time_slice;
        }
        proc->rq = rq;
        rq->proc_num ++;
    }
    
    

    其中trap.c中是通过run_timer_list函数来实现每次进程时间片减少的

    void
    run_timer_list(void) {
        bool intr_flag;
        local_intr_save(intr_flag);
        {
            list_entry_t *le = list_next(&timer_list);
            if (le != &timer_list) {
                timer_t *timer = le2timer(le, timer_link);
                assert(timer->expires != 0);
                timer->expires --;
                while (timer->expires == 0) {
                    le = list_next(le);
                    struct proc_struct *proc = timer->proc;
                    if (proc->wait_state != 0) {
                        assert(proc->wait_state & WT_INTERRUPTED);
                    }
                    else {
                        warn("process %d's wait_state == 0.\n", proc->pid);
                    }
                    wakeup_proc(proc);
                    del_timer(timer);
                    if (le == &timer_list) {
                        break;
                    }
                    timer = le2timer(le, timer_link);
                }
            }   
            sched_class_proc_tick(current);就走了个这个
        }
        local_intr_restore(intr_flag);
    }
    
    

    嗯~~ 一大堆代码,但是在lab6中好像没有用到if里的就走了 sched_class_proc_tick(current);来调用调度算法的RR_proc_tick这个

  • 请在实验报告中简要说明如何设计实现”多级反馈队列调度算法“,给出概要设计,鼓励给出详细设计

    嗯~ 不会

练习2: 实现 Stride Scheduling 调度算法(需要编码)

首先是这个BIG_STRIDE应该取多少值:

static int
proc_stride_comp_f(void *a, void *b)
{
     struct proc_struct *p = le2proc(a, lab6_run_pool);
     struct proc_struct *q = le2proc(b, lab6_run_pool);
     int32_t c = p->lab6_stride - q->lab6_stride;
     if (c > 0)
          return 1;
     else if (c == 0)
          return 0;
     else
          return -1;
}

下面来仔细分析一下吧:

​ 在具体实现时,有一个需要注意的地方:stride属性的溢出问题,在之前的实现里面我们并没有考虑 stride 的数值范围,而这个值在理论上是不断增加的,在 stride溢出以后,基于stride的比较可能会出现错误。比如假设当前存在两个进程A和B,stride属性采用16位无符号整数进行存储。当前队列中元素如下(假设当前运行的进程已经被重新放置进运行队列中):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jMnZa3UF-1690546856134)(https://chyyuu.gitbooks.io/ucore_os_docs/content/lab6_figs/image001.png)]

此时应该选择 A 作为调度的进程,而在一轮调度后,队列将如下:

image

可以看到由于溢出的出现,进程间stride的理论比较和实际比较结果出现了偏差。我们首先在理论上分析这个问题:令PASS_MAX为当前所有进程里最大的步进值。则我们可以证明如下结论:对每次Stride调度器的调度步骤中,有其最大的步进值STRIDE_MAX和最小的步进值STRIDE_MIN之差:

STRIDE_MAX – STRIDE_MIN <= PASS_MAX

有了该结论,在加上之前对优先级有Priority > 1限制,我们有STRIDE_MAX – STRIDE_MIN <= BIG_STRIDE,于是我们只要将BigStride取在某个范围之内,即可保证对于任意两个 Stride 之差都会在机器整数表示的范围之内。而我们可以通过其与0的比较结构,来得到两个Stride的大小关系。在上例中,虽然在直接的数值表示上 98 < 65535,但是 98 - 65535 的结果用带符号的 16位整数表示的结果为99,与理论值之差相等。所以在这个意义下 98 > 65535。基于这种特殊考虑的比较方法,即便Stride有可能溢出,我们仍能够得到理论上的当前最小Stride,并做出正确的调度决定。

所以最后应该取 0x7FFFFFFF 即 (((uint32_t)-1) / 2)

#define BIG_STRIDE 0x7FFFFFFF /* you should give a value, and is ??? */

然后就是

stride调度算法是抢占式的。在stride_enqueue中,每当就绪队列入队时都会为其分配一定的时间片,当线程运行的过程中发生时钟中断时则会通过stride_proc_tick函数扣减对应的时间片。当为线程分配的时间片扣减为0时,则会将线程的need_resched设置为1。

在trap中断处理函数中,当对应中断号的处理例程返回时会单独的检查need_resched的值,当发现为1时,则会触发schedule函数进行一次强制的线程调度,从而令当前时间片扣减为0的线程得以让出CPU,使其它的就绪线程能得到执行的机会。这也是stride调度算法被称为抢占式调度算法的原因:无论当前执行的线程是否主动的让出cpu,在分配的时间片用完之后,操作系统将会强制的撤下当前线程,进行一次调度,通过如下就可以看出

void trap(struct trapframe *tf)
{
    // dispatch based on what type of trap occurred
    // used for previous projects
    if (current == NULL)
    {
        trap_dispatch(tf);
    }
    else
    {
        // keep a trapframe chain in stack
        struct trapframe *otf = current->tf;
        current->tf = tf;

        bool in_kernel = trap_in_kernel(tf);

        trap_dispatch(tf);

        current->tf = otf;
        if (!in_kernel)
        {
            if (current->flags & PF_EXITING)
            {
                do_exit(-E_KILLED);
            }
            if (current->need_resched)
            {
                schedule();
            }
        }
    }
}

static void
stride_init(struct run_queue *rq)
{
     list_init(&rq->run_list);
     rq->lab6_run_pool = NULL;
     rq->proc_num = 0;
}
static void
stride_enqueue(struct run_queue *rq, struct proc_struct *proc)
{
     rq->lab6_run_pool = skew_heap_insert(rq->lab6_run_pool, &proc->lab6_run_pool, proc_stride_comp_f);
     if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice)
     {
          proc->time_slice = rq->max_time_slice;
     }
     proc->rq = rq;
     rq->proc_num++;
}

static void
stride_dequeue(struct run_queue *rq, struct proc_struct *proc)
{
     rq->lab6_run_pool =  skew_heap_remove(rq->lab6_run_pool, &proc->lab6_run_pool, proc_stride_comp_f);
     rq->proc_num--;
}

static struct proc_struct *
stride_pick_next(struct run_queue *rq)
{
     if (rq->lab6_run_pool == NULL)
          return NULL;
     struct proc_struct *p = le2proc(rq->lab6_run_pool, lab6_run_pool);
     if(p->lab6_priority == 0){
          p->lab6_stride += BIG_STRIDE;
     }else{
          p->lab6_stride += BIG_STRIDE / p->lab6_priority;
     }
     return p;
}

static void
stride_proc_tick(struct run_queue *rq, struct proc_struct *proc)
{
     if (proc->time_slice > 0){
          proc->time_slice--;
     }
     if (proc->time_slice == 0){
          proc->need_resched = 1;
     }
}

整体理解算法不难,因为人家的斜堆已经给出了,不需要自己实现

斜堆结构实现的就绪队列其入队、出队操作能达到O(logn)的对数复杂度,比其双向链表实现的就绪队列入队、出队效率O(n)要高出一个数量级

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

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

相关文章

探索网页原型设计:构建出色的用户体验

在当今数字化时代&#xff0c;用户对网页体验的要求日益提高。在网页设计过程中&#xff0c;扮演着至关重要的角色。通过网页原型设计&#xff0c;产品经理能够更好地展示和传达网页的整体布局、导航结构、元素位置和交互效果&#xff0c;从而使团队成员更清晰地了解设计意图&a…

2023年最佳咨询项目管理软件推荐,助力项目顺利进行!

咨询项目管理软件哪个好&#xff1f;用顾问的终极项目管理软件Zoho Projects的咨询软件管理知识库&#xff0c;简化流程让客户满意。 一、加强您的咨询项目管理 1、简化客户管理 通过多合一平台超越客户期望。管理客户请求、组织文件并保护机密数据。 2、跟踪您的整个投资组…

FinalShell docker容器mysql:中文显示乱码

FinalShell 中文显示乱码 在FinalShell 执行 select 语句发现表格&#xff0c;发现中文显示的问号 解决方法&#xff1a; mysql default-character-set utf8mb4 -p1234这个方法是&#xff0c;可以登录直接显示中文&#xff0c;但是在修改&#xff0c;输入中文还是不能解决。 如…

电赛培训(高频电路类赛题)学习总结

此篇文章基于全国电子设计大赛培训网的官网的高频电路类赛题总结的知识点。 高频电路赛题的相关理论知识点 &#xff08;1&#xff09;高频电路的单位 a.1kHz1000Hz不等于1KHz&#xff08;大写的K是错误的&#xff09; b.S是西门子&#xff0c;电导的单位&#xff0c;s是秒&…

5G时代的APP开发:机遇与挑战

APP开发是互联网行业中的重要组成部分&#xff0c;随着5G时代的到来&#xff0c;移动 APP开发也迎来了新的机遇和挑战。 5G时代不仅会为移动 APP开发带来新的发展机遇&#xff0c;也会给移动 APP开发带来新的挑战。对于企业和开发者而言&#xff0c;5G时代带来的机遇和挑战是并…

C语言程序设计——程序环境和预处理

一、翻译环境 &#xff08;1&#xff09;组成一个程序的每个源文件&#xff0c;通过翻译过程分布转换成对应的目标代码&#xff1b; &#xff08;2&#xff09;每个目标文件由链接器链接到一起&#xff0c;形成一个单一而完整的可执行程序&#xff1b; &#xff08;3&#xf…

基于ESP8266+网络调试助手点灯实验

文章目录 ESP8266串口wifi模块简介实验准备硬件接线程序下载注意事项总结 ESP8266串口wifi模块 简介 ESP8266 是一种低成本、高性能的 Wi-Fi 模块&#xff0c;内置了 TCP/IP 协议栈&#xff0c;它可以作为单独的无线网络控制器&#xff0c;或者与其他微控制器进行串口通信。它…

Linux之 Vim 搜索方式

方式一&#xff1a;快速搜索&#xff08;字符串完全匹配&#xff0c;区分大小写&#xff09; 格式&#xff1a; / 关键词 or &#xff1f; 关键词 /内容 #按回车键搜索 从上到下查找 ?内容 #按回车键搜索 从下到上查找 优点&#xff1a;快速定位到该关键字 回车之后&…

Spark(37):Streaming DataFrame 和 Streaming DataSet 创建

目录 0. 相关文章链接 1. 概述 2. socket source 3. file source 3.1. 读取普通文件夹内的文件 3.2. 读取自动分区的文件夹内的文件 4. kafka source 4.1. 导入依赖 4.2. 以 Streaming 模式创建 Kafka 工作流 4.3. 通过 Batch 模式创建 Kafka 工作流 5. Rate Source…

回归预测 | MATLAB实现PSO-GPR粒子群优化高斯过程回归多输入单输出回归预测

回归预测 | MATLAB实现PSO-GPR粒子群优化高斯过程回归多输入单输出回归预测 目录 回归预测 | MATLAB实现PSO-GPR粒子群优化高斯过程回归多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab基于PSO-GPR基于粒子群算法优化高斯过程回归的数据回归预…

Jmeter经过处理的变量设置全局变量

之前遇到一个问题&#xff1a;项目的某些接口是需要登录的而且登录不能多用户登录。模拟登录的时候传入请求头的其中一个参数比较复杂&#xff0c;需要登录后的某些参数拼接和加密后设置成全局变量&#xff0c;在请求头中调用这个变量&#xff0c;正常的设置全局变量的方法百度…

Java NIO 详解

Java 从1.4开始引入NIO&#xff08;New IO&#xff09;&#xff0c;是一种基于块&#xff08;Block&#xff09;的IO机制&#xff0c;也称为非阻塞IO。相比于传统的Java IO&#xff08;IO流&#xff09;方式&#xff0c;Java NIO提供了更快速、高效、灵活的IO操作。 Java NIO的…

一文让你彻底搞懂Mybatis之缓存机制

编译软件&#xff1a;IntelliJ IDEA 2019.2.4 x64 操作系统&#xff1a;win10 x64 位 家庭版 Maven版本&#xff1a;apache-maven-3.6.3 Mybatis版本&#xff1a;3.5.6 文章目录 一. 缓存是什么&#xff1f;二. 为什么要使用缓存&#xff1f;三. Mybatis中的缓存分哪几种&#…

自己实现MyBatis 底层机制--抽丝剥茧(上)

&#x1f600;前言 本篇博文是学习过程中的笔记和对于MyBatis底层机制的分析思路&#xff0c;希望能够给您带来帮助&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到…

Base64之间的相互转化

使用org.apache.ommons.codec.binary.Base64实现字符串和Base64之间的相互转化 字符串转化为Base64之间的相互转化一 //转化为Base64字符串 String strOld "Welcome to the new world"; base64EncodeStr Base64.encodeBase64String(strOld.getBytes()); System.o…

黑马点评项目学习笔记(15w字详解,堪称史上最详细,欢迎收藏)

黑马点评项目学习笔记 文章目录 黑马点评项目学习笔记前言项目搭建导入数据库初始化项目启动项目启动前端项目启动后端项目 基于Session实现短信验证码登录短信验证码登录配置登录拦截器数据脱敏 Session集群共享问题基于Redis实现短信验证码登录短信验证登录配置登录拦截器 店…

漏洞分析|Metabase 代码执行漏洞(CVE-2023-38646):H2 JDBC 深入利用

0x01 概述 最近 Metabase 出了一个远程代码执行漏洞&#xff08;CVE-2023-38646&#xff09;&#xff0c;我们通过研究分析发现该漏洞是通过 JDBC 来利用的。在 Metabase 中兼容了多种数据库&#xff0c;本次漏洞中主要通过 H2 JDBC 连接信息触发漏洞。目前公开针对 H2 数据库…

国产内存强势崛起,光威神条有神价,无套路闭眼可入

今年的DIY电脑市场终于摆脱了前两年的阴霾&#xff0c;从CPU到内存都有着充足的货源&#xff0c;而且价格靠谱&#xff0c;特别是国产存储品牌超级厚道&#xff0c;内存、硬盘等配件的价格基本都是大跳水&#xff0c;相比于去年&#xff0c;同样的价格能够买到容量和性能翻倍的…

ERROR 1064 - You have an error in your SQL syntax;

ERROR 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near (/, 少个逗号吧&#xff0c;以前开始写SQL&#xff0c;特别是修改SQL的时候容易出现这样错误。 而且自己也知道在附近…

SAP财务系统中的“复式记账法”

1. 前言 “复式记账法”是财务的基础知识&#xff0c;对于财务出身的小伙伴是so easy&#xff0c;但对于技术出身的同学&#xff0c;通常会被“借贷”关系弄的晕头转向。 本文会简明扼要的总结“复式记账法”的基本原理&#xff0c;并以采购和销售流程为例来介绍如何进行复式…