操作系统实验报告【优秀9篇】
在生活中,大家逐渐认识到报告的重要性,我们在写报告的时候要注意语言要准确、简洁。其实写报告并没有想象中那么难,差异网的小编精心为您带来了9篇《操作系统实验报告》,希望能对您的写作有一定的参考作用。
操作系统实验报告 篇一
一、实验目的
模拟文件系统实现的基本功能,了解文件系统的基本结构和文件的各种管理方法,加深理解文件系统的内部功能及内部实现。通过用高级语言编写和调试一个简单的文件系统,模拟文件管理的工作过程,从而对各种文件操作命令的实质内容和执行过程有比较深入的了解。
二、实验内容和要求
编程模拟一个简单的文件系统,实现文件系统的管理和控制功能。要求本文件系统采用两级目录,即设置主文件目录[MFD]和用户文件目录[UED]。另外,为打开文件设置运行文件目录[AFD]。设计一个10个用户的文件系统,每次用户可保存10个文件,一次运行用户可以打开5个文件,并对文件必须设置保护措施。在用户程序中通过使用文件系统提供的Create、open、read、write、close、等文件命令,对文件进行操作
三、实验主要仪器设备和材料
硬件环境:IBM-PC或兼容机
软件环境:C语言编程环境
四、实验原理及设计方案
1、实验原理
运用二级目录思想来模拟文件系统。
为每个用户建立一个单独的用户文件目录UFD。这些文件目录具有相似的结构,它由用户文件的文件块组成。此外,在系统再建立一个主文件目录MFD;在主文件目录中,每个用户目录都占有一个目录项,其目录项中包含文件名和指向该文件目录文件的指针。
3、程序流程图
五、结果过程及截图
系统的使用简要说明:
登陆:
create命令新建文件
file5
Create失败
命令删除文件
Delete失败:
open打开命令,及write命令来追加文件
Read命令和close命令关闭
对没打开的文件读写:
六、所遇困难的解决以及心得体会
本实验的代码长度的最常的,但是原理很简单,并且实验要求的是模拟,所以大大降低了实验的难度,在操作系统课中我们还有一个课程设计,也是文件管理系统,但是要比这个难很多,通过这个实验,我对文件管理系统的运行机制有了深入的了解,因为要做好这个实验必须要懂得文件管理系统的'架构,这也迫使我认认真真的研究了操作系统中关于文件管理这一章的内容。同时这个程序也为以后的课程设计奠定了基础,并且很多的代码也是可以重用的。
七、思考题
1、文件系统要解决哪些问题?
答:要解决文件和用户是否同名,文件创建数是否超额,所要求文件是否存在等问题。
2、什么是文件目录?什么是文件目录中包含哪些信息?目前广泛采用的目录结构形式是哪种?
答:文件目录就是用来展示一个用户的所有文件的一个表格,文件目录应包含文件名,保密码,文件大小,目前广泛采用的目录结构形式是多级目录形式。
八、源代码
#include
#include
#include
#include
#define NULL 0
typedef struct mdf{//MDF结构体
char username[20];//用户名 char filename[20];//文件名
struct mdf *next;
}MDF;
typedef struct ufd{//UFD结构体
char filename[20];//文件名
int protect;//文件保护码 unsigned int length;//文件长度
struct ufd *next;
}UFD;
typedef struct afd{//AFD结构体
char filename[20];//文件名 int protect;//文件保护码 unsigned int point;//文件读写指针
struct afd *next;
}AFD;
MDF *pmdf;//全局链表头指针
UFD *pufd;
AFD *pafd;
char UserUFD[20];//已经登陆成功的用户名
void initMDF()//初始化MDF表
{
FILE *fp; pmdf= (MDF*)malloc(sizeof(MDF)); MDF *p = pmdf; if((fp = fopen(
} exit(1); } while (!feof(fp)){//把MDF文件中的内容装入链表 } p->next = (MDF*)malloc(sizeof(MDF)); p = p->next; fscanf(fp,
void printUFD()//打印MDF表 {
UFD *p = pufd->next; puts(
}
void initUFD(char *name)//初始化UFD表 {
FILE *fp; pufd= (UFD*)malloc(sizeof(UFD)); UFD *p = pufd; if((fp = fopen(name,
} p->next = NULL; fclose(fp);
int checkuser()//检测登陆的用户名 {
} char username[20]; while(1){ puts(
void initAFD()//初始化AFD {
pafd = (AFD*)malloc(sizeof(AFD)); pafd->next = NULL; }
bool create()//创建文件命令 {
char filename[20]; UFD *p = pufd->next; AFD *pa = pafd; puts(
} } if(!p->next) break; p= p->next; p->next = (UFD*)malloc(sizeof(UFD)); p=p->next; strcpy(p->filename, filename); p->protect = 2; p->length = 0; p->next = NULL; while(pa->next){//创建文件后加入到AFD } pa=pa->next; pa->next = (AFD*)malloc(sizeof(AFD)); pa = pa->next; strcpy(pa->filename ,filename); pa->protect = 2; pa->point = 0; pa->next = NULL; return 1;
}
bool _()//删除文件命令 {
char filename[20]; puts(
puts(
return 0;
}
bool open()//打开文件命令
{
char filename[20]; unsigned int protect; puts(
} } puts(
void close()//关闭文件命令
{
char filename[20]; UFD *pu = pufd->next; puts(
int read()//读文件命令
{
char filename[20]; unsigned int length; AFD *p = pafd->next;
} puts(
int write()//写文件命令
{
}
char filename[20]; unsigned int length; AFD *p = pafd->next; puts(
void destroy()//释放内存
{
}
void saveUFD()//保存UFD文件
{
}
void bye()//推出系统
{ FILE *fp; UFD *p = pufd->next; if((fp = fopen(UserUFD,
UFD *pu = pufd->next; while(pa){ if(pa->protect == 2){ while(pu){ } saveUFD(); printUFD(); destroy(); } } if(strcmp(pa->filename, pu->filename) == 0){ } pu->length = pa->point; break; pu = pu->next; pa =pa->next;
}
void operate()//命令识别
{
while(1){ char command[20]; char name[][8] = {
}
} return; }else puts(
void print()
{
puts(
int main()
{
}
print(); initMDF(); checkuser(); initAFD(); operate();)//命令识别 return 0;
操作系统实验报告 篇二
许昌学院
《操作系统》实验报告书
学号:姓名:闫金科班级:成绩:
5006140057
14物联网工程 2016年02月实验一 Linux的安装与配置
一、实验目的
1、 熟悉Linux系统的基本概念,比如Linux发行版、宏内核、微内核等。 2. 掌握Linux系统的安装和配置过程,初步掌握Linux系统的启动和退出方法。 3. 熟悉Linux系统的文件系统结构,了解Linux常用文件夹的作用。
二、实验内容
1、 从网络上下载VMware软件和两个不同Linux发行版镜像文件。 2. 安装VMware虚拟机软件。
3、 在VMware中利用第一个镜像文件完成第一个Linux的安装,期间完成网络信息、用户信息、文件系统和硬盘分区等配置。
4、 在VMware中利用第二个镜像文件完成第二个Linux的安装,并通过LILO或者GRUB解决两个操作系统选择启动的问题。
5、 启动Linux系统,打开文件浏览器查看Linux系统的文件结构,并列举出Linux常用目录的作用。
三、 实验过程及结果
1、启动VMware,点击新建Linux虚拟机,如图所示:
2、点击下一步,选择经典型,点击下一步在选择客户机页面选择Linux,版本选择Red Hat Enterprise Linux 5,如图所示:
3、点击下一步创建虚拟机名称以及所要安装的位置,如图所示:
4、点击下一步,磁盘容量填一个合适大小,此处选择默认值大小10GB,如图所示:
5、点击完成,点击编辑虚拟机设置,选择硬件选项中的CD-ROM (IDE.。.)选项,在右侧连接中选择“使用ISO镜像(I)”选项,点击“浏览”,找到Linux的镜像文件,如图所示:
6点击确定按钮后,点击启动虚拟机按钮,来到Linux的安装界面,如图所示:
7、到此页面之后,等待自动检测安装,如图所示:
8、等到出现如图所示页面后点击“skip”按钮,跳过检测,直接进入安装设置界面,如图所示:
9、安装设计界面如图所示:
10、点击Next按钮进入设置语言界面,设置语言为“简体中文”,如图所示:
11、点击Nest按钮进入系统键盘设置按钮,设置系统键盘为“美国英语式”,如图所示:
12、点击下一步按钮,弹出“安装号码”对话框,选择跳过输入安装号码,如图所示:
13、按照提示,一直点击下一步按钮,如图所示:
14、到设置最后一步,点击下一步按钮进入开始安装Red Hat Enterprise Linux Sever界面,如图所示:
15、安装完成后,进入欢迎界面,按照提示点击前进按钮知道进入Linux桌面,如图所示:
16、安装成功的Linux系统桌面如图所示,桌面包含五个图标,分别为:计算机、jk’s Home、回收站、RHEL/5.3 i386DVD。
四、实验总结
通过安装虚拟机等操作让我认识到Linux这系统一些基本特点,本次试验学会了安装虚拟机并且使用虚拟机安装操作系统,掌握了红帽Linux系统的安装和配置过程,以及对镜像ISO文件的使用,有别于我们机器上使用的系统,通过虚拟机这个软件还可以在已有系统的基础上使用其他操作系统。安装过程中一定要注意选择版本的时候要选择Red Hat Enterprise Linux 5版本,否则安装不能成功。自己动手成功的安装了Linux系统,自己对Linux的学习产生更大的兴趣。
实验二 Linux操作系统的运行模式
一、实验目的
1、 熟悉Linux系统终端工作环境的使用,了解Linux命令的格式,使用学会利用常用的Linux命令来完成系统的管理和维护。
2、 了解X-Windows的特点,熟悉Linux图形用户接口的使用,掌握GNOME桌面环境的基本操作。
3、 了解和掌握在Linux环境下安装软件包的方法,如QQ for Linux等用软件的安装方法。
二、实验内容
1、 启动Linux系统打开虚拟终端界面,使用Linux的在线帮助指令man或help获得ls、uname、date、cal、mkdir、cp等Linux命令的帮助手册,了解这些命令的具体使用方法。同时,也可以通过执行“命令名 –help”来显示该命令的帮助信息,如“ls –help”,试用这些命令。
2、 通过uname命令的执行,查看并给出相关系统信息:操作系统的名称、系统域名、系统CPU名称等。
3、 在主目录下创建一个名为myetc的子目录,将/etc目录下与网络相关的文件和子目录拷贝到该目录,并将这些文件的执行权限设置为可执行。
4、 在主目录/home下创建目录program、music 和temp,然后在program下建立目录java和C,列出完成该过程的所有命令。
5、 在图形界面环境中,查看GNOME桌面的面板和桌面,设置GNOME,包括屏幕保护程序、更改背景和指定关联程序等。 6. 实现对光盘的加载和访问,然后卸载。
三、实验过程及结果
1、打开终端,输入 【ls –help】来查看【ls】指令的使用方法,同理查看uname、date 、cal、mkdir、cp的使用方法。
2、在终端中输入【uname –a】显示操作系统名系统cpu名和系统域名
3、重启系统,用【root】用户名进入系统,以获得权限。在终端中输入【mkdir myetc】,在主目录下创建【myrtc】的目录,【ls】查看是否创建。输入【cd.。】返回至【/】文件,输入【cp –r etc root/myetc】讲etc中内容复制到myetc中,进入myetc文件【ls】查看。输入
【chmod u+x etc】赋予文件可执行的权限,输入【ll】查看。
4、在home下,输入【mkdir {program,music,temp}】,可在home下创立这三个目录,输入【ls】查看。在program下输入【mkdir{java,C}】,可创立java和C两个目录,【ls】查看。
5、在桌面上方选择【系统】-【首选项】,即可设置屏幕保护程序和更改背景和指定关联程序
5、在桌面上可见看到有CD光盘,双击浏览,右键【弹出】即卸载。
四、实验总结和体会
Linux的指令系统是学习Linux操作系统很重要的一部分,指令系统相当于在Windows操作系统下的doc,可以省去图形化界面。通过这次的实验让我了解了Linux的强大功能,了解到Linux有许多方便快捷的设置基本配置的方法,这使我更喜欢上Linux的使用。在使用指令的过程中,有时候对文件的操作需要一定的权限,这时需要在登陆时用户名使用【root】,而不是我们在安装时使用的用户名,这样就获得了管理员权限,可以对一些系统文件进行操作。
实验三 Linux应用软件与系统管理
一、实验目的
1、 了解集成办公软件,掌握利用的套件来完成文档和图片的处理。
2、 了解Linux网络管理的知识,熟悉Linux网络配置的方法,掌握在Linux环境下配置Web服务器和ftp服务的方法。
二、实验内容
1、 配置Linux系统的网络环境,安装FTP和Web服务器,并配置相关的属性,利用FTP实现WINDOWS和Linux之间的数据交换。
2、 利用FTP程序上传自己的照片到FTP服务器,利用OpenOffice的文字处理工具OpenOffice Writer制作一份表格形式的个人简历。个人简历中至少包含学号、姓名、性别、专业、照片和学习经历等内容,并保存为网页格式(html格式)。 3. 将个人简历网页设置为WEB服务器的首页,然后在客户端利用浏览器访问WEB服务器,查看效果。
4、 通过读取proc文件系统,获取系统各种信息(如主机名、系统启动时间、运行时间、版本号、所有进程信息、CPU使用率等),并以比较容易的方式显示。
三、实验过程及结果
1、配置网络环境:在 (服务。cmd)。里面进行以下操作:在服务里选择3按回车
完成后,可在本地连接看到VMware已连接上网络
在虚拟机设置中设置以太网网络连接方式为
网关地址填虚拟机的网管,IP地址设为虚拟机的一个子网:
四、总结:
在linux系统下,make是我们经常用到的编译命令,所以关于make代码和他的操作指令一定要记清楚。所以,熟练掌握了make和makefile工具之后,源码安装软件就变的像windows下安装软件一样简单。
实验四 进程控制与管理
一、实验目的
1、 掌握GCC编译器的用法,学会利用GCC编辑器来编辑C语言程序,学会利用GDB调试器来调试C语言程序。
2、 理解进程和程序的区别和联系,
3、 掌握在Linux环境下观察进程运行情况和CPU工作情况的命令。 4. 了解fork()系统调用,掌握利用fork()创建进程的方法。
5、 了解Linux系统其他与进程相关的系统调用,如exec、wait和exit等。 6. 了解Linux常用的进程通信机制。
二、实验内容
1、 利用Linux的进程管理命令ps、top来监视和跟踪进程,体会进程和程序的关系。 2. 利用Linux的文字编辑器编写文件复制的C语言程序,并用gcc编译该程序,然后运行该程序。
3、 编写一段程序,使用系统调用fork( )创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示'a',子进程分别显示字符'b'和字符'c'。试观察记录屏幕上的显示结果,并分析原因。
4、 修改上述程序,每一个进程循环显示一句话。子进程显示'daughter „'及'son „„',父进程显示 'parent „„',观察结果,分析原因。 5. 用fork( )创建一个进程,再调用exec( )用新的程序替换该子进程的内容。
三、实验过程及结果
1、利用Linux的进程管理命令ps、top来监视和跟踪进程,体会进程和程序的关系。 从用户身份切换到ROOT身份
输入命令 ps 查看进程
输入命令 top 跟踪进程
2、利用Linux的文字编辑器编写一个计算机100个自然数和的C语言程序,并用gcc编译该程序,然后运行该程序。
创建一个。C文件 并进入进行编辑
用GCC 进行编译,再查看文件,发现产生执行文件 a.out
执行这个可执行文件得到结果5050
1、 编写一段程序,使用系统调用fork( )创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示'a',子进程分别显示字符'b'和字符'c'。试观察记录屏幕上的显示结果,并分析原因。
穿件一个。C文件 并进行编写程序代码
反复执行2次该程序
可以看出两次执行的结果 a b c 出现的顺序不同,原因是,3个进程的输出次序是随机的,并不会按规定的顺序出现,所以会出现上述结果。
4、修改上述程序,每一个进程循环显示一句话。子进程显示'daughter „'及'son „„',父进程显示 'parent „„',观察结果,分析原因。 重新修改代码
执行这段程序
原分析:
因和之前一样,可以看出执行的结果 3个单词出现的顺序不同,原因是, 3个进程的输出次序是随机的,并不会按规定的顺序出现,所以会出现上述结果。
5、用fork( )创建一个进程,再调用exec( )用新的程序替换该子进程的内容。
编写代码
执行的结果
结果表明 execl 替代了son的内容
四、实验总结和体会
这个实验考察的是进程之间存在很多可能性以及对编辑器的使用。本次实验学习了在linux环境下用gcc编译器运行c语言程序,在linux环境下编写程序用到了vi编辑器,知道了该编辑器也需要各种命令来操作。编写C语言程序时用到了fork()函数,再调用execl()用新的程序替换该子进程的内容。
实验五 进程调度模拟程序的设计与实现
一、实验目的
1、 了解进程调度的概念,掌握常用进程调度算法的原理。 2. 掌握Linux程序设计编辑、编译和调试的技巧。
二、实验内容
1、 编写程序实现进程调度调度算法先来先服务、优先级高优先和时间片轮转调度算法。(编程语言不限)
2、 输入数据,输出运行结果。
三、实验过程及结果
1先来先服务
#i nclude
struct { int id;
float ArriveTime; float RequestTime; float StartTime; float EndTime; float RunTime; float DQRunTime; int Status; }arrayTask[4]; GetTask() { int i; float a;
for(i=0;i<4;i++) {arrayTask[i]。id=i+1; printf(“input the number”);
printf(“input the the ArriveTime of arrayTask[%d]:”,i); scanf(“%f”,&a);
arrayTask[i]。ArriveTime=a;
printf(“input the RequestTime of arrayTask[%d]:”,i); scanf(“%f”,&a);
arrayTask[i]。RequestTime=a; arrayTask[i]。StartTime=0; arrayTask[i]。EndTime=0; arrayTask[i]。RunTime=0; arrayTask[i]。Status=0;
} }
int fcfs()
{
int i,j,w=0;
for(i=0;i<4;i++)
{
if(arrayTask[i]。Status==0)
{
t=arrayTask[i]。ArriveTime;
w=1;
}
if(w==1)
break;
}
for(i=0;i<4;i++)
{
if(arrayTask[i]。ArriveTime t=arrayTask[i]。ArriveTime; } for(i=0;i<4;i++) { if (arrayTask[i]。ArriveTime==t) return i; } } int sjf() { int i,x=0,a=0,b=0; float g; for(i=0;i<4;i++) { if(arrayTask[i]。Status==1) {g=arrayTask[i]。EndTime; x=1; } } if(x==0){ t=arrayTask[0]。ArriveTime; for(i=0;i<4;i++) { if(arrayTask[i]。ArriveTime t=arrayTask[i]。ArriveTime; a=i; } } return a; } else { for(i=0;ig) g=arrayTask[i]。EndTime; } for(i=0;i<4;i++) { if(arrayTask[i]。Status==0&& arrayTask[i]。ArriveTime<=g) { t=arrayTask[i]。RequestTime; a=i; b=1; } /*判断有没有进程在前个进程完成前到达*/ } if(b!=0) /*有进程到达则按SJF*/ { for(i=0;i<4;i++) { if(arrayTask[i]。Status==0&&arrayTask[i]。ArriveTime<=g&&arrayTask[i]。RequestTime return a; } else{ /*否则按FCFS*/ for(i=0;i<4;i++) {if(arrayTask[i]。Status==0) t=arrayTask[i]。ArriveTime; } for(i=0;i<4;i++) { if(arrayTask[i]。Status==0&&arrayTask[i]。ArriveTime return a; } } } new(int s) /*定义执行进程后相关数据的修改*/ { int i,g=0; for(i=0;i<4;i++) { if(arrayTask[i]。Status==0) continue; else { g=1; break; } } if(g==0) /*当处理的是第一个未执行的进程时执行*/ { arrayTask[s]。StartTime=arrayTask[s]。ArriveTime; arrayTask[s]。EndTime=arrayTask[s]。RequestTime+arrayTask[s]。ArriveTime; arrayTask[s]。RunTime=arrayTask[s]。RequestTime; arrayTask[s]。Status=1; g=2; } if(g==1) /*当处理的不是第一个未执行的进程时执行*/ { arrayTask[s]。Status=1; for(i=0;i<4;i++) { if(arrayTask[i]。Status==1) d=arrayTask[i]。EndTime; } for(i=0;i<4;i++) /*查找最后执行的进程的完成时间*/ { if(arrayTask[i]。EndTime>d&&arrayTask[i]。Status==1) d=arrayTask[i]。EndTime; } if(arrayTask[s]。ArriveTime arrayTask[s]。StartTime=arrayTask[s]。ArriveTime; arrayTask[s]。EndTime=arrayTask[s]。StartTime+arrayTask[s]。RequestTime; arrayTask[s]。RunTime=arrayTask[s]。EndTime-arrayTask[s]。ArriveTime; } arrayTask[s]。DQRunTime=arrayTask[s]。RunTime/arrayTask[s]。RequestTime; } Printresult(int j) /*定义打印函数*/ { printf(“%dt”,arrayTask[j]。id); printf(“%5.2ft”,arrayTask[j]。ArriveTime); printf(“%5.2ft”,arrayTask[j]。RequestTime); printf(“%5.2ft”,arrayTask[j]。StartTime); printf(“%5.2ft”,arrayTask[j]。EndTime); printf(“%5.2ft”,arrayTask[j]。RunTime); printf(“%5.2fn”,arrayTask[j]。DQRunTime); } main() { int i,b,k,a,c=0; int d[4]; clrscr(); printf(“t F. FCFS n”); printf(“t S. SFJ n”); printf(“t Q. EXIT n”); for(i=0;;i++) { if(c) break; printf(“please input the number a:n”); scanf(“%d”,&a); switch(a) { case Q: c=1; break; case F:printf(“please input the different-ArriveTime of arrayTasksn”); GetTask(); printf(“*****************************the result of fcfsn”); printf(“NumbertArrivetServertStarttFinishtTurnovetTake power turnover timen”); for(b=0;b<4;b++) /*调用两个函数改变结构体数的值*/ { k=fcfs(); d[b]=k; new(k); } for(b=0;b<4;b++) Printresult(d[b]);/*调用打印函数打出结果*/ continue; case S: printf(“please input the different-RequestTime of array Tasksn”); GetTask(); printf(“******************************the result of sjfn”); printf(“NumbertArrivetRequesttStarttEndtRuntDQRun timen”); for(b=0;b<4;b++) { k=sjf(); d[b]=k; new(k); } for(b=0;b<4;b++) Printresult(d[b]); continue; default:printf(“the number Error.please input another number!n”); } } } 四、 实验总结和体会 通过做本实验,让我对进程或作业先来先服务、高优先权、按时间片轮转调度算法以及进程调度的概念和算法,有了更深入的认识!理解进程的状态及变化,动态显示每个进程的当前状态及进程的调度情况。进程调度是处理机管理的核心内容。优先级高优先是根据作业的优先级,总是选择优先级最高者进入队列。轮转调度算法是调度程序每次把CPU分配给就绪队列首进程/线程使用规定的时间间隔,就绪队列中都路保留巡行一个时间片。 实验一 嵌入式开发环境的建立 一、实验目的 通过此实验系统,读者可以了解嵌入式实时操作系统 uC/OS-II 的内核机制和运行原理。本实验系统展示了 uC/OS-II 各方面的管理功能,包括信号量、队列、内存、时钟等。在各个实验中具体介绍了 uC/OS-II 的相关函数。读者在做实验的同时能够结合理论知识加以分析,了解各个函数的作用和嵌入式应用程序的设计方法,最终对整个 uC/OS-II 和嵌入式操作系统的应用有较为清楚的认识。 二、实验步骤 1、 安装集成开发环境LambdaEDU 集成开发环境LambdaEDU 的安装文件夹为 LambdaEDU ,其中有一个名为“Setup.exe” 的文件,直接双击该文件便可启动安装过程。具体的安装指导请看“LambdaEDU 安装手 册。doc”文件。 当 LambdaEDU 安装完毕之后,我们看到的是一个空的界面,现在就开始一步一步地将 我们的实验项目建立并运行起来。 2、 建立项目 为了我们的实验运行起来,需要建立1 个项目基于x86 虚拟机的标准应用项目。通过点 击“文件”、“新建”、“项目”开始根据向导创建一个项目。 在随后出现的对话框中选择“Tool/标准应用项目”,点击下一步,开始创建一个标准的 可执行的应用程序项目。 在随后出现的对话框中填入项目名称“ucos_x86_demo”。点击“下一步”。 选择“pc386 uC/OS-II 应用(x86)”作为该项目的应用框架。点击“下一步” 选择“pc386_elf_tra_debug”作为该项目的基本配置。点击“完成”。 新创建的项目“ucos_x86_demo”将会被添加到项目列表。src 文件夹下保存了该项目中 包含的源文件。ucos2 文件夹中包含了移植到x86 虚拟机的全部代码。init.c 文件是基于ucos2 和本虚拟机的一个应用程序。在进行ucos2 内核实验中,只需要替换init.c 文件,即可。文 件名不限,但是文件名中最好不要使用英文符号和数字以外的其他字符, 3. 构建项目 到这里,项目配置全部完成。接下来就可以进行构建项目了。 第一次构建本项目,在此项目上点击右键,选择“重建BSP 及项目”。即可开始构建。 之后弹出的对话框显示了构建的进度。可以点击“在后台运行”,以隐藏该对话框 在构建的同时,在右下角的“构建信息”视图输出构建过程中的详细信息: 注:“重新构建”将本项目中的全部源代码进行一次完全的编译和连接,花费时间较多。 “构建项目”则仅仅将新修改过的源代码进行编译和连接,花费时间最少。“重建BSP及项 目”,不但要完成“重新构建”的全部工作,另外还要编译与该项目有关的的LambdaEDU 中内置的部分代码,花费时间最多。但是在项目刚建立后,第一次构建时需要选择“重建 BSP 及项目”。以后的构建中选择“重新构建”或“构建项目”即可。另外,在替换了源代 码中的文件后,需要选择“重新构建”来完成该项目的构建。 4、 配置虚拟机和目标机代理 (1) 制作X86启动盘 在 LambdaEDU 中依次点击“工具”、“Bochs”、“制作虚拟机启动映象”。 对启动盘进行一些参数设置后(如下图所示),系统将自动为你生成一个PC 虚拟机的 启动盘映像。 (2) 配置虚拟机 选择使用的网络适配器(网卡)后,点击“确定”完成配置。 注意:如果计算机上有多网卡,请将其他网卡停用(包括 VMware 虚拟机添加的虚拟 网卡)。 (3) 创建目标机代理 配置好虚拟机后,创建目标机代理:点击LambdaEDU 左下方窗口中绿色的十字符号, 在弹出的窗口中选择“基于TA 的连接方式”,并点击“下一步”。 在弹出的“新目标机连接配置中”的这些参数,应该与之前制作启动盘时设置的参数一致。 注意: 名字:输入目标机的名字(缺省是 default),注意如果和现有目标机重名的话,改个名 字。 连接类型:默认选择 UDP IP地址:这里输入目标机(在本实验系统中是虚拟机)的 IP地址; 最后点击“确定”,在目标机管理窗口中,可以看到新增加了一个名为default 的目标机 节点 (4) 调试应用 启动虚拟机。 虚拟机启动后的画面如下(其中显示的IP 地址创建虚拟机启动盘时填入的IP 地址)中设置的IP 地址): 在成功完成构建的项目ucos_x86_demo 中的“pc386_elf_tra_debug”上点击鼠标右键,在弹出的菜单中选择“调试”,启动调试器调试生成的程序: 第一次进行调试/运行,需要选择目标机,如下图,选择“Default”,点击“确定”,开 始向目标机(虚拟机)下载应用程序。 程序下载完成后,会弹出一个“确认透视图切换”对话框,选择“是”,切换到调试透 视图。 调试的界面如下: 点击绿色的按钮,全速运行。 注意:全速运行后,程序不能够被暂停和停止。 三、实验过程中遇到的问题及体会 在设置IP地址时,要求该IP地址与本计算机在同一个子网中,同时要求该 IP地址没有被网络上其他计算机使用。此外,通过构建开发环境,处次体验到了嵌入式开发工作的乐趣。 实验二 任务的基本管理 一、实验目的 1、理解任务管理的基本原理,了解任务的各个基本状态及其变迁过程; 2.掌握 uC/OS-II 中任务管理的基本方法(创建、启动、挂起、解挂任务); 3. 熟练使用 uC/OS-II 任务管理的基本系统调用。 二、实验原理及程序结构 1、 实验设计 为了展现任务的各种基本状态及其变迁过程,本实验设计了 Task0、Task1 两个任务: 任务 Task0 不断地挂起自己,再被任务 Task1 解挂,两个任务不断地切换执行。通过本实验,读者可以清晰地了解到任务在各个时刻的状态以及状态变迁的原因。 2. 运行流程 描述如下: (1)系统经历一系列的初始化过程后进入 boot_card()函数,在其中调用 ucBsp_init()进 行板级初始化后,调用 main()函数; (2)main()函数调用 OSInit()函数对 uC/OS-II 内核进行初始化,调用 OSTaskCreate 创 建起始任务 TaskStart; (3)main()函数调用函数 OSStart()启动 uC/OS-II 内核的运行,开始多任务的调度,执 行当前优先级最高的就绪任务 TaskStart; (4)TaskStart 完成如下工作: a、安装时钟中断并初始化时钟,创建 2 个应用任务; b、挂起自己(不再被其它任务唤醒),系统切换到当前优先级最高的就绪任务Task0。之后整个系统的运行流程如下: t1 时刻,Task0 开始执行,它运行到 t2 时刻挂起自己; t2 时刻,系统调度处于就绪状态的优先级最高任务 Task1 执行,它在 t3 时刻唤醒Task0,后者由于优先级较高而抢占 CPU; Task0 执行到 t4 时刻又挂起自己,内核调度 Task1 执行; Task1 运行至 t5 时刻再度唤醒 Task0; …… 3、 µC/OS-Ⅱ中的任务描述 一个任务通常是一个无限的循环,由于任务的执行是由操作系统内核调度的,因此任务是绝不会返回的,其返回参数必须定义成 void。在μC/OS-Ⅱ中,当一个运行着的任务使一个比它优先级高的任务进入了就绪态,当前任务的 CPU 使用权就会被抢占,高优先级任务会立刻得到 CPU 的控制权(在系统允许调度和任务切换的前提下)。μC/OS-Ⅱ可以管理多达 64 个任务,但目前版本的μC/OS-Ⅱ有两个任务已经被系统占用了(即空闲任务和统计任务)。必须给每个任务赋以不同的优先级,任务的优先级号就是任务编号(ID),优先级可以从 0 到 OS_LOWEST_PR10-2。优先级号越低,任务的优先级越高。μC/OS-Ⅱ总是运行进入就绪态的优先级最高的任务。 4. 源程序说明 (1) TaskStart任务 TaskStart 任务负责安装操作系统的时钟中断服务例程、初始化操作系统时钟,并创建所 有的应用任务: UCOS_CPU_INIT(); /* Install uC/OS-II's clock tick ISR */ UCOS_TIMER_START(); /*Timer 初始化*/ TaskStartCreateTasks(); /* Create all the application tasks */ OSTaskSuspend(OS_PRIO_SELF); 具体负责应用任务创建的 TaskStartCreateTasks 函数代码如下,它创建了两个应用任务 Task0 和 Task1: void TaskStartCreateTasks (void) { INT8U i; for (i = 0; i TaskData[i] = i; // Each task will display itsown information } OSTaskCreate(Task0, (void *)&TaskData[0], &TaskStk[0][TASK_STK_SIZE1], 6); } TaskStart 任务完成上述操作后将自己挂起,操作系统将调度当前优先级最高的应用任务Task0 运行。 (2) 应用任务 应用任务 Task0 运行后将自己挂起,之后操作系统就会调度处于就绪状态的优先级最高的任务,具体代码如下: void Task0 (void *pdata) { INT8U i; INT8U err; i=*(int *)pdata; for (;;) { printf(“Application tasks switched %d times!nr”,++count); printf(“TASK_0 IS RUNNING.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。nr”); printf(“task_1 is suspended!nr”); printf(“**************************************************nr”); err=OSTaskSuspend(5); // suspend itself } } 应用任务 Task1 运行后将 Task0 唤醒,使其进入到就绪队列中: void Task1 (void *pdata) { INT8U i; INT8U err; i=*(int *)pdata; for (;;) { OSTimeDly(150); printf(“Application tasks switched %d times!nr”,++count); printf(“task_0 is suspended!nr”); printf(“TASK_1 IS RUNNING.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。.。nr”); printf(“**************************************************nr”); OSTimeDly(150); err=OSTaskResume(5); /* resume task0 */ } } 三、运行及观察应用输出信息 按照本实验手册第一部分所描述的方法建立应用项目并完成构建 ,当我们在 LambdaEDU 调试器的控制下运行构建好的程序后,将看到在μC/OS-Ⅱ内核的调度管理下, 两个应用任务不断切换执行的情形: 四、本实验中用到的µC/OS-Ⅱ相关函数 4.1 OSTaskCreate() OSTaskCreate()建立一个新任务。任务的建立可以在多任务环境启动之前,也可以在 正在运行的任务中建立。中断处理程序中不能建立任务。一个任务必须为无限循环结构,且 不能有返回点。 OSTaskCreate()是为与先前的μC/OS 版本保持兼容,新增的特性在 OSTaskCreateExt ()函数中。 无论用户程序中是否产生中断,在初始化任务堆栈时,堆栈的结构必须与 CPU 中断后 寄存器入栈的顺序结构相同。详细说明请参考所用处理器的手册。 函数原型: INT8U OSTaskCreate( void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio ); 参数说明: task 是指向任务代码首地址的指针。 pdata 指向一个数据结构,该结构用来在建立任务时向任务传递参数。 ptos 为指向任务堆栈栈顶的指针。任务堆栈用来保存局部变量,函数参数,返回地址 以及任务被中断时的 CPU 寄存器内容。任务堆栈的大小决定于任务的需要及预计的中断嵌 套层数。计算堆栈的大小,需要知道任务的局部变量所占的空间,可能产生嵌套调用的函数,及中断嵌套所需空间。如果初始化常量 OS_STK_GROWTH 设为 1,堆栈被设为从内存高地址 向 低 地 址 增 长 , 此时 ptos 应 该 指 向任 务堆 栈 空 间 的 最 高 地 址 。 反 之 , 如 果OS_STK_GROWTH 设为 0,堆栈将从内存的低地址向高地址增长。prio 为任务的优先级。每个任务必须有一个唯一的优先级作为标识。数字越小,优先级越高。 返回值: OSTaskCreate()的返回值为下述之一: OS_NO_ERR:函数调用成功。 OS_PRIO_EXIST:具有该优先级的任务已经存在。 OS_PRIO_INVALID:参数指定的优先级大于 OS_LOWEST_PRIO。 OS_NO_MORE_TCB:系统中没有 OS_TCB 可以分配给任务了。 注意: 任务堆栈必须声明为 OS_STK 类型。 在任务中必须调用μC/OS 提供的下述过程之一:延时等待、任务挂起、等待事件发生 (等待信号量,消息邮箱、消息队列),以使其他任务得到 CPU。 用 户 程 序 中 不 能 使 用 优 先 级 0 , 1 , 2 , 3 , 以 及 OS_LOWEST_PRIO-3, OS_LOWEST_PRIO-2, OS_LOWEST_PRIO-1, OS_LOWEST_PRIO。这些优先级μC/OS 系统 保留,其余的 56 个优先级提供给应用程序。 4.2 OSTaskSuspend() OSTaskSuspend () 无条件挂起一个任务 。调用此函数的任务也可以传递参数 OS_PRIO_SELF,挂起调用任务本身。当前任务挂起后,只有其他任务才能唤醒。任务挂起 后,系统会重新进行任务调度,运行下一个优先级最高的就绪任务。唤醒挂起任务需要调用 函数 OSTaskResume ()。 任务的挂起是可以叠加到其他操作上的。例如,任务被挂起时正在进行延时操作,那么 任务的唤醒就需要两个条件:延时的结束以及其他任务的唤醒操作。又如,任务被挂起时正 在等待信号量,当任务从信号量的等待对列中清除后也不能立即运行,而必须等到被唤醒后。 函数原型: INT8U OSTaskSuspend( INT8U prio); 参数说明: prio 为指定要获取挂起的任务优先级,也可以指定参数 OS_PRIO_SELF,挂起任务本 身。此时,下一个优先级最高的就绪任务将运行。 返回值: OSTaskSuspend()的返回值为下述之一: OS_NO_ERR:函数调用成功。 OS_TASK_ SUSPEND_IDLE:试图挂起 µC/OS-II 中的空闲任务(Idle task)。此为非法操作。 16 OS_PRIO_INVALID :参数指定的优先级大于 OS_LOWEST_PRIO 或没有设定 OS_PRIO_SELF 的值。 OS_TASK_ SUSPEND _PRIO:要挂起的任务不存在。 注意: 在程序中 OSTaskSuspend()和 OSTaskResume ()应该成对使用。 用 OSTaskSuspend()挂起的任务只能用 OSTaskResume ()唤醒。 4.3 OSTaskResume() OSTaskResume ()唤醒一个用 OSTaskSuspend()函数挂起的任务。OSTaskResume ()也是唯一能“解挂”挂起任务的函数。 函数原型: INT8UOSTaskResume ( INT8U prio); 参数说明: prio 指定要唤醒任务的优先级。 返回值: OSTaskResume ()的返回值为下述之一: OS_NO_ERR:函数调用成功。 OS_TASK_RESUME_PRIO:要唤醒的任务不存在。 OS_TASK_NOT_SUSPENDED:要唤醒的任务不在挂起状态。 OS_PRIO_INVALID:参数指定的优先级大于或等于 OS_LOWEST_PRIO。 五、实验过程中遇到的问题及体会 实验过程中体会到了嵌入式开发的乐趣,对上课老师所讲的内容有了进一步的认识与理解。 17 实验三 信号量:哲学家就餐问题的实现 一、实验目的 掌握在基于嵌入式实时操作系统 uC/OS-II 的应用中,任务使用信号量的一般原理。通 过经典的哲学家就餐实验,了解如何利用信号量来对共享资源进行互斥访问。 二、实验原理及程序结构 1、 实验设计 掌握在基于嵌入式实时操作系统 uC/OS-II 的应用中,任务使用信号量的一般原理。通 过经典的哲学家就餐实验,了解如何利用信号量来对共享资源进行互斥访问。 2. 源程序说明 五个哲学家任务(ph1、ph2、ph3、ph4、ph5)主要有两种过程:思考(即睡眠一段时 间)和就餐。每个哲学家任务在就餐前必须申请并获得一左一右两支筷子,就餐完毕后释放 这两支筷子。五个哲学家围成一圈,每两人之间有一支筷子。一共有五支筷子,在该实验中 用了五个互斥信号量来代表。每个任务的代码都一样,如下所示: void Task (void *pdata) { INT8U err; INT8U i; INT8U j; i=*(int *)pdata; j=(i+1) % 5; uC/OS-II 实验指导书 for (;;) { TaskThinking2Hungry(i); OSSemPend(fork[i], 0, &err); OSSemPend(fork[j], 0, &err); /* Acquire semaphores to eat */ TaskEat(i); OSSemPost(fork[j]); OSSemPost(fork[i]); /* Release semaphore */ OSTimeDly(200); /* Delay 10 clock tick */ } } 操作系统配置 修改 uC_OS-II/OS_CFG.h: :: : #define OS_MAX_EVENTS 10 /*最多可以有 10 个事件*/ #define OS_MAX_FLAGS 5 /*最多可以有 5 个事件标志*/ #define OS_MAX_MEM_PART 5 /*最多可以划分 5 个内存块*/ #define OS_MAX_QS 2 /*最多可以使用 2 个队列*/ #define OS_MAX_TASKS 8 /*最多可以创建 8 个任务*/ #define OS_LOWEST_PRIO 14 /*任务优先级不可以大于 14*/ #define OS_TASK_IDLE_STK_SIZE 1024 /*空闲任务堆栈大小*/ #define OS_TASK_STAT_EN 1 /*是否允许使用统计任务*/ #define OS_TASK_STAT_STK_SIZE 1024 /*统计任务堆栈大小*/ #define OS_FLAG_EN 1 /*是否允许使用事件标志功能*/ #define OS_FLAG_WAIT_CLR_EN 1 /*是否允许等待清除事件标志*/ #define OS_FLAG_ACCEPT_EN 1 /*是否允许使用 OSFlagAccept()*/ #define OS_FLAG_DEL_EN 1 /*是否允许使用 OSFlagDel()*/ #define OS_FLAG_QUERY_EN 1 /*是否允许使用 OSFlagQuery()*/ #define OS_MBOX_EN 0 /*是否允许使用邮箱功能*/ #define OS_MEM_EN 0 /*是否允许使用内存管理的功能*/ #define OS_MUTEX_EN 0 /*是否允许使用互斥信号量的功能*/ #define OS_Q_EN 0 /*是否允许使用队列功能*/ #define OS_SEM_EN 1 /*是否允许使用信号量功能*/ #define OS_SEM_ACCEPT_EN 1 /*是否允许使用 OSSemAccept()*/ #define OS_SEM_DEL_EN 1 /*是否允许使用OSSemDel() */ #define OS_SEM_QUERY_EN 1 /*是否允许使用OSSemQuery()*/ #define OS_TASK_CHANGE_PRIO_EN 1 /* 是 否 允 许 使 用 OSTaskChangePrio()*/ #define OS_TASK_CREATE_EN 1 /*是否允许使用 OSTaskCreate()*/ #define OS_TASK_CREATE_EXT_EN 1 /*是否允许使用 OSTaskCreateExt()*/ #define OS_TASK_DEL_EN 1 /*是否允许使用 OSTaskDel()*/ #define OS_TASK_SUSPEND_EN 1 /* 是 否 允 许 使 用 OSTaskSuspend() and OSTaskResume()*/ #define OS_TASK_QUERY_EN 1 /*是否允许使用 OSTaskQuery()*/ #define OS_TIME_DLY_HMSM_EN 1 /* 是 否 允 许 使 用 OSTimeDlyHMSM()*/ #define OS_TIME_DLY_RESUME_EN 1 /* 是 否 允 许 使 用 OSTimeDlyResume()*/ #define OS_TIME_GET_SET_EN 1 /* 是否允许使用 OSTimeGet() 和 OSTimeSet()*/ #define OS_SCHED_LOCK_EN 1 /* 是 否 允 许 使 用 OSSchedLock() 和 OSSchedUnlock()*/ #define OS_TICKS_PER_SEC 200 /*设置每秒之内的时钟节拍数目*/ 三、运行及观察应用输出信息 开始,所有的哲学家先处于 thinking 状态,然后都进入 hungry 状态: 后首先获得两个信号量的 1、3 号哲学家开始 eating,待他们释放相关信号量之后,哲 学家 2、5、4 获得所需的信号量并 eating: 应用如此这般地循环执行程序下去„„ 19 四、本实验中用到的µC/OS-Ⅱ相关函数 4.1 OSSemCreate() OSSemCreate()函数建立并初始化一个信号量。信号量的作用如下: 允许一个任务和其他任务或者中断同步 取得设备的使用权 标志事件的发生 函数原型: OS_EVENT *OSSemCreate( (( (WORD value) )) ) 参数说明: value 参数是所建立的信号量的初始值,可以取 0 到 65535 之间的任何值。 返回值: OSSemCreate()函数返回指向分配给所建立的信号量的控制块的指针。如果没有可用的 控制块,OSSemCreate()函数返回空指针。 注意: 必须先建立信号量,然后使用。 4.2 OSSemPend() OSSemPend()函数用于任务试图取得设备的使用权,任务需要和其他任务或中断同 步,任务需要等待特定事件的发生的场合。如果任务调用 OSSemPend()函数时,信号量 的值大于零,OSSemPend()函数递减该值并返回该值。如果调用时信号量等于零, OSSemPend()函数函数将任务加入该信号量的等待队列。OSSemPend()函数挂起当前 任务直到其他的任务或中断置起信号量或超出等待的预期时间。如果在预期的时钟节拍内信 号量被置起,μC/OS-Ⅱ默认最高优先级的任务取得信号量恢复执行。一个被 OSTaskSuspend ()函数挂起的任务也可以接受信号量,但这个任务将一直保持挂起状态直到通过调用 OSTaskResume()函数恢复任务的运行。 函数原型: :: : Void OSSemPend ( OS_EVNNT *pevent, INT16U timeout, int8u *err ); 参数说明: :: : pevent 是指向信号量的指针。该指针的值在建立该信号量时可以得到。( 参考 OSSemCreate()函数)。 Timeout 允许一个任务在经过了指定数目的时钟节拍后还没有得到需要的信号量时 恢复就绪状态。如果该值为零表示任务将持续地等待信号量,最大的等待时间为 65535 个时 钟节拍。这个时间长度并不是非常严格的,可能存在一个时钟节拍的误差。 Err 是指向包含错误码的变量的指针。OSSemPend()函数返回的错误码可能为下述几 种: OS_NO_ERR :信号量不为零。 OS_TIMEOUT :信号量没有在指定数目的时钟周期内被设置。 OS_ERR_PEND_ISR :从中断调用该函数。虽然规定了不允许从中断调用该函数, 但 µC/OS-Ⅱ仍然包含了检测这种情况的功能。 OS_ERR_EVENT_TYPE :pevent 不是指向信号量的指针。 返回值: 无 注意: 必须先建立信号量,然后使用。 不允许从中断调用该函数。 4.3 OSSemPost() OSSemPost()函数置起指定的信号量。如果指定的信号量是零或大于零,OSSemPost () 函数递增该信号量并返回。如果有任何任务在等待信号量,最高优先级的任务将得到信 号量并进入就绪状态。任务调度函数将进行任务调度,决定当前运行的任务是否仍然为最高 优先级的就绪状态的任务。 函数原型: INT8U OSSemPost(OS_EVENT *pevent); 参数说明: pevent 是指向信号量的指针。该指针的值在建立该信号量时可以得到。( 参考 OSSemCreate()函数)。 返回值: OSSemPost()函数的返回值为下述之一: OS_NO_ERR :信号量被成功地设置 OS_SEM_OVF :信号量的值溢出 OS_ERR_EVENT_TYPE :pevent 不是指向信号量的指针 注意: 必须先建立信号量,然后使用。 4.4 OSTimeDly() OSTimeDly()将一个任务延时若干个时钟节拍。如果延时时间大于 0,系统将立即进 行任务调度。延时时间的长度可从 0 到 65535 个时钟节拍。延时时间 0 表示不进行延时,函 21 数将立即返回调用者。延时的具体时间依赖于系统每秒钟有多少时钟节拍(由文件 SO_CFG.H 中的常量 OS_TICKS_PER_SEC 设定)。 函数原型: void OSTimeDly ( INT16U ticks); 参数说明: ticks 为要延时的时钟节拍数。 返回值: 无 注意: 注意到延时时间 0 表示不进行延时操作,而立即返回调用者。为了确保设定的延时时间, 建议用户设定的时钟节拍数加 1。例如,希望延时 10 个时钟节拍,可设定参数为 11。 五、实验过程中遇到的问题及体会 在实验前要对该问题进行深入的理解,即五个哲学家任务(ph1、ph2、ph3、ph4、ph5)主要有两种过程:思考(即睡眠一段时间)和就餐。每个哲学家任务在就餐前必须申请并获得一左一右两支筷子,就餐完毕后释放这两支筷子。五个哲学家围成一圈,每两人之间有一支筷子。只有理解了,才能更好的进行实验。 22 1.实习目的 (一).通过综合实训进一步巩固、深化和扩展学生的专业技能。 1.熟练掌握Linux操作系统的安装及基本配置。 2.熟练掌握Linux系统管理。 3.掌握Linux下用户和组的管理。 4.掌握Linux下FTP服务器的管理。 (二)训练和培养学生获取信息和处理信息的能力,充分培养和提高学生的动手能力,学会通过网站、书籍等方式收集所需的。资料。 (三)培养学生运用所学的知识和技能解决Linux使用、管理过程中所遇到的实际问题的能力及其基本工作素质。 (四)培养学生理论联系实际的工作作风、严肃认真的科学态度以及独立工作的能力,树立自信心。 (五)训练和培养学上的团队协作精神与合作能力。 2 实习概况 2.1 实习要求 具体来讲,《linux操作系统》课程包括以下实习内容: (一)独立完成实训。 (二)要求熟练掌握Linux操作系统的安装与基本配置。 (三)熟练掌握Linux系统管理基本方法。 (四)掌握Linux下用户和组的管理。。 (五)掌握Linux下的FTP服务器的管理。 2.2 实习时间 20XX年12月16日至20XX年12月20日 2.3 实习基本情况 实习地点:四教学楼 4112、4212、4312、4412 实习环境 :RedHat9软件 实习内容:掌握linux操作系统 2.4 硬件环境 3 实习内容 3.1 linux安装 Linux是一类Unix计算机操作系统的统称。Linux 是以Unix 操作系统为原型的多任务、多用户的系统。可运行于多种硬件平台:PC、Alpha、SPARC、 POWER PC。 今天实习的主要内容是学习了解Linux的安装过程;Linux登录和退出 ,熟悉Linux操作系统的图形界面 (一)Linux的安装过程 1)VMware软件的安装 因为我用的是机房的电脑,所以不用安装VMware软件。如果要安装,过程十分简单,下载完毕,直接“Next”即可完成安装。 2)虚拟机的安装。打开VMware软件,单击“新建虚拟机”命令根据提示选择一种要安装的操作系统,一般选择典型设置,然后直接按“下一步”即可。需要注意的就是在分区的时候需按要求建立合适的分区,如下图所示。 图3-1-1 选择分区 3)Red Hat Linux 9.0安装 首先单击“编辑虚拟机设置”,改写镜像为“linux9cd1”,然后返回初始界面。点击“启动该虚拟机”,便进入到软件的安装过程。开始是“欢迎使Red Hat Linux”界面,然后经历语言选择、键盘配置、鼠标配置、磁盘分区设置、选择软件包组、安装软件包等操作后,然后是虚拟机安装完第一张盘后要进行第二张盘的安装,如图3-2经过老师的指点,按住“Ctrl+Alt”,将鼠标调出,双击右下方任务栏第一个按钮,依次选择第二、三镜像,继续安装,便安装成功了。如图3-3。 实验项目二 进程管理 一、 实验目的 1、 理解进程的概念,掌握父、子进程创建的方法。 2、 认识和了解并发执行的实质,掌握进程的并发及同步操作。 二、 实验内容 1、 编写一C语言程序,实现在程序运行时通过系统调用fork( )创建两个子进程,使父、子三进程并发执行,父亲进程执行时屏幕显示“I am father”,儿子进程执行时屏幕显示“I am son”,女儿进程执行时屏幕显示“I am daughter”。 2、 多次连续反复运行这个程序,观察屏幕显示结果的顺序,直至出现不一样的情况为止。记下这种情况,试简单分析其原因。 3、 修改程序,在父、子进程中分别使用wait()、exit()等系统调用“实现”其同步推进,并获取子进程的ID号及结束状态值。多次反复运行改进后的程序,观察并记录运行结果。 三、 源程序及运行结果 源程序1: #include printf(“error!”); else if( pid == 0 ) { printf(“I am son!n”); } else { int pid=fork(); if (pid<0) printf(“error!”); else if( pid == 0 ) { printf(“I am daughter! n“); } else printf(”I am father!n“); } sleep(1); return 0; } 运行结果: 源程序2: #include int pid=fork(); if(pid<0) printf(”error!“); else if( pid == 0 ) { message=”I am daughter!“; pid=getpid(); n=3; } else { int pid=fork(); if (pid<0) printf(”error!“); else if( pid == 0 ) { message=”I am son!“; pid=getpid(); n=3; } else message=”I am father!"; n=3; } for(;n>0;n--) { puts(message); sleep(1); } return 0; } 运行结果: 四、 实验分析与总结 1、 实验内容1运行结果为什么无固定顺序,fork()函数创建进程是如何并发执行的。 答:因为进程是并发执行的,fork()函数创建的三个进程抢占 cpu不同,从而导致三个程序被cpu 调度顺序不同,所以实验一结果无固定顺序。Fork()函数调用成功后,子进程与父进程并发执行的代码相同,但由于子进程也继承父进程的程序指针,所以子进程是从fork()后执行的,另外fork在子进程和父进程中返回值是不同的。在父进程中返回子进程的pid,而在子进程中返回0,使父进程和子进程执行不同的分支。 2、 实验内容3是如何实现父子进程的同步执行的。 答:wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。程序段主要使用了函数wait()和,exit()这是因为父进程必须等待两个子进程终止后才终。在父进程中调用wait()函数,则父进程被阻塞,进入等待队列,等待子进程结束。子进程终止时执行exit()向父进程发终止信号,当接到信号后,父进提取子进程的结束状态值,从wait()返回继续执行原程序,从而实现了父、子进程的同步推进。 总结:通过进程管理实验,了解fork()函数创建进程是并发执行的,wait()进程表示会暂时停止目前进程的执行,可以灵活运用fork()和wait()进程解决有关问题。在实验中遇到许多问题,如:实验中调用fork()进程失败,可能的原因有系统中有太多的进程或者实际用户ID的进程总数超过了系统的限制。刚接触VMware这个系统,操作不熟悉,多次操作后,了解这个系统有关操作,实验做起来就比较简单了。对实验代码也不熟悉,最后通过请教老师和同学,终于实验理解透彻,并成功运行了。不仅上课要认真听课,要想真正学会,课下也要付出努力。 一、实验目的 用高级语言编写和调试一个进程调度程序,以加深对进程的概念及进程调度算法的理解。 二、实验内容和要求 编写并调试一个模拟的进程调度程序,采用“简单时间片轮转法”调度算法对五个进程进行调度。 每个进程有一个进程控制块( PCB)表示。进程控制块可以包含如下信息:进程名、到达时间、需要运行时间、已运行时间、进程状态等等。 进程的到达时间及需要的运行时间可以事先人为地指定(也可以由随机数产生)。进程的到达时间为进程输入的时间。 进程的运行时间以时间片为单位进行计算。 每个进程的状态可以是就绪 W(Wait)、运行R(Run)两种状态之一。 就绪进程获得 CPU后都只能运行一个时间片。用运行时间加1来表示。 如果运行一个时间片后,进程的已占用 CPU时间已达到所需要的运行时间,则撤消该进程,如果运行一个时间片后进程的已占用CPU时间还未达所需要的运行时间,也就是进程还需要继续运行,此时应分配时间片给就绪队列中排在该进程之后的进程,并将它插入就绪队列队尾。 每进行一次调度程序都打印一次运行进程、就绪队列、以及各个进程的 PCB,以便进行检查。 重复以上过程,直到所要进程都完成为止。 三、实验主要仪器设备和材料 硬件环境:IBM-PC或兼容机 软件环境:C语言编程环境 四、实验原理及设计方案 1、进程调度算法:采用多级反馈队列调度算法。其基本思想是:当一个新进程进入内在后,首先将它放入第一个队列的末尾,按FCFS原则排队等待高度。当轮到该进程执行时,如能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚为完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行,以此类推。 2、实验步骤: (1)按先来先服务算法将进程排成就绪队列。 (2)检查所有队列是否为空,若空则退出,否则将队首进程调入执行。 (3)检查该运行进程是否运行完毕,若运行完毕,则撤消进程,否则,将该进程插入到下一个逻辑队列的队尾。 (4)是否再插入新的进程,若是则把它放到第一逻辑队列的列尾。 (5)重复步骤(2)、(3)、(4),直到就绪队列为空。 五、流程图 六、结果过程及截图 初始化队列 按Y键继续运行进程: 按Y键继续运行进程: 运行若干次后的状态: 添加新的进程: 七、所遇困难的解决以及心得体会 在这个多级反馈的实验中,我采取了用一条实际上的链表队列来模拟多个逻辑上的队列,通过维护几个链表的状态信息来找到每个进程运行完后应该插入的地方,还有一个标志位Fend用来表明新插入的队列的位置。虽然实验原理很简单,但是在编写代码的过程中遇到了不少的问题,在两个小时之内已经完成的大体代码的编写,但是之中存在不少的问题,导致了用了差不多四个小时的时间去调试才把它弄好,这主要归咎于在开始设计代码的。不太合理,在后期使得代码结构有些混乱,使得调试更加的麻烦,以及对编程的不熟悉。通过这个实验不仅使我对进程的调度算法有了更深的认识,使得理论知识得到的实践,也使我的编程能力得到了进一步提高。 七、思考题 1、 分析不同调度算法的调度策略,比较不同调度算法的优缺点,总结它们的适用范围。 答:动态有限权算法:动态优先权是指在创建进程时所创建的优先权,会随进程的推进或者等待时间的增加而改变,以便获得更好的调度性能。处理机为每个进程分配一定的时间片,在就绪队列中,优先权高的进程将优先获得处理机,进程在进去运行完响应的时间片后,如没完成,优先权减1,从新回到就绪队列等待分配处理机。 时间片的轮转法:系统将所有进程排成一个队列,按照先来先服务的原则,对队列首的进程进行处理,每个进程在用完自己的时间片后,从新回到队尾进行排队。每运行一次,进程的需要时间减1,直到就绪队列为空! 八、源代码 #include #include #include #define getpch(type) (type*)malloc(sizeof(type)) #define NULL 0 #define TIME 2//时间片长度 typedef struct pcb{//进程管理块 char name[10];//进程名字 char state; //进程状态 int queue; int ntime; int rtime; int etime; //进程所在的队列 //进程需要运行的时间 //进程已经运行的时间 //进程在本队列可运行的时间片 struct pcb *link; }PCB; PCB *ready = NULL, *p = NULL, *pfend = NULL,*p =NULL; 位置的变量 int geti() //使用户仅能输入整数 { char ch; int i = 0; fflush(stdin); ch = get); while(ch == ){ //就绪队列,进程插入 printf( fflush(stdin); ch = get); } while(ch != ){ if(ch > 9 || ch fflush(stdin); i = 0; ch = get); }else{ i = i*10 + (ch - 0); } ch = get); } return i; } void findpos()//更新状态量 { PCB *ps = pfend; if(!ps || !ps -> link || (ps-> link->queue - ps->queue) > 1) p = ps; else{ while (ps->link && ps ->link->queue != (pfend ->queue +2)) } ps = ps->link; p = ps; } void ()//插入进程 { if(!ready ){ ready = p; pfend = p; p = p; }else if(ready ->queue == 1){//第一队列存在 p->link = pfend->link; pfend->link = p; pfend = p; findpos(); } void input()/*建立进程控制块函数*/ { int i,num; printf( for(i=0; i p=getpch(PCB); printf( printf( p->queue =1; p->etime = TIME; p->link=NULL; ();/*调用函数*/ } else{ p->link = ready; ready = p; } findpos(); } void disp(PCB *pr)/*建立进程现实函数,用于显示当前进程*/ { printf( } void check()/*建立进程查看函数*/ { } void sort()//调整进程队列 { } if(!ready->link ||ready->queue link->queue) return; p = ready ->link; ready ->link = p ->link; p ->link = ready; p = ready; ready = p; if (ready && ready -> queue == p ->queue){ findpos(); } PCB *pr; printf( void addnew()//添加新的进程 { if(ready ->queue != 1){ (ready -> queue)++; ready->etime *= 2; ready -> state=w; sort();/*调用sort函数*/ input(); } else{ input(); } } void destroy()/*建立进程撤销函数(进程运行结束,撤销进程)*/ { } void running()/*建立进程就绪函数(进程运行时间到,置就绪状态)*/ { } void main() { char ch; input(); while(ready != NULL) { printf( } ready ->state = R; check(); running(); printf( 一、实验目的 本实验要求学生模拟作业调度的实现,用高级语言编写和调试一个或多个作业调度的模拟程序,了解作业调度在操作系统中的作用,以加深对作业调度算法的理解。 二、实验内容和要求 1、编写并调度一个多道程序系统的作业调度模拟程序。 作业调度算法:采用基于先来先服务的调度算法。可以参考课本中的方法进行设计。 对于多道程序系统,要假定系统中具有的各种资源及数量、调度作业时必须考虑到每个作业的资源要求。 三、实验主要仪器设备和材料 硬件环境:IBM-PC或兼容机 软件环境:C语言编程环境 四、实验原理及设计方案 采用多道程序设计方法的操作系统,在系统中要经常保留多个运行的作业,以提高系统效率。作业调度从系统已接纳的暂存在输入井中的一批作业中挑选出若干个可运行的作业,并为这些被选中的作业分配所需的系统资源。对被选中运行的作业必须按照它们各自的作业说明书规定的步骤进行控制。 采用先来先服务算法算法模拟设计作业调度程序。 (1)、作业调度程序负责从输入井选择若干个作业进入主存,为它们分配必要的资源,当它们能够被进程调度选中时,就可占用处理器运行。作业调度选择一个作业的必要条件是系统中现有的尚未分配的资源可满足该作业的资源要求。但有时系统中现有的尚未分配的资源既可满足某个作业的要求也可满足其它一些作业的'要求,那么,作业调度必须按一定的算法在这些作业中作出选择。先来先服务算法是按照作业进入输入井的先后次序来挑选作业,先进入输入井的作业优先被挑选,当系统中现有的尚未分配的资源不能满足先进入输入井的作业时,那么顺序挑选后面的作业。 (2) 假定某系统可供用户使用的主存空间共100k,并有5台磁带机。 (3)流程图: 五、结果过程及截图 读取文件jobs.txt来初始化主存,磁带机的个数,并打印出来。 初始时间是9:00: 按Y运行5分钟: 按Y运行5分钟: 按Y运行5分钟: 多次运行后最后状态: 六、所遇困难的解决以及心得体会 这个实验是花的时间最多的一个实验,第一次做的时候由于理解有些问题,所以做错了。之后重新做了一遍,收获还是很多的,遇到了很多的细节问题,例如像是时间化成浮点数和浮点数化成时间等一些问题,从中也暴露了自己的编程能力欠缺,之后要多多的写程序。 七、思考题 1、 写出每种算法的调度策略,最后比较各种算法的优缺点。 答:先来先服务算法是根据作业的进入时间来排序,到达时间短的先运行,优点是实现简单,缺点是运行时间慢。 短作业优先算法是根椐作业的估计运行时间来排序,估计运行时间短的先运行,优点是运行时间快,缺点是实现起来比较复杂。 2、 选择调度算法的依据是什么? 答:如果作业要求的速度不高,而且作业比较小型,那就最好用先来先服务算法。 如果作业要求的速度高,作业流程复杂,那就最好用短作业优先算法。 八、源代码 #include #include #include #include #define getjcb() (JCB*)malloc(sizeof(JCB)) typedef struct {//资源的总量 int memory; int tape; }RESOURCE; typedef struct JCB {//作业控制块 char username[20];//用户名 char jobname[10];//作业名 char state;//作业状态 char atime[5];//到达时间 float rtime;//运行时间 RESOURCE resource;//资源数量 struct JCB*link; }JCB; RESOURCE source = {100,5}; JCB *pjcb =getjcb();//作业链表头 char nowtime[5];//现在时间,初始时间为9:00 FILE* ignore(FILE *fp)//忽略文件中的空白符 { if(feof(fp)) return fp; char ch = fgetc(fp); while (!feof(fp) && (ch == || ch == )){ ch = fgetc(fp); } //if(!feof(fp)) return fp; fseek(fp, -1, SEEK_CUR); return fp; } FILE* findFILE *fp,char c)//在文件中找到一个字符的位置(读取文件时用) { if(feof(fp)) return fp; char ch = fgetc(fp); while (!feof(fp) && (ch != c)){ } ch = fgetc(fp); fseek(fp, -1, SEEK_CUR); return fp; } void destory()//释放链表所占的内存 { JCB *p = pjcb->link; while(pjcb){ free(pjcb); pjcb = p; if(p) p = p->link; } } float stof(char *time)//把时间转化为浮点型数 { float h = 0, m = 0; int i = 0; while(time[i] != :){ h = h*10 + time[i] - 0; } i++; while(time[i] != ){ m = m*10 + time[i] - 0; } i++; i++; return (h + m/60); } char* ftos(double ftime)//把浮点型数值转化为时间 { } float timesub(char *time1, char *time2)//两个时间相减,得到时间差 { } void print()//打印输出 { JCB *p = pjcb->link; printf( p->atime, p->rtime, p->,p->resource.tape); } void sendsource()//为作业分配资源 { JCB *p; p = pjcb->link; while(p){//为到达的作业调度 p = p->link; } if(p->state == W && - p-> >=0 && source.tape - p->resource.tape >=0){ } } p = p->link; p->state = R; -= p->; source.tape -= p->resource.tape; printf( } void init()//初始化,读取文件中的作业信息 { FILE *fp; JCB *p= NULL,*q = pjcb ; if((fp = fopen( } } fp = ignore(fp); p->rtime = 0;//不初始化则会发生错误,????? fscanf(fp, int checkend() //检查是否所有的作业都已经运行完了 { JCB *p = pjcb ->link; while(p){ if(p ->state != F){ return 0; } p = p->link; } return 1; } void run()//运行作业 { if(checkend()){//检查是否所有的作业都已经运行完了 printf( } } p = p->link; } p = pjcb ->link; while(p){//计算到达的作业 if( strcmp(nowtime, p->atime) ==0 && p->state == N){ p->state = W; printf( int main() { char ch; double time =9.00; } double step = float(5)/60+0.00001; ftos(9.0); init(); do{ run(); puts( 实验二 进程调度 1.目的和要求 通过这次实验,理解进程调度的过程,进一步掌握进程状态的转变、进程调度的策略,进一步体会多道程序并发执行的特点,并分析具体的调度算法的特点,掌握对系统性能的评价方法。 2.实验内容 阅读教材《计算机操作系统》第二章和第三章,掌握进程管理及调度相关概念和原理。 编写程序模拟实现进程的轮转法调度过程,模拟程序只对PCB进行相应的调度模拟操作,不需要实际程序。假设初始状态为:有n个进程处于就绪状态,有m个进程处于阻塞状态。采用轮转法进程调度算法进行调度(调度过程中,假设处于执行状态的进程不会阻塞),且每过t个时间片系统释放资源,唤醒处于阻塞队列队首的进程。 程序要求如下: 1)输出系统中进程的调度次序; 2)计算CPU利用率。 3.实验环境 Windows操作系统、VC++6.0 C语言 4设计思想: (1) 程序中进程可用PCB表示,其类型描述如下: struct PCB_type { int pid ; //进程名 int state ; //进程状态 2——表示“执行”状态 1——表示“就绪”状态 0——表示“阻塞”状态 int cpu_time ; //运行需要的CPU时间(需运行的时间片个数) } 用PCB来模拟进程; (2)设置两个队列,将处于“就绪”状态的进程PCB挂在队列ready中;将处于“阻塞”状态的进程PCB挂在队列blocked中。 队列类型描述如下: struct QueueNode{ struct PCB_type PCB; Struct QueueNode *next; } 并设全程量: struct QueueNode *ready_head=NULL,//ready队列队首指针 *ready_tail=NULL , //ready队列队尾指针 *blocked_head=NULL,//blocked队列队首指针 *blocked_tail=NULL; //blocked队列队尾指针 (3)设计子程序: start_state(); 读入假设的数据,设置系统初始状态,即初始化就绪队列和阻塞队列。 dispath(); 模拟调度,当就绪队列的队首进程运行一个时间片后,放到就绪队列末尾,每次都是队首进程进行调度,一个进程运行结束就从就绪队列中删除,当到t个时间片后,唤醒阻塞队列队首进程。 calculate(); 就绪进程运行一次,usecpu加1,当就绪队列为空时unusecpu加1,CPU利用率为use_cpu/(use_cpu+unuse_cpu)。 5源代码: #include struct PCB_type { int pid ; //进程名 int state ; //进程状态 //2--表示“执行”状态 //1--表示“就绪”状态 //0--表示 m.chayi5.com “阻塞”状态 int cpu_time ; //运行需要的CPU时间(需运行的时间片个数) }; struct QueueNode{ struct PCB_type PCB; struct QueueNode *next; }; struct QueueNode *ready_head=NULL, //ready队列队首指针 *ready_tail=NULL, //ready队列队尾指针 *block_head=NULL, //blocked队列队首指针 *block_tail=NULL; //blocked队列队尾指针 int use_cpu,unuse_cpu; void start_state() //读入假设的数据,设置系统初始状态 { int n,m; int i; struct QueueNode *p,*q; printf(“输入就绪节点个数n:”); scanf(“%d”,&n); printf(“输入阻塞节点个数m:”); scanf(“%d”,&m); p=(struct QueueNode *)malloc(sizeof(struct QueueNode)); p->next =NULL; ready_head=ready_tail=p; for(i=0;i { p=(struct QueueNode *)malloc(sizeof(struct QueueNode)); p->next =NULL; p->PCB.state=1; printf(“输入就绪进程%d的pid和cpu_time:”,i+1); scanf(“%d%d”,&p->PCB.pid,&p->PCB.cpu_time); ready_tail->next=p; ready_tail=p; } q=(struct QueueNode *)malloc(sizeof(struct QueueNode)); q->next =NULL; block_head=block_tail=q; for(i=0;i { q=(struct QueueNode *)malloc(sizeof(struct QueueNode)); q->next=NULL; q->PCB.state=0; printf(“输入阻塞进程%d的pid和cpu_time:”,i+1); scanf(“%d%d”,&q->PCB.pid,&q->PCB.cpu_time); block_tail->next=q; block_tail=q; } printf(“n处于就绪状态的进程有:n”); p=ready_head->next; i=1; while(p) {printf(“进程%d的pid和cpu_time:%5d%5d%5dn“,i,p->PCB.pid,p->PCB.state,p->PCB.cpu_time); p=p->next; i++; } } void dispath() //模拟调度 { int x=0,t; use_cpu=0; unuse_cpu=0; printf(”输入t:“); scanf(”%d“,&t); printf(”开始调度n“); while(ready_head!=ready_tail||block_head!=block_tail) { struct QueueNode *p,*q; if(ready_head!=ready_tail) { p=ready_head->next; ready_head->next=p->next; p->next=NULL; if(ready_head->next==NULL) { ready_tail=ready_head; } p->PCB.state=2; printf(”进程%d调度t“,p->PCB.pid); state和 use_cpu++; x++; p->PCB.cpu_time--; if(p->PCB.cpu_time) { ready_tail->next=p; ready_tail=p; } else { printf(”进程%d完成t“,p->PCB.pid); free(p); } } else { unuse_cpu++; x++; printf(”空闲一个时间片t“); } if(x==t&&block_head!=block_tail) { q=block_head->next; block_head->next=q->next; q->next=NULL; if(block_head->next==NULL) { block_tail=block_head; } ready_tail->next=q; ready_tail=q; x=0; } } } void calculate() //计算CPU利用率 { printf(”ncpu的利用率%。2fn“,(float)use_cpu/(use_cpu+unuse_cpu)); } void main() {start_state(); dispath(); calculate(); } 6运行结果: 7实验总结: 实验帮我复习了数据结构和C语言,且巩固课本知识,知道了如何定义结构体,如何在链接队列中增删节点。模拟进程调度帮我们巩固了进程三状态之间的变迁。懂得调式的重要性。总之,我们明白了理论联系实际。多看书,多上机。 实验三 可变分区存储管理 1.目的和要求 通过这次实验,加深对内存管理的认识,进一步掌握内存的分配、回收算法的思想。 2.实验内容 阅读教材《计算机操作系统》第四章,掌握存储器管理相关概念和原理。 编写程序模拟实现内存的动态分区法存储管理。内存空闲区使用自由链管理,采用最坏适应算法从自由链中寻找空闲区进行分配,内存回收时假定不做与相邻空闲区的合并。 假定系统的内存共640K,初始状态为操作系统本身占用64K。在t1时间之后,有作业A、B、C、D分别请求8K、16K、64K、124K的内存空间;在t2时间之后,作业C完成;在t3时间之后,作业E请求50K的内存空间;在t4时间之后,作业D完成。要求编程序分别输出t1、t2、t3、t4时刻内存的空闲区的状态。 3.实验环境 Windows操作系统、VC++6.0 C语言 4、设计思想 模拟内存分配和回收,要设置两个链队列,一个空闲区链和一个占用区链,空闲区链节点有起始地址,大小和指向下一节点的指针等数据域,占用区链节点有起始地址,大小,作业名和指向下一节点的指针等数据域,本实验用最坏适应算法,每次作业申请内存都是从空闲链队头节点分配,如果相等,就删除空闲头结点,如果小于申请的,就不分配,否则就划分内存给作业,剩下的内存大小,重新插入空闲链队,按从大到小,接着把作业占用的内存放到占用区链节点的末尾。每次作业运行完,就要回收其占用的内存大小,把作业节点按从大到小插入到空闲链队中。 5.源代码: #include struct freelinkNode *next; }; struct busylinkNode{ char name; int len; int address; struct busylinkNode *next; }; struct freelinkNode *free_head=NULL; //自由链队列(带头结点)队首指针 struct busylinkNode *busy_head=NULL; //占用区队列队(带头结点)首指针 struct busylinkNode *busy_tail=NULL; //占用区队列队尾指针 void start(void) /* 设置系统初始状态*/ { struct freelinkNode *p; struct busylinkNode *q; free_head=(struct freelinkNode*)malloc(sizeof(struct freelinkNode)); free_head->next=NULL; // 创建自由链头结点 busy_head=busy_tail=(struct busylinkNode*)malloc(sizeof(struct busylinkNode)); busy_head->next=NULL; // 创建占用链头结点 p=(struct freelinkNode *)malloc(sizeof(struct freelinkNode)); p->address=64; p->len=640-64;//OS占用了64K p->next=NULL; free_head->next=p; q=(struct busylinkNode *)malloc(sizeof(struct busylinkNode)); q->name='S'; /* S表示操作系统占用 */ q->len=64; q->address=0; q->next=NULL; busy_head->next=q; busy_tail=q; } void requireMemo(char name, int require) /*模拟内存分配*/ { freelinkNode *w,*u,*v; busylinkNode *p; if(free_head->next->len>=require) { p=(struct busylinkNode*)malloc(sizeof(struct busylinkNode)); p->name=name; p->address=free_head->next->address; p->len=require; p->next=NULL; busy_tail->next=p; busy_tail=p; } else printf(”Can't allocate“); w=free_head->next; free_head->next=w->next; if(w->len==require) { free(w); } else { w->address=w->address+require; w->len=w->len-require; } u=free_head; v=free_head->next; while((v!=NULL)&&(v->len>w->len)) { u=v; v=v->next; } u->next=w; w->next=v; } void freeMemo(char name) /* 模拟内存回收*/ { int len; int address; busylinkNode *q,*p; freelinkNode *w,*u,*v; q=busy_head; p=busy_head->next; while((p!=NULL)&&(p->name!=name)) { q=p; p=p->next; } if (p==NULL) { printf(”%c is not exist“,name); } else { if(p==busy_tail) { busy_tail=q; } else { q->next=p->next; len=p->len; address=p->address; free(p); w=(struct freelinkNode*)malloc(sizeof(struct freelinkNode)); w->len=len; w->address=address; u=free_head; v=free_head->next; while((v!=NULL)&&(v->len>len)) { u=v; v=v->next; } u->next=w; w->next=v; } } } void past(int time) /* 模拟系统过了time 时间*/ { printf(”过了时间%d后:n“,time); } void printlink() /* 输出内存空闲情况(自由链的结点) */ { freelinkNode *p; printf(”内存的空闲情况为:n“); p=(struct freelinkNode *)malloc(sizeof(struct freelinkNode)); p=free_head->next; while(p!=NULL) { printf(”内存的起始地址和内存的大小%5dt%5d:n",p->address,p->len); p=p->next; } } void main() { int t1=1,t2=2,t3=3,t4=4; start(); past(t1); requireMemo('A',8); requireMemo('B',16); requireMemo('C',64); requireMemo('D',124); printlink(); past(t2); freeMemo('C'); printlink(); past(t3); requireMemo('E',50); printlink(); past(t4); freeMemo('D' ); printlink(); } 6.运行结果: 7、实验总结: 巩固编程能力,和调式能力,复习课本知识,明白理论联系实际的重要性,动手能力非常重要,多看书,多独立思考,品味痛苦的过程,享受成功的喜悦。 操作系统实验报告 院系:数计学院 班级:大类6班 学号:100511624 姓名:明章辉 指导教师:徐军利 操作 系统 实验报告 实验名称: 线程 控制实验 计算机科学与技术学院 目录 一、实验目的和要求 2 二、实验内容 2 三、实验步骤 2 四、实验结果与分析 3 1.单线程 3 2.单线程(睡眠 4s) 3 3.多线程 4 4.多线程(每个子线程睡眠 1s) 4 5.单线程与多线程对比 5 五、程序源代码 5 1.单线程实验代码 5 2.单线程实验代码 6 六、实验体会 7 一、实验目的和要求 通过本实验掌握在 Linux 操作系统中遵循 Posix线程标准接口进行多线程程序编程,熟练掌握线程的创建 pthread_create(),线程的终止 pthread_exit(),等待线程合并 pthread_join()等线程控制操作,利用信号量或者互斥锁实现线程建的同步。 二、实验内容 问题:求 1000000 个浮点数(精确到小数点后 4 位)的平均值(和,最大值,最小值),具体的问题描述流程图如下图图 1 所示: 三 、实验 步骤 1、随机生成1000000个浮点数; 2、创建 4个子线程,分别求 250000个浮点数之和; 3、完成 1000000 个浮点数之和并打印结果; 4、统计多线程并发执行完成计算的时间; 5、写一个单线程程序,同样完成 1000000 个随机数求和的计算,统计计算时间,并和前面结果进行对比; 6、让单线程程序睡眠四秒钟、多线程程序各子程序睡一秒的条件下(两个程序总的睡眠时间相同),对比执行结果; 7、分析两次对比结果的差异,写出自己的见解。 四、实验结果与分析1、单线程完成 1000000 个浮点数的求和运算所用的时间情况如下图图 2 所示: 图 图 2 单线程计算 时间 分析:实验中每次随机产生一个 0 到 1 之间的浮点数,1000000 个这样的数相加的话的平总和大概就在 500000 左右(按照随机数的平均值原理),实验中 sum=,显然结果正确,整个计算运行时间为。 2、单线程完成1000000 个浮点数的求和运算,单线程中睡眠 4 秒钟,所用的时间情况如下图图3 所示: 图 图 3 单线程计算 时间( 睡眠 4 秒) 分析:根据上一次单线程的执行情况来看,这一次让单线程睡眠 4 秒钟,最后执行时间刚好就是4 秒加上计算时间。也就是说计算 1000000 个浮点数的总和平均时间约为。 3、四个子线程共同完成1000000 个浮点数的求和计算所用时间情况如下图图 4所示: 图 图 4 多线程计算时间 分析:因为这次是 4 个子线程并发运行,每个子线程只需计算 250000个浮点数的总和,理想情况下这时候的运行时间应该是这单线程中计算时间的四分之一。从图中可以看到执行时间是,很显然这个时间约为单线程求 1000000 个浮点数之和的时间()的四分之一,符合预期的结果。 4、四个子线程共同完成1000000 个浮点数的求和计算,其中每个子线程睡眠 1 秒钟,最终所用时间情况如下图图 5所示: 图 图 5 多线程计算时间( 每个 子线程眠 睡眠 1 秒) 分析:这里四个子线程每个子线程睡眠一秒,但由于四个子线程并发同步的在执行,当一个子线程在睡眠时,另外一个子线程却仍然在继续求和计算,因此他们一起合作同步完成1000000个浮点数的计算所需的时间就是 1 秒加上上图中不睡眠的时候的计算时间。从图中可以 看到≈1s+,所以最终的结果符合预期值。 5、单线程计算时间(睡眠4s)与多线程计算时间(每个子线程睡眠 1s)对比效果如下图图 6 所示: 图 图 6 单线程 ( 睡眠 4s)与 与 多线程 ( 每个眠 子线程睡眠 1s) 计算时间 对比图 五、程序源代码 /************************* *FileName: *Author: *Date:2013/11/22 ***************************/ #include<>#include<>#include<>#include<>#include int i; srand(time(NULL)); for(i=0;i { SUM += (float)(rand()/(float)RAND_MAX); } sleep(4); } int main() { pthread_t p; int result; float time; struct timeval start; struct timeval end; gettimeofday(&start,NULL); result=pthread_create(&p,NULL,ADD,NULL); if(result!=0) { printf(“Create Thread of ADD Failuren”); exit(-1); } pthread_join(p,NULL); gettimeofday(&end,NULL); time = ((float) - *1000000+(float) - )/1000000; printf(“Signal_Thread_Sum:%。4fn”,SUM); printf(“Signal_Thread_Execution_Time:%。4fs(sleep 4 sec)n”,time); return 0; } /************************* *FileName: *Author:wangtao *Date:2013/11/22 ***************************/ #include<>#include<>#include<>#include<>#include int i; srand(time(NULL)); for(i=0;i { pthread_mutex_lock(&mutex); SUM += (float)((float)rand()/RAND_MAX); pthread_mutex_unlock(&mutex); } printf(“pthread%d:%。4fn”,*k,SUM); sleep(1); } int main(void) { pthread_t p1,p2,p3,p4; int result1,result2,result3,result4; int k1=1,k2=2,k3=3,k4=4; struct timeval start; struct timeval end; float time; gettimeofday(&start,NULL); pthread_mutex_init(&mutex,NULL); result1=pthread_create(&p1,NULL,(void*)ADD,&k1); result2=pthread_create(&p2,NULL,(void*)ADD,&k2); result3=pthread_create(&p3,NULL,(void*)ADD,&k3); result4=pthread_create(&p4,NULL,(void*)ADD,&k4); if(result1!=0||result2!=0||result3!=0||result4!=0) { printf(“Create Child Thread Failure!n”); exit(1); } pthread_join(p1,NULL); pthread_join(p2,NULL); pthread_join(p3,NULL); pthread_join(p4,NULL); gettimeofday(&end,NULL); time = ((float) - *1000000 + (float) - )/1000000; printf(“SUM = %。4fn”,SUM); printf(“Multi_thread_time = %。4fs(Each child thread sleep 1 sec)n”,time); return 0; } 六、实验体会 这是第一次使用多线程编程编写代码,第一次直观感受到这种多线程编程对程序的执行速率的影响。虽然说操作系统课程已经上了好几个星期了,课堂上一直在学习多线程编程的算法思想,但是那只是书面上的讲授,真正直观的感受和体会还是得依靠实验来了解。 因为之前就接触过 linux 系统,所以对于程序的编译执行方面还是问题不大,最主要的就是代码的编写问题。一开始到实验室完全不知道要做什么,因为根本连实验内容都不知道,直到助教在大屏幕上显示这个实验题目,我才开始了解实验的题目和要求。这里我就是想建议一下老师您可以应该给我们实验题目,让我们在实验前就了解一下实验内容,不然到了实验室都不知道到底要干嘛。读懂了解题意之后,我就开始参考所给的一些线程创建函数说明来编写多线程同步完成1000000个浮点数的求和运算。因为对这些线程函数不是很了解,在使用 pthread_create()函数创建线程时,老是传入函数参数不对,后来百度之后了解到传入的子线程函数的类型应该为 void * (*start_thread)(void)形式。最后运行试验与单线程的进行比较,强烈感受到多线程的并发同步运行的特点。 总之,这次试验使我对课堂知识有了更深的体会和巩固,为以后的课程学习打下了基础。 它山之石可以攻玉,以上就是差异网为大家带来的9篇《操作系统实验报告》,希望可以对您的写作有一定的参考作用。嵌入式操作系统实验报告 篇三
操作系统实验报告 篇四
操作系统实验报告 篇五
操作系统实验报告 篇六
操作系统实验报告 篇七
操作系统实验报告 篇八
操作系统第一次实验报告 篇九