本科实验报告
课程名称: 操作系统B
实验项目: 操作系统实验
实验地点: 逸夫楼
专业班级:软件1009班 学号:2010004611
学生姓名: 李瑞
指导教师: 王华夏
2012 年 12月 6日
实验一 几种操作系统的界面
一、目的和要求
(1)目的:
本实验的目的是熟悉1—2种操作系统的界面,在熟练使用机器的基础上,能了解各种操作命令和系统调用在系统中的大致工作过程。也就是通过操作系统的外部特征,逐步深入到操作系统的内部实质内容中去。
(2)要求:
能熟练的在1—2种操作系统的环境下工作,学会使用各种命令,熟悉系统提供的各种功能,主动而有效地使用计算机。
熟悉系统实用程序的调用方法和各种系统调用模块的功能和作用
二、实验内容和原理
在某种操作系统的环境下建立、修改、运行、打印源程序和结果,最后撤消一个完整的程序。
三、主要仪器设备
ProBook 6450b ubuntu
四、操作方法与实验步骤
代码:
#include <stdio.h>
#include <errno.h>
#include <semaphore.h>
#include <fcntl.h>
#define SEM_NAME "mysem"
#define OPEN_FLAG O_RDWR|O_CREAT
#define OPEN_MODE 00777
#define INIT_V 0
static sem_t *sem = NULL;
static void mysem(char *str)
{
int i = 0;
while('\0' != str[i])
{
printf("%c\n", str[i++]);
sleep(1);
}
}
int main(void)
{
pid_t pid = -1;
int ret = -1;
int status = -1;
//创建一个命名信号量
sem = sem_open(SEM_NAME, OPEN_FLAG, OPEN_MODE, INIT_V);
//创建子进程
pid = fork();
if(-1 == (ret = pid))
{
perror("fork failed: ");
goto _OUT;
}
if(0 == pid)
{
mysem("abcd");
//V操作
sem_post(sem);
}
if(0 < pid)
{
//P操作
sem_wait(sem);
mysem("1234");
//等待子进程结束
wait(&status);
//删掉在系统创建的信号量
sem_unlink(SEM_NAME);
//彻底销毁打开的信号量
sem_close(sem);
}
_OUT:
return ret;
}
五、 实验结果与分析
六、讨论、心得
通过本实验,熟练了Linux操作系统的使用,了解了编译的基本步骤和指令:如何进入vi的命令模式,进入输入模式如何操作,末行模式指令,保存退出,命令模式命令等等基础的指令;使用时不再像开始那么生疏了。
实验二 进程调度程序设计
一、目的和要求
(一) 目的
进程是操作系统最重要的概念之一,进程调度是操作系统的主要内容,本实验要求能独立地用高级语言编写一个进程调度程序,调度算法可任意选择或自行设计,本实验可加深对进程调度和各种调度算法的理解。
(二) 要求
1. 设计一个有几个进程并发执行的进程调度程序,每个进程由一个进程控制块(PCB)表示,进程控制块通常应包括下述信息:进程名,进程优先数,进程需要运行的时间,占用CPU的时间以及进程的状态等,且可按照调度算法的不同而增删。
2. 调度程序应包含2—3种不同的调度算法,运行时可以任选一种,以利于各种方法的分析和比较。
3. 系统应能显示或打印各进程状态和参数的变化情况,便于观察。
二、实验内容和原理
1. 题目 本程序可选用优先数法或简单轮转法对五个进程进行调度。每个进程处于运行R(run)、就绪W(wait)和完成F(finish)三种状态之一,并假定起始状态都是就绪状态W。
为了便于处理,程序中进程的运行时间以时间片为单位计算。各进程的优先数或轮转时间片数、以及进程需要运行的时间片数,均由伪随机数发生器产生。
进程控制块结构如表2-1所示:
表2-1 PCB
进程控制块链结构如图2-1所示:
RUN HEAD TAIL
…
图2-1 进程控制块链结构
其中:RUN—当前运行进程指针;
HEAD—进程就绪链链首指针;
TAIL—进程就绪链链尾指针。
2. 算法与框图 程序框图如图2-2所示。
图2-2 进程调度框图
(1)优先数法。 进程就绪链按优先数大小从大到小排列,链首进程首先投入运行。每过一个时间片,运行进程所需运行的时间片数减1,说明它已运行了一个时间片,优先数也减3。理由是该进程如果在一个时间片中完成不了,优先级应降低一级。接着比较现行进程和就绪链链首进程的优先数,如果仍是现行进程高或者相同,就让现行进程继续运行,否则,调度就绪链链首进程投入运行。原运行进程再按其优先数大小插入就绪链,且改变它们对应的进程状态,直至所有进程都运行完各自的时间片数。
(2)简单轮转法。 进程就绪链按各进程进入的先后次序排列,链首进程首先投入运行。进程每次占用处理机的轮转时间按其重要程度登入进程控制块中的轮转时间片数记录项(相应于优先数法的优先数记录项位置)。每过一个时间片,运行进程占用处理机的时间片数加1,然后比较占用处理机的时间片数是否与该进程的轮转时间片数相等,若相等说明已到达轮转时间,应将现运行进程排到就绪链末尾,调度链首进程占用处理机,且改变它们的进程状态,直至所有进程完成各自的时间片。
三、主要仪器设备
ProBook 6450b ubuntu
四、 操作方法与实验步骤
程序清单:
#include <stdio.h>
#include <stdlib.h>
#define furthest 5
struct process /*PCB STRUCTURE*/
{ int id;
int priority;
int cputime;
int alltime;
char state;
int next; }prochain[furthest-1];
int procnum;
int rand();
int algo;
int run,head,tail,j;
main() /*MAIN PROGRAM*/
{ agan: printf(“type the algorithm is (1:RR,2:PRIO):”);
scanf(“%d”,&algo);
if (algo==2)
{ printf(“output of priority.\n”);
init();
prisch();}
else
{ if (algo==1)
{ printf(“output of round robin.\n”);
init();
timesch();}
else
{ printf(“try again,please\n”);
goto agan;}
}
for (j=1;j<=40;j++)
{ printf(“=”); }
printf(“\n\n”);
for (j=1;j<=40;j++)
{ printf(“=”); }
printf(“\n\n”);
printf(“system finished\n);}
print() /*PRINT THE RUNNING PROCESS,WAITING
QUEUE AND PCB SEQUENCE LIST*/
{ int k,p;
for (k=1;k<=40;k++)
printf(“=”);
printf(“\nrunning proc. ”);
printf(“waiting queue.”);
printf(“\n %d ”,prochain[run].id);
p=head;
while(p!=0)
{ printf(“%5d”,p);
p=prochain[p].next;}
printf(“\n”);
for (k=1;k<=40;k++)
printf(“=”);
printf(“\n”);
printf(“ id “);
for (k=1;k<furthest+1;k++)
printf(“%5d”,prochain[k].id);
printf(“\n”);
printf(“priority ”);
for (k=1;k<furthest+1;k++)
printf(“%5d”,prochain[k].priority);
printf(“\n”);
printf(“cputime ”);
for (k=1;k<furthest+1;k++)
printf(“%5d”,prochain[k].cputime);
printf(“\n”);
printf(“alltime ”);
for (k=1;k<furthest+1;k++)
printf(“%5d”,prochain[k].alltime);
printf(“\n”);
printf(“state ”);
for (k=1;k<furthest+1;k++)
printf(“%5c”,prochain[k].state);
printf(“\n”);
printf(“next ”);
for (k=1;k<furthest+1;k++)
printf(“%5d”,prochain[k].next);
printf(“\n”);
}
insert(int q) /*INSERT A PROCESS*/
{ int p,s;
p=head;
s=prochain[head].next
while((prochain[q].priority<prochain[s].priority)&&(s!=0))
{ p=s;
s=prochain[s].next;}
prochain[p].next=q;
prochain[q].next=s;
}
insert2() /*PUT A PROCESS ONTO THE TAIL OF THE QUEUE*/
{ prochain[tail].next=run;
tail=run;
prochain[run].next=0;
}
init() /*CREATE A WAITING QUEUE*/
{ int i;
head=0;
if (alog==2)
{ for (i=1;i<furthest+1;i++)
{ prochain[i].id=i;
prochain[i].priority=(rand()+11)%41;
prochain[i].cputime=0;
prochain[i].alltime=(rand()+1)%7;
prochain[i].state=’W’;
prochain[i].next=0;
if(prochain[i].priority<prochain[head].
priority)&&(head!=0))
insert(prochain[i].id);
else
{ prochain[i].next=head;
head= prochain[i].id;}
}
}
else
{ for (i=1;i<furthest+1;i++)
{ prochain[i].id=i;
prochain[i].priority=(rand()+1)%3+1;
prochain[i].cputime=0;
prochain[i].alltime=(rand()+1)%7;
prochain[i].state=’W’;
prochain[i].next=(i+1)%(furthest+1);}
head=1;
tail=furthest;
prochain[furthest].next=0;
}
run=head;
prochain[run].state=’R’;
head=prochain[head].next;
prochain[run].next=0;
print();
}
prisch() /*THE PROCESS WITH PRIO ALGORITHM*/
{ while(run!=0)
{ prochain[run].cputime+=1;
prochain[run].priority-=3;
prochain[run].alltime-=1;
if(prochain[run].alltime==0)
{ prochain[run].state=’F’;
prochain[run].next=0;
if(head!=0)
{ run=head;
prochain[run].state=’R’;
head=prochain[head].next;}
else
{ prochain[0].id=prochain[run].id;
run=0;}
}
else
{ if((prochain[run].priority< prochain[head].
priority)&&(head!=0))
{ prochain[run].state=’W’;
insert(run);
run=head;
prochain[run].state=’R’;
head= prochain[head].next;}
}
print();
}
}
timesch() /*THE PROCESS WITH RR ALRORITHM*/
{ while(run!=0)
{ prochain[run].alltime-=1;
prochain[run].cputime+=1;
if(prochain[run].alltime==0)
{ prochain[run].state=’F’;
prochain[run].next=0;
if(head!=0)
{ run=head;
prochain[run].state=’R’;
head=prochain[head].next;}
else
{ prochain[0].id=prochain[run].id;
run=0;}
}
else
{ if((prochain[run].cputime==prochain[run].
priority)&&(head!=0))
{ prochain[run].state=’W’;
prochain[run].cputime=0;
insert2();
run=head;
prochain[run].state=’R’;
head=prochain[head].next;}
}
print();
}
}
五、 实验结果与分析
六、讨论、心得
通过编写长长的代码,以及编译运行,观察结果,对进程调度算法有了更深一层的印象。
本实验选用优先数法或简单轮转法对五个进程进行调度。每个进程处于运行R(run)、就绪W(wait)和完成F(finish)三种状态之一。
优先数法是预先确定个进程的优先数,系统将cpu的使用权赋予就绪队列中具备最高优先级的就绪进程。这种方法又分为静态与动态的。静态的缺点是:在整个进程运行过程中不会改变,不能反映实时的信息,所以动态的是对静态的改进,在整个进程运行过程中各个进程的优先级在不断的改变。
简单轮转法是就绪队列中的所有进程以相等的速度向前推进。
实验三 存储管理程序设计
一、目的和要求
(一) 目的
存储管理的主要功能之一是合理地分配主存空间。请求页式管理是一种常用的虚拟存储管理技术。
本实验的目的是通过请求页式存储管理中页面置换算法的模拟设计,来了解虚拟存储技术的特点,掌握请求页式存储管理的页面置换算法。
(二) 要求
模拟页式虚拟存储管理中硬件的地址转换和缺页中断的处理过程,并用先进先出调度算法(FIFO)处理缺页中断。
二、实验内容和原理
(1) 为了装入一个页面而必须调出一页时,如果被选中调出的页面在执行中没有修改过,则不必把该页重新写到磁盘上(因磁盘上已有副本)。因此,在页表中可以增加是否修改过的标志,当执行“存”指令、“写”指令时把对应页的修改标志置成“1”,表示该页修改过,否则为“0”,表示该页未修改过。页表格式如表3-1所示。
表3-1 页表格式
(2) 设计一个地址转换程序来模拟硬件的地址转换和缺页中断处理过程。当访问的页在主存时则形成绝对地址,但不去模拟指令的执行,可用输出转换后的绝对地址来表示一条指令已完成。当访问的页不在主存时则输出“*该页页号”来表示硬件产生了一次缺页中断。模拟地址转换的程序流程如图3-1所示。
(3) 编制一个FIFO页面调度程序。FIFO页面调度算法总是先调出作业中最先进入主存的那一页,因此,可以用一个数组来构成页号队列。数组中每个元素是该作业已在主存的页面号,假定分配给作业的主存块数为m,且该作业开始的m页已装入主存,则数组可由m个元素组成:
P[0],P[1],…,P[m-1]
它们的初值为
P[0]∶=0,P[1]∶=1,…,P[m-1]∶= m-1
用一指针k指示当要装入新页时应调出的页在数组的位置,k的初值为“0”。
图3-1 地址转换和FIFO页面调度流程
当产生缺页中断后,操作系统总是选择P[k]所指出的页面调出,然后执行
P[k]∶=要装入的新页页号
k∶=(k+1)mod m
在实验中不必实际地启动磁盘执行调出一页和装入一页的工作,而用输出“OUT调出的页号”和“IN要装入的新页页号”来模拟一次调出和装入的过程。模拟程序的流程见图3-1。
(4)假定主存的每块长度为1024个字节,现有一个共7页的作业,其副本已在磁盘上。系统为该作业分配了4块主存块,且该作业的第0页至第3页已经装入主存,其余3页尚未装入主存,该作业的页表见表3-2所示。
表3-2 作业的页表
如果该作业依次执行的指令序列如表3-3所示。
表3-3 作业依次执行的指令序列
依次执行上述的指令序列来调试你所设计的程序(仅模拟指令的执行,不必考虑指令序列中具体操作的执行)
(5)为了检查程序的正确性,可自行确定若干组指令序列,运行设计的程序,核对执行结果。
三、主要仪器设备
ProBook 6450b ubuntu
四、操作方法与实验步骤
程序清单:
#include "stdio.h"
#define size 1024
struct plist
{
int number;
int flag;
int block;
int modify;
int location;
};
Struct plist p1[7]={{0,1,5,0,010},{1,1,8,0,012},{2,1,9,0,013},{3,1,1,0,021},{4,0,-1,0,022},{5,0,-1,0,023},{6,0,-1,0,121}};
struct ilist
{
char operation[10];
int pagenumber;
int address;
};
struct ilist p2[12]={{"+",0,70},{"+",1,50},{"*",2,15},{"存",3,21},
{"取",0,56},{"-",6,40},{"移位",4,53},{"+",5,23},
{"存",1,37},{"取",2,78},{"+",4,1},{"存",6,84}};
main()
{
Int i,lpage,pflage,replacedpage,pmodify;///////////////////////////////////////////////////////////////////////////////////
int p[4]={0,1,2,3};
int k=0;
int m=4;
long memaddress;
printf("\n 操作 \t 页号 \t页内地址 标志 绝对地址 修改页号 页架号 绝对地址\n");
for(i=0;i<12;i++)
{
lpage=p2[i].pagenumber;
pflage=p1[lpage].flag;
if(pflage==0)
{
replacedpage=p[k];
pmodify=p1[replacedpage].modify;
p[k]=lpage;
k=(k+1)%m;
p1[lpage].flag=1;//标志位改为1
p1[lpage].block=p1[replacedpage].block;
p1[replacedpage].block=-1;
p1[replacedpage].flag=0;
p1[replacedpage].modify=0;
}
memaddress=p1[lpage].block*size+p2[i].address;
if(p2[i].operation=="save")
p1[lpage].modify=1;
printf(" %s\t",p2[i].operation);
printf(" %d\t",p2[i].pagenumber);
printf(" %d\t",p2[i].address);
printf(" %d\t",pflage);
if(pflage==1)
printf(" %d\t",memaddress);
else
printf(" *%d\t",p2[i].pagenumber);
if(pflage==1)
printf(" \t");
else
printf(" %d->%d\t",p2[i].pagenumber,replacedpage);
printf(" %d\t",p1[lpage].block);
printf(" %d\t",memaddress);
printf("\n");
}
}
五、实验结果与分析
六、讨论、心得
通过请求页式存储管理中页面置换算法的模拟设计实验,进一步了解虚拟存储技术的特点,理解了请求页式存储管理的页面置换算法,用先进先出调度算法处理缺页中断。做实验时应该先理解实验原理,清楚自己每一步是在干什么,出错以后可以知道错误的原因以便自己很快改正。