实验任务4 驱动调度
基本要求:要求使用随机量,比如进程的数量(在1-10之间)、进程磁盘访问数量和地址等是随机量,要求计算磁盘平均访问时间或寻道距离。
1、基本理论
磁盘驱动调度:磁盘可以被多个进程共享的设备,如果有多个进程请求访问磁盘时,为了保证信息的安全,系统的每一时刻只允许一个进程进入磁盘进行I/O操作,别的进程需要等待。磁盘需要采用一种适当的算法,使每个进程对磁盘的平均寻道时间最小。磁盘调度的最终目标是使磁盘的平均寻道时间最少。磁盘是可供多个进程共享的设备,由于磁盘是串行IO,当有多个进程要求访问磁盘时,应采用一种调度算法,以使各进程对磁盘的平均访问时间尽可能的少。
驱动调度的性能指标:使用率,是指磁盘处理I/O的时间百分比。过高的使用率(比如超过80%),通常意味着磁盘I/O存在性能瓶颈。饱和度,是指磁盘处理I/O的繁忙程度。过高的饱和度,意味着磁盘存在严重的性能瓶颈。当饱和度为100%时,磁盘无法接受新的I/O 请求。
IOPS (Input/Output Per Second),是指每秒的I/О请求数。
吞吐量是指每秒的I/O请求大小,即每秒磁盘I/O的流量,磁盘写入加上读出数据的大小。单位为bps。
响应时间是指I/O请求从发出到收到响应的间隔时间。
驱动调度的几种算法:
(1)先来先服务(FCFS)算法。即先来的请求先被响应。FCFS策略为我们建立起一个随机访问机制的模型,但是假如用这个策略反复响应从里到外的请求,那么将会消耗大量的时间。FCFS也被看作是最简单的磁盘调度算法。
(2)最短寻道时间优先(SSTF)算法。要求访问的磁道,与当前磁头所在的磁道距离最近,以使每次的寻道时间最短。
(3)扫描调度(SCAN)算法。该算法不仅考虑到欲访问的磁道与当前磁道间的距离,更优先考虑的是磁头当前的移动方向。例如,当磁头正在自里向外移动时,SCAN算法所考虑的下一个访问对象,应是其欲访问的磁道,既在当前磁道之外,又是距离最近的。这样自里向外的访问,直至再无更外的磁道需要访问时,才将磁道换向自外向里移动。这时,同样也是每次选择这样的进程来调度,也就是要访问的当前位置内距离最近者,这样,磁头又逐步地从外向里移动,直至再无更里面的磁道要访问,从而避免了出现“饥饿”现像。
(4)循环扫描(C-SCAN)算法。当磁头刚从里向外移动而越过了某一磁道时,恰好又有一进程请求访问此磁道,这时,该里程就必须等待,为了减少这种延迟,CSCAN算法规定磁头单向移动,而本实验过程中我们所设计的是磁头从里向外移动,而从外向里移动时只须改方向而已,本实验未实现。但本实验已完全能演示循环扫描的全过程。
2、实验的目的
通过模拟驱动调度功能,加深关于共享设备管理和驱动调度的理解,明确不同调度算法的优缺点,加深操作系统基本原理的理解。
3、实验平台
编译环境:IDEA JDK-1.8
编译语言:Java
4、主要数据结构和操作
(1)定义函数部分主要代码
Random r = new Random(); // 生成随机数
Scanner in = new Scanner(System.in);
System.out.println("请输入当前磁道的个数,随机生成磁道号:");
int s = in.nextInt(); // 磁道个数
OS[] a = new OS[100];// new了100个引用
System.out.println("生成的随机磁道号为:");
for (int i = 0; i < s; i++) {
a[i] = new OS(); // 分配内存空间
a[i].Set_x(r.nextInt(200));
System.out.println(a[i].Get_x() + " ");
}
(2)先来先服务(FCFS)算法部分主要代码
for (int i = 0; i < s; i++)
System.out.print(a[i].Get_x() + " ");
for (int i = 0; i < s; i++) {
a[i].Set_y(now - a[i].Get_x());
if (a[i].Get_y()< 0)
a[i].Set_y(-a[i].Get_y()); // 外围磁道与最里面磁道的距离
sum += a[i].Get_y();
now = a[i].Get_x();
}
(3)最短寻道时间优先(SSTF)算法部分主要代码
// 找出每一次与当前磁头位置距离最小的磁道
for (int i = 0; i < s; i++) {
a[i].Set_y(now - a[i].Get_x()); // 记录每一个磁道与磁头的距离
if (a[i].Get_y() < 0) {
a[i].Set_y(-a[i].Get_y());
}
}
for (int k = 0; k < s; k++) { // 将差值最小的放置最前面
for (int i = 0; i < s - 1; i++) {
for (int j = i + 1; j < s; j++)
if (a[i].Get_y() > a[j].Get_y()) {
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
System.out.print(a[k].Get_x() + " ");
sum += a[k].Get_x();
}
(4)扫描调度(SCAN)算法部分主要代码
for (int i = 0; i < s; i++) // 对访问磁道按由小到大顺序排序
for (int j = i + 1; j < s; j++)
if (a[i].Get_x() > a[j].Get_x()) {
temp = a[i].Get_x();
a[i].Set_x(a[j].Get_x());
a[j].Set_x(temp);
}
// 输出排好序的磁盘
int flag = 1;
for (int i = 0; i < s; i++) { //先从比磁头大的磁道开始,磁头向外移动
if (a[i].Get_x() > now) {
a[i].Set_y(a[i].Get_x() - now);
now = a[i].Get_x();
sum += a[i].Get_y();
flag++;
System.out.print(a[i].Get_x() + " ");
}
}
for (int i = s - flag; i >= 0; i--) {
a[i].Set_y(now - a[i].Get_x());
now = a[i].Get_x();
sum += a[i].Get_y();
System.out.print(a[i].Get_x() + " ");
}
()循环扫描(C-SCAN)算法部分主要代码
for (int i = 0; i < s; i++) // 对访问磁道按由小到大顺序排列
for (int j = i + 1; j < s; j++)
if (a[i].Get_x() > a[j].Get_x()) {
temp = a[i].Get_x();
a[i].Set_x(a[j].Get_x());
a[j].Set_x(temp);
}
//在当前磁道的右侧进行寻道操作
int flag = 0;
for (int i = 0; i < s; i++) {
if (a[i].Get_x() > now) {
a[i].Set_y(a[i].Get_x() - now);
now = a[i].Get_x();
sum += a[i].Get_y();
flag++;
System.out.print(a[i].Get_x()+" ");
}
}
for (int i = 0; i < s - flag; i++) { //进行移动磁盘序列号的寻道距离的计算
a[i].Set_y(now - a[i].Get_x());
if (a[i].Get_y()<0){
a[i].Set_y(-a[i].Get_y());
}
System.out.print(a[i].Get_x()+" ");
now = a[i].Get_x();
sum += a[i].Get_y();
}
5问题分析和解决
问题一:在进行扫描算法的代码编写与调试过程中,先进行磁盘号大的调度,进行完磁盘大的调度后,需要返回磁盘号比当前磁盘号小但是最接近的那个磁盘号,在进行过程中得到结果与自己手算的结果出入很大。
解决方案:通过调试我发现由于设置的标志量出现了问题,在进行磁盘号由大往小的返回调度时,返回的第一个磁盘号为第一个比当前磁盘号大的磁盘号,因此我发现这是因为标志位的值小1造成的,因此在初始化标志位的时候我把标志的值设置为了1。
问题二:在进行循环扫描算法的时候,在返回最小磁盘号再进行往最接近当前磁盘号且比当前磁盘号小的那个磁盘号时调度时,得出的结果比自己手算的结果小。
解决方案:通过调试,我发现是由于标志位的设置出了问题,这里我受到扫描算法的思维禁锢导致我再进行标志位的初始化时依旧把标志位设置为了1,这是错误的,因为我把磁盘号存在一个对象数组中,而且数组的起始位为0,所以在进行标志位的设置需要把标志位设置为0,这样解决了这个问题了。
6实验结果分析
先来先服务算法:
优点:公平;如果请求访问的磁道比较集中的话,算法性能还算过的去。
缺点:如果有大量进程竞争使用磁盘,请求访问的磁道很分散,则FCFS在性能上很差,寻道时间长。
最短寻道时间优先算法:
优点:性能较好,平均寻道时间短。
缺点:可能产生“饥饿”现象,以本题为例,本例中,如果在处理119号磁道的访问请求时又来了个125号磁道的访问请求,处理125号磁道的访问请求时又来了一个119号磁道的访问请求。如果有源源不断的119号、125号磁道的访问请求到来的话,其他磁道的访问请求就永远得不到满足,从而产生“饥饿”现象。
扫描调度算法:
优点:性能较好,平均寻道时间较短,不会产生饥饿现象
缺点:SCAN算法对于各个位置磁道的响应频率不平均(比如:假设此时磁头正在往右移动,且刚处理过188号磁道,那么下次处理54号磁道的请求就需要等磁头移动很长一段距离;而响应了188号磁道的请求之后,很快又可以再次响应188号磁道的请求了)
循环扫描算法:
优点:比起SCAN算法来说,对于各个位置磁道的响应频率很均匀。
7实验小结
本实验的目的是为了让我们观察,体会操作系统磁盘调度的方法。要做出实验首先要了解算法的过程,最短寻道算法(sstf)就是总会从等待访问者中挑选寻找时间最短的那个请求先执行的,无论是在磁盘的里面还是外面,只要离他近的就先执行。而循环扫描算法就和我们平时坐的电梯有点类似,先往一个方向运行。这次的实验里对总道数的计算一开始理解的不够全,尤其是循环扫描算法,但用电梯实例来想象模拟后我很快弄明白了怎么回事。
这次的实验有了了实验一的经历所以做起来更加的轻松了,但是程序仍然有很多改进的地方,资源可以更加节约,算法也还有优化的余地,但是时间和精力有限,我会在课余的时间加深对磁盘调度的理解同时加上其他的算法,例如扫描调度(SCAN)算法,先来先服务(FCFS)等。这次实验也让我感觉到了算法才是程序的灵魂,没有好的算法就像巧妇难为无米之炊,即使有再好的工具和基本功没有算法和基本思想也是不可行的。