C语言 操作系统实验 四种调度(最高响应比优先算法 HRN)

article/2025/6/20 12:36:33

注:

本文是四个调度算法的第一篇算法。

本文是根据CSDN上某一FCFS调度算法魔改来的,所以FCFS的算法不会发到网站。

我是个菜鸡,发文是为了纪念自己完成了代码,以及累计自己的经验。

如有知识错误或者算法有逻辑漏洞请各位大佬高抬贵手。

实验环境:win10、VS2019(C++)

PS:虽然是cpp文件,实际上是披着cpp的c

        运行前,首先要在预处理器上插入_CRT_SECURE_NO_WARNINGS,避免运行错误,在插入前要用;隔开。

        具体位置就在解决方案下面一栏(名字是自己起的),右键属性即可找到。

         这个核心代码相对于之前的最短优先算法SJF来说在判断单元节点到达时间上是一致的,只不过多了等待时间变量wait以及响应比计算函数hrn,内容相对稀少,所以本篇会详细介绍响应比hrn函数以及在SJF中没提到的其他模板函数。

        注:如果想了解SJF算法或是判断单元节点到达时间函数此处为链接:C语言 操作系统实验 四种调度(最短优先算法SJF)_key_149的博客-CSDN博客

        好了直接进正题

        最高响应比算法除了单元节点到达时间的思路外,只有一个核心:响应比的计算

        先上代码:

void hrn(JCB* p)//响应比计算函数,第一轮计算时wait == p->tr
{hr2 = hr;hr = (wait - p->tr) / p->tn;if (wait % p->tn){hr = hr + 1;}
}

        响应比 = 响应时间 / 预计执行时间

        响应时间 = 等待时间 + 预计执行时间

        综上:响应比 = 1 + 作业等待时间  / 预计执行时间

        而通过观察能发现,1是所有响应比都需要加的一个不变的常量,而在实际的代码编写当中可以省略,而关键的就在于会变的两个变量:等待时间、预计执行时间;

        接下来就要对这两个变量进行分析:

        预计执行时间:每个单元节点都有自己的预计执行时间当指针遍历某一单元节点时,就能获取当前节点的预计执行时间,而每个单元节点内的预计执行时间也是不会改变的。这样只需在遍历链表时直接计算响应比并进行记录便可。(在代码段中hr2代表的就是上一个进程的响应比,hr则是当前的响应比。)

        等待时间:在第一个单元节点输出时,此时等待时间为0,是没有响应比的。所以在对链表头进行比较时,仅需对提交时间进行比较即可。在链表输出一次后,等待时间wait便是当前节点的执行时间 + 提交时间。但要注意的是这个等待时间并不包含与下一个单元节点的提交时间相交的部分,很有可能某个进程在上一个进程执行时的任意时刻到达,所以要减去它的提交时间,即 (wait - p->tr)出现的原因。

        除了正常的公式计算,hrn函数中还包含了一个if语句,在C/C++中float类型的函数是不能被比较的,所以响应比的类型是int,而除出来的商也会是整数类型,在这种前提条件下极有可能出现响应比是小数的情况,进而使其和真正能整除的响应比混在一起,此时响应比+1以避免这种情况产生。

        通过对这两个变量的总结可以得知:

                1.在对链表头的的排序中,仅需要记录头结点的等待时间 + 执行时间;即wait = p->tr + p->tn;对于不断变换的头结点,wait也会相应的不断刷新。

                2.至于头结点后的单元节点的排序是按提交时间排序即可。(与SJF思路类似,以便分层)

                3.对于响应比会在输出每个单元节点后进行变化,所以响应比hrn函数应在遍历单元节点时逐一运行。

                4.wait函数是在单元节点释放前加上当前的执行时间(头结点例外)

        这样hrn的核心思路就捋清楚了,接下来上完整代码:

#include "stdio.h"
#include <stdlib.h>
#include <conio.h>
#define getpch(type) (type*)malloc(sizeof(type))
#define NULL 0//定义作业jcb结构体
struct jcb
{char name[100];int tr;//作业提交时间int tn;//作业运行时间int ts;//作业开始时间int tf;//作业完成时间float ti;//作业周转时间float wi;//作业带权周转时间int flag;//顺序struct jcb* link;
}*ready = NULL, * h, * p;
typedef struct jcb JCB;
JCB* first, * second, * last;int time = 0;
int num = 0; //作业数量
int sum = 0;//目前总时间
float eti = 0.0;
float ewi = 0.0;
int wait;//等待时间
int hr = 0;
int hr2 = 0;void hrn(JCB* p)//响应比计算函数,第一轮计算时wait == p->tr
{hr2 = hr;hr = (wait - p->tr) / p->tn;if (wait % p->tn){hr = hr + 1;}
}void linkpcb()//先将链表尾插法连接起来 //如果行不通就执行方案2 即将这些先按到达顺序排序后,通过dolevel进行分层,在用sort进行最短排序
{int insert = 0;if ((ready == NULL) || ((p->tr) <= (ready->tr) && p->tn < ready->tn)) /*提交 兼 提交 时间最小者,插入队首*/{p->link = ready;ready = p;wait = p->tr + p->tn;//计算首位的等待时间}else /* 进程比较提交时间,插入适当的位置中*/{first = ready;second = first->link;while (second != NULL){if ((p->tr) < (second->tr)) /*若插入作业比当前作业提交时间小,*/{p->link = second;//p插入到second前面first->link = p;second = NULL;//second只能在连接好的链表上运行insert = 1;}else /* 插入进程提交时间最高,则插入到队尾*/{first = first->link;second = second->link;}}if (insert == 0) first->link = p;//循环到最后若依旧没有比second大的,则first此时是p的前一个}
}void dolevel()
{if (second->tr <= sum){sum = sum + second->tn;}else{sum = second->tr + second->tn;second->flag = 0;}second = second->link;}void destroy() /*建立作业撤消函数(进程运行结束,卸载作业)*/
{free(p);
}
void running() /* 建立作业就绪函数(作业运行时间到,置就绪状态*/
{if (p->link == NULL)destroy(); /* 调用destroy函数*/
}
void disp(JCB* pr) /*建立作业显示函数,用于显示当前作业*/
{printf(" %s\t", pr->name);printf(" %d\t", pr->tr);printf(" %d\t", pr->tn);printf(" %d\t", pr->ts);printf(" %d\t", pr->tf);printf(" %-0.2f\t", pr->ti);printf(" %-0.2f\t", pr->wi);printf("\n");}
void check() /* 建立作业查看函数 */
{//累加变量time 记录当前用时 !!!if (time >= p->tr){p->ts = time;p->tf = p->ts + p->tn;time = p->tf;}else{p->ts = p->tr;p->tf = p->ts + p->tn;time = p->tf;}p->ti = p->tf - p->tr;p->wi = p->ti / p->tn;//!!!}void sort() /* 建立对作业进行提交时间排列函数,分出第一层*/
{first = ready;//first归位p = ready;last = ready;if (last->flag == 0)//置1{last->flag = 1;}while ((last->flag == 1 && last->link != NULL))//一层中找到最小的  最后一个会被强制退出{hrn(last);if (hr > hr2){p = last;}last = last->link;if (last->link == NULL)//在最后一个被踢出前先进行比较{hrn(last);if (hr > hr2){p = last;}}}wait = wait + p->tn;hr = 0;hr2 = 0;last = p->link;if (ready == p)//防止最小可能是头单元节点造成ready成空指针{ready = ready->link;}first->link = last;p->link = NULL;check();disp(p);eti += p->ti;ewi += p->wi;running();
}void input() /* 建立作业控制块函数*/
{int i;printf("\n 请输入即将运行的进程总数目");scanf("%d", &num);for (i = 0; i < num; i++){p = getpch(JCB);printf("\n 输入作业名:");scanf("%s", p->name);printf("\n 输入作业提交时间:");scanf("%d", &p->tr);printf("\n 输入作业运行时间:");scanf("%d", &p->tn);printf("\n");p->ts = 0;p->tf = 0;p->ti = 0.0f;p->wi = 0.0f;p->link = NULL;p->flag = 1;wait = p->tr;linkpcb();}p->link = NULL;printf("\n 名称 \t 提交 \t 运行 \t 开始 \t 完成 \t 周转\t 带权周转 \n");second = ready->link;//第二个sum = ready->tr + ready->tn;p = ready;check();//对最先到达那个进行特殊输出disp(p);eti += p->ti;ewi += p->wi;ready = ready->link;p->link = NULL;running();hrn(p);//记录当前响应比值do{dolevel();} while (second->link != NULL);//second此时是末尾节点for (i = 1; i < num; i++){sort(); /* 调用sort函数,对提交顺序和执行时间进行比较*/}}int main() /*主函数*/
{input();//此时已经按提交顺序排好了eti /= num;ewi /= num;printf("\n该组作业平均周转时间为:%0.2f", eti);printf("\n该组作业平均带权周转时间为:%0.2f", ewi);return 0;
}

        这里需要注意一点:dolevel函数在运行时是计算当前时间sum的,但 sum != wait;sum的作用是为链表分层,而wait是随响应比变化的单元节点等待时间。这也就意味着响应比小的节点不一定是提交时间早的节点。

        到此HRN的思路就截止了,接下来是链表模板函数的分析:

        1.结构体jcb,在结构体中定义了每一结构体内的项目,如提交时间、等待时间等。在结构体设置完后要设置结构体变量,此处是指针变量* ready、* p等。

        2.typedef struct jcb JCB;此行代码是用typedef定义一个类型,类型是结构体struct,结构体的名字jcb,而这个用typedef定义的名为jcb的结构体怎么表示呢?在此处命名为JCB。同样的JCB定义的指针first、second以及last。而理论上这三个指针变量和ready、p是一样的。

        3.定义这个类型,还需要做一些准备工作:#define getpch(type) (type*)malloc(sizeof(type))
此行代码宏定义了申请即getpch,申请的类型是type类型,即2中的typedef,malloc是向机器申请空间,空间的大小为sizeof测量的type类型的大小。

        4.在input函数中设置循环,对每个申请的空间进行初始化以及输入工作。

        5.check()函数负责计算周转时间和带权周转时间,周转时间有两个算法:

                执行时间 + 等待时间;

                完成时间 - 提交时间;

        带权周转时间公式:周转时间 / 执行时间;

        6.在disp输出后,p->NULL;作为一个标记,而running()则是通过p->NULL作为判断可以运行销毁函数destory;而running函数本身是作为进程工作而设立的函数,此处仅用于调用销毁函数。


http://chatgpt.dhexx.cn/article/QtMXx84P.shtml

相关文章

处理动态图的图神经网络

汤吉良老师团队发表于2020年的SIGIR 《Streaming Graph Neural Networks》论文阅读笔记 背景&#xff1a; 图能够很好的表示实际数据&#xff08;如社交网络&#xff0c;传输网络&#xff09;。利用神经网络建模图结构数据&#xff0c;学习特征表示&#xff0c;改善图相关任务…

ImageView加载网络图片

使用第三方的库Glide加载网络图片 首先去下载一个glide的包 下载地址&#xff1a;https://github.com/bumptech/glide/releases/download/v4.7.0/glide-full-4.7.0.jar 我这里用的是glide-full-4.7.0 下载好之后直接复制到app\libs下面&#xff0c;然后点同步&#xff0c;可…

图神经网络的池化操作

图神经网络有两个层面的任务&#xff1a;一个是图层面&#xff08;graph-level&#xff09;&#xff0c;一个是节点&#xff08;node-level&#xff09;层面&#xff0c;图层面任务就是对整个图进行分类或者回归&#xff08;比如分子分类&#xff09;&#xff0c;节点层面就是对…

网络图的绘制方法详细讲解

网络拓扑图形如网络结构,并且由箭头线条、节点、路线三个因素组成。网络工程师在绘制网络图时,为了展示网络传输方式和途径,通常将网络节点设备和通信介质进行物理布局。网络拓扑图的结构类型有:星型、环型、树型、总线型、网状、分布式结构、等等。 网络图一般用处 在计算…

网络拓扑图怎么画 详细教程

大数据时代&#xff0c;如何更好地去运营、呈现数据&#xff0c;并从其中发掘出更多信息成为了人们探索的方向。网络拓扑图就是一种非常有用的信息化图表&#xff0c;这种网状关系呈现出来的利器可以使我们把想要传递的信息更加清晰、有逻辑的呈现在别人的眼前。 1. 什么是网络…

图神经网络及其应用

Graph Neural Networks and its applications 摘要 以图形形式构建的数据存在于生物化学、图像处理、推荐系统和社会网络分析等多个领域。利用预处理技术在图结构数据上训练机器学习模型有几种方法&#xff0c;但它们缺乏完全适应数据集和手头任务的灵活性。图神经网络允许创…

[概念]神经网络的种类(前馈神经网络,反馈神经网络,图网络)

随着神经网络的不断发展&#xff0c;越来越多的人工神经网络模型也被创造出来了&#xff0c;其中&#xff0c;具有代表性的就是前馈型神经网络模型、反馈型神经网络模型以及图网络. 1.前馈型神经网络模型 前馈神经网络&#xff08;Feedforward Neural Network ,FNN&#xff09…

java实现下载网络图片到本地

文章目录 前言一、示例二、代码 1.代码示例2.运行结果总结 前言 当我们在网络上看到自己想要保存的照片&#xff0c;有的网站设置了权限&#xff0c;不能保存情况下&#xff0c;我们可以借助Java的文件流读取网络上的图片&#xff0c;并保存到本地。 一、示例 比如豆瓣话题第…

用python实现数字图片识别神经网络--启动网络的自我训练流程,展示网络数字图片识别效果

上一节&#xff0c;我们完成了网络训练代码的实现&#xff0c;还有一些问题需要做进一步的确认。网络的最终目标是&#xff0c;输入一张手写数字图片后&#xff0c;网络输出该图片对应的数字。由于网络需要从0到9一共十个数字中挑选出一个&#xff0c;于是我们的网络最终输出层…

android中的ImageView,ImageView加载网络图片

android中的ImageView&#xff0c;ImageView加载网路图片 在布局文件中加入标签&#x1f3f7;️ <ImageViewandroid:layout_width"300dp"android:layout_height"200dp"android:background"#111111"android:id"id/imageView_pic1"a…

css用网络图片做背景图片,网络编程css为图片设置背景图片

网络编程css为图片设置背景图片 广告 CSS的功能是非常强大的&#xff0c;对于元素的表现以及页面的布局&#xff0c;都提供了非常强大的功能&#xff0c;这主要在于我们灵活的运行&#xff0c;在这方面提供了丰富且富含价值的各种教程与信息。对于图片的使用&#xff0c;其实更…

硬核!一文梳理经典图网络模型

作者 | Chilia 哥伦比亚大学 nlp搜索推荐 整理 | NewBeeNLP 图神经网络已经在NLP、CV、搜索推荐广告等领域广泛应用&#xff0c;今天我们就来整体梳理一些经典常用的图网络模型&#xff1a;DeepWalk、GCN、Graphsage、GAT&#xff01; 1. DeepWalk [2014] DeepWalk是…

图网络入门

目录 图网络基础知识 Graph Embedding 基础图网络模型 GCN GraphSAGE GAT 其他图网络模型 图网络基础知识 图网络的应用场景有很多&#xff0c;目前在工业界的主要应用是在推荐和风控领域&#xff0c;其他包括社交网络、交通网络、化学分子、3D点云等也都有一些应用。 …

图网络(Graph Network, GN)

目录 什么是图网络&#xff1f; 图网络框架 图网络内容 图的定义 GN块的内部结构 图网络中的关系归纳偏置 什么是图网络&#xff1f; 图网络是2018年由DeepMind、谷歌大脑、MIT和爱丁堡大学等公司和机构的27位科学家共同发表的论文《Relational inductive biases, deep …

图网络分类以及一些通用框架

图网络大体分类 递归图神经网络&#xff08;Recurrent Graph Neural Networks&#xff09;图卷积神经网络&#xff08;Graph Convolution Networks&#xff09;图注意力网络&#xff08;Graph Attention Networks&#xff09;图自编码器&#xff08;Graph Auto-encoder&#x…

泛型的使用和作用

概述 泛型的使用 多态与泛型 两者要一致&#xff0c;Animal 和cat要一致 泛型的作用 提高 Java 程序的类型安全 通过前面的学习我们知道&#xff0c; 在集合中可以添加 Object 类型的对象&#xff0c; 如果在不使用泛型的情况下定义了一个 ArrayList 对象&#xff0c; 那 么各…

java泛型(java泛型的作用)

java泛型的基本使用是什么&#xff1f; add("test1"); String test1 (String)strList。get(0); System。out。println("Test 1 : " test1); } public void testGeneric() { List strList new ArrayList(); strList。 Java泛型的规则和限制有哪些呢&…

java泛型的作用及实现原理

一、泛型的介绍 泛型是Java 1.5的新特性&#xff0c;泛型的本质是参数化类型&#xff0c;也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中&#xff0c;分别称为泛型类、泛型接口、泛型方法。 Java泛型被引入的好处是安全简单。在Java …

泛型(一)--泛型的作用和定义

一、泛型的作用 泛型是.net中十分常见的一种特性。它是在.net 2.0的时候加入。那为什么要在.net 2.0的时候加入泛型这个特性呢&#xff1f;我们首先来看一段代码。 using System;namespace 泛型{class Program{static void Main(string[] args){int iParameter 1;string sPar…