这里能搜索到更多你想要的范文→
当前位置:好范文网 > 实用范文 > 其他范文 >

操作系统实验报告【新版多篇】

发布时间:2023-07-24 11:52:32 审核编辑:本站小编下载该Word文档收藏本文

【说明】操作系统实验报告【新版多篇】为好范文网的会员投稿推荐,但愿对你的学习工作带来帮助。

操作系统实验报告【新版多篇】

操作系统实验报告 篇一

实验二

进程调度

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#include

struct PCB_type {

int pid ;

//进程名

int

state ;

//进程状态

//2--表示“执行”状态

//1--表示“就绪”状态

//0--表示“阻塞”状态

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#includestruct freelinkNode{ int len; int address;

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 姓名:明章辉

指导教师:徐军利

操作系统实验报告 篇二

一、实验目的

模拟文件系统实现的基本功能,了解文件系统的基本结构和文件的各种管理方法,加深理解文件系统的内部功能及内部实现。通过用高级语言编写和调试一个简单的文件系统,模拟文件管理的工作过程,从而对各种文件操作命令的实质内容和执行过程有比较深入的了解。

二、实验内容和要求

编程模拟一个简单的文件系统,实现文件系统的管理和控制功能。要求本文件系统采用两级目录,即设置主文件目录[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;

操作系统实验报告 篇三

实验项目二 进程管理

一、实验目的

1、理解进程的概念,掌握父、子进程创建的方法。

2、认识和了解并发执行的实质,掌握进程的并发及同步操作。

二、实验内容

1、编写一C语言程序,实现在程序运行时通过系统调用fork( )创建两个子进程,使父、子三进程并发执行,父亲进程执行时屏幕显示“I am father”,儿子进程执行时屏幕显示“I am son”,女儿进程执行时屏幕显示“I am daughter”。

2、多次连续反复运行这个程序,观察屏幕显示结果的顺序,直至出现不一样的情况为止。记下这种情况,试简单分析其原因。

3、修改程序,在父、子进程中分别使用wait()、exit()等系统调用“实现”其同步推进,并获取子进程的ID号及结束状态值。多次反复运行改进后的程序,观察并记录运行结果。

三、源程序及运行结果

源程序1:

#include#include #includeint main(int argc, char ** argv ) { int pid=fork(); if(pid<0)

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#include #includeint main(int argc, char ** argv ) { char *message; int n;

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这个系统,操作不熟悉,多次操作后,了解这个系统有关操作,实验做起来就比较简单了。对实验代码也不熟悉,最后通过请教老师和同学,终于实验理解透彻,并成功运行了。不仅上课要认真听课,要想真正学会,课下也要付出努力。

嵌入式操作系统实验报告 篇四

实验一 嵌入式开发环境的建立

一、实验目的

通过此实验系统,读者可以了解嵌入式实时操作系统 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。

你也可以在好范文网搜索更多本站小编为你整理的其他操作系统实验报告【新版多篇】范文。

word该篇DOC格式操作系统实验报告【新版多篇】范文,共有10427个字。好范文网为全国范文类知名网站,下载本文稍作修改便可使用,即刻完成写稿任务。立即下载:
操作系统实验报告【新版多篇】下载
操作系统实验报告【新版多篇】.doc
下载Word文档到电脑,方便编辑和打印
编辑推荐: 星级推荐 星级推荐 星级推荐 星级推荐 星级推荐
下载该Word文档
好范文在线客服
  • 问题咨询 QQ
  • 投诉建议 QQ
  • 常见帮助 QQ
  • 暂无