合操作系统实验指导书612Word格式文档下载.docx

上传人:b****6 文档编号:20170048 上传时间:2023-01-17 格式:DOCX 页数:38 大小:536.83KB
下载 相关 举报
合操作系统实验指导书612Word格式文档下载.docx_第1页
第1页 / 共38页
合操作系统实验指导书612Word格式文档下载.docx_第2页
第2页 / 共38页
合操作系统实验指导书612Word格式文档下载.docx_第3页
第3页 / 共38页
合操作系统实验指导书612Word格式文档下载.docx_第4页
第4页 / 共38页
合操作系统实验指导书612Word格式文档下载.docx_第5页
第5页 / 共38页
点击查看更多>>
下载资源
资源描述

合操作系统实验指导书612Word格式文档下载.docx

《合操作系统实验指导书612Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《合操作系统实验指导书612Word格式文档下载.docx(38页珍藏版)》请在冰豆网上搜索。

合操作系统实验指导书612Word格式文档下载.docx

具体操作:

点击“任务栏→位置→主文件夹”,打开主文件夹位置文件浏览器,空白处右键单击,弹出菜单选择“创建文档→空文件”,新建一个空文件,并命名为“hello.c”,右键单击“hello.c”,选择“使用Gedit打开”,在Gedit编辑器中编辑代码如下:

#include<

stdio.h>

intmain()

{

printf("

Hello,Wrold!

\n"

);

}

编辑完成后,点击“保存”,保存文件。

voidmain(){

intx,y;

pleaseinputx:

"

scanf("

%d"

&

x);

if(x<

6)

{

y=x-12;

printf("

x=%d,y=%d\n"

x,y);

}

elseif(x<

15)

{

y=3*x-1;

printf("

}

else

{

y=5*x+9;

printf("

}

2.使用gcc编译源程序。

gcc是linux下的一种c程序编译工具,使用方法如下:

编译:

gcc-ofilename1filename.c(或者gccfilename.c-ofilename1),其中:

filename.c是源文件名,filename1是目标文件名,o代表object

点击“任务栏→应用程序→附件→终端”,当前默认路径即为主文件夹,输入“gcchello.c-ohello”,回车运行后,若无任何提示,怎说明编译成功,已生成可执行文件“hello“,若提示有错误,则根据具体提示回到Gedit中修改源程序,保存后重新编译.

3.执行程序

执行:

./filenamel其中:

filename1是目标文件名。

在“终端”中输入“./hello”,回车后运行,若无错误,终端中将显示运行结果“Hello,Wrold!

”。

5、注意事项

1.gcc编译器不能编译不带扩展名的c语言程序。

2.注意编译和运行程序的基本过程。

3.重新编辑源程序后,必须重新编译,才会生成新的可执行程序。

实验项目二进程管理

2、实验目的

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

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

3、实验预备知识

1.fork()函数

头文件:

unistd.h>

sys/types.h>

函数原型:

pid_tfork( 

void);

(pid_t是一个宏定义,其实质是int,被定义在#include<

中)

返回值:

若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;

否则,出错返回-1

函数说明:

一个现有进程可以调用fork函数创建一个新进程。

由fork创建的新进程被称为子进程(childprocess)。

fork函数被调用一次但返回两次。

两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。

子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。

注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。

linux将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。

为什么fork会返回两次?

由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回。

因为fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,这两次的返回值是不一样的,过程如图2.1。

调用fork之后,数据、堆栈有两份,代码仍然为一份但是这个代码段成为两个进程的共享代码段都从fork函数中返回,箭头表示各自的执行处。

当父子进程有一个想要修改代码段时,两个进程真正分裂。

图2.1fork()函数分裂示意图

示例代码:

//对于此程序而言此头文件用不到

int 

main(int 

argc, 

char 

**argv)

pid=fork();

if 

(pid<

0)

printf("

error!

else 

if(pid==0)

Thisisthechildprocess!

else

Thisistheparentprocess!

childprocessid=%d"

pid);

return 

0;

fork函数的特点概括起来就是“调用一次,返回两次”,在父进程中调用一次,在父进程和子进程中各返回一次。

fork的另一个特性是所有由父进程打开的描述符都被复制到子进程中。

父、子进程中相同编号的文件描述符在内核中指向同一个file结构体,也就是说,file结构体的引用计数要增加。

2.wait()函数

sys/wait.h>

pid_twait(int*status);

如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。

wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。

如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。

子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一起返回。

如果不在意结束状态值,则参数status可以设成NULL。

子进程的结束状态值请参考下面的waitpid()。

stdlib.h>

intmain(int 

**argv)

pid_tpid;

intstatus,i;

if(fork()==0)

Thisisthechildprocess.pid=%d\n"

,getpid());

exit(5);

else

sleep

(1);

Thisistheparentprocess,waitforchild...\n"

);

pid=wait(&

status);

i=WEXITSTATUS(status);

child’spid=%d.exitstatus=%d\n"

,pid,i);

3.waitpid()函数

pid_twaitpid(pid_tpid,int*status,intoptions);

waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。

子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一快返回。

参数pid为欲等待的子进程识别码,其他数值意义如下:

pid<

-1等待进程组识别码为pid 

绝对值的任何子进程。

pid=-1等待任何子进程,相当于wait()。

pid=0等待进程组识别码与目前进程相同的任何子进程。

pid>

0等待任何子进程识别码为pid的子进程。

参数option可以为0或下面的组合:

WNOHANG如果没有任何已经结束的子进程则马上返回,不予以等待。

WUNTRACED如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。

子进程的结束状态返回后存于status,底下有几个宏可判别结束情况:

WIFEXITED(status)如果子进程正常结束则为非0值。

WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED来判断是否正常结束才能使用此宏。

WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真。

WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED来判断后才使用此宏。

WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真。

一般只有使用WUNTRACED时才会有此情况。

WSTOPSIG(status)取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED来判断后才使用此宏。

4.exit()函数

stdlib.h>

voidexit(intstatus);

无。

进程结束正常终止,返回结束状态。

status为进程结束状态,是返回给父进程的一个整数,以备查考。

为了及时回收进程所占用的资源并减少父进程的干预,UNIX/LINUX利用exit()来实现进程的自我终止,通常父进程在创建子进程时,应在进程的末尾安排一条exit(),使子进程自我终止。

exit(0)表示进程正常终止,exit

(1)表示进程运行有错,异常终止。

如果调用进程在执行exit()时,其父进程正在等待它的终止,则父进程可立即得到其返回的整数。

核心须为exit()完成以下操作:

(1)关闭软中断

(2)回收资源

(3)写记帐信息

(4)置进程为“僵死状态”

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

intpid;

forkprogramstarring\n"

pid=fork();

if(pid==0)

son\n"

else

{

pid=fork();

if(pid==0)

daughter\n"

else

father\n"

2.多次连续反复运行这个程序,观察屏幕显示结果的顺序,直至出现不一样的情况为止。

记下这种情况,试简单分析其原因。

3.修改程序,在父、子进程中分别使用wait()、exit()等系统调用“实现”其同步推进,并获取子进程的ID号及结束状态值。

多次反复运行改进后的程序,观察并记录运行结果。

5、实验报告要求

1.列出实验内容1、3各程序清单,并以截图形式记录相应运行结果。

2.对实验运行结果进行分析:

1)实验内容1运行结果为什么无固定顺序,fork()函数创建进程是如何并发执行的。

2)实验内容3是如何实现父子进程的同步执行的。

intmain(){

intn;

char*message;

intpid,status,i;

if(pid==0)

message="

son"

;

n=3;

}

if(pid==0){

message="

daughter"

n=3;

else{

father"

for(;

n>

n--){

puts(message);

实验项目三进程调度

1.理解进程控制块和进程组织方式;

2.掌握时间片轮转调度算法实现处理机调度。

1.实验基本原理

进程控制块通过链表队列的方式组织起来,系统中存在运行队列和就绪队列(为简单起见,不设阻塞队列),进程的调度就是进程控制块在运行队列和就绪队列之间的切换。

当需要调度时,从就绪队列中挑选一个进程占用处理机,即从就绪队列中删除一个进程,插入到运行队列中,当占用处理机的进程运行的时间片完成后,放弃处理机,即在运行队列中的进程控制块经过一段时间(时间片)后,从该队列上删除,如果该进程运行完毕,则删除该进程(节点);

否则,则插入到就绪队列中。

2.实验中使用的数据结构

(1)PCB进程控制块

内容包括参数①进程名name;

②要求运行时间runtime;

③已运行时间runedtime;

④本轮运行时间killtime。

(2)进程队列

为简单起见,只设运行队列,就绪队列两种数据结构,进程的调度在这两个队列中切换,如图3.1所示。

图3.1PCB链表

3.rand()函数和srand()函数

库函数中系统提供了两个函数用于产生随机数:

srand()和rand()。

函数一:

intrand(void);

从srand(seed)中指定的seed开始,返回一个[0,RAND_MAX(0x7fff)]间的随机整数。

函数二:

voidsrand(unsignedseed);

参数seed是rand()的种子,用来初始化rand()的起始值。

函数rand()是真正的随机数生成器,而srand()会设置供rand()使用的随机数种子。

如果你在第一次调用rand()之前没有调用srand(),那么系统会为你自动调用srand()。

而使用同种子相同的数调用srand()会导致相同的随机数序列被生成。

srand((unsigned)time(NULL))则使用系统定时/计数器的值做为随机种子。

每个种子对应一组根据算法预先生成的随机数,所以,在相同的平台环境下,不同时间产生的随机数会是不同的,相应的,若将srand(unsigned)time(NULL)改为srand(TP)(TP为任一常量),则无论何时运行、运行多少次得到的“随机数”都会是一组固定的序列,因此srand生成的随机数是伪随机数。

但是,要注意的是所谓的“伪随机数”指的并不是假的随机数。

其实绝对的随机数只是一种理想状态的随机数,计算机只能生成相对的随机数即伪随机数。

计算机生成的伪随机数既是随机的又是有规律的——一部份遵守一定的规律,一部份则不遵守任何规律。

比如“世上没有两片形状完全相同的树叶”,这体现到了事物的特性——差异性;

但是每种树的叶子都有近似的形状,这正是事物的共性——规律性。

从这个角度讲,我们就可以接受这样的事实了:

计算机只能产生伪随机数而不是绝对的随机数。

系统在调用rand()之前都会自动调用srand(),如果用户在rand()之前曾调用过srand()给参数seed指定了一个值,那么rand()就会将seed的值作为产生伪随机数的初始值;

而如果用户在rand()前没有调用过srand(),那么系统默认将1作为伪随机数的初始值。

如果给了一个定值,那么每次rand()产生的随机数序列都是一样的。

所以为了避免上述情况的发生我们通常用srand((unsigned)time(0))或者srand((unsigned)time(NULL))来产生种子。

如果仍然觉得时间间隔太小,可以在(unsigned)time(0)或者(unsigned)time(NULL)后面乘上某个合适的整数。

例如,srand((unsigned)time(NULL)*10)。

另外,关于time_ttime(0):

time_t被定义为长整型,它返回从1970年1月1日零时零分零秒到目前为止所经过的时间,单位为秒。

srand()、rand()用法举例:

time.h>

voidmain()

inti,j;

srand(10);

//srand((int)time(0));

for(i=0;

i<

10;

i++)

j=(int)(rand())%20;

%d\n"

j);

4.malloc()函数

malloc.h>

void*malloc(unsignedintsize);

其作用是在内存的动态存储区中分配一个长度为size的连续空间,此函数的值(即“返回值”)是一个指向分配域其实地址的指针(类型为void)。

若此函数未能成功执行则返回空指针。

5.程序流程图

图3.2模拟进程调度的流程图

6.部分参考程序

(1)PCB数据结构

structPCB

intname;

intruntime;

intrunedtime;

intkilltime;

structPCB*next;

};

typedefstructPCBPCB;

(2)创建就绪列表

#defineLENsizeof(PCB)

PCB*runqueue;

//运行队列指针

PCB*top,*tail,*temp;

//就绪队列指针

inti;

srand((int)time(0));

for(i=0;

i<

NUM;

i++)

temp=(PCB*)malloc(LEN);

temp->

name=i;

runtime=rand()%15;

runedtime=0;

next=NULL;

killtime=0;

if(i==0)

top=temp;

tail=temp;

tail->

next=temp;

processname%d,runtime=%d,runedtime=%d,killtime=%d\n"

tail->

name,tail->

runtime,tail->

runedtime,tail->

killtime);

7.按时间片轮转算法进行进程调度的过程描述。

第1步:

取就绪队列的队首结点为运行队列的结点,修改就绪队列队首指针后移;

第2步:

调度运行队列结点,即运行队列结点的要求运行时间减去时间片时间;

第3步:

a.若修改后要求运行时间<

=0,则表示该进程结点运行完毕,修改进程结点的PCB信息,记录runtime,runedtime,killtime等信息。

并将结点信息输出。

b.否则,表示该进程结点未完成,记录runtime,runedtime,killtime等信息,将结点信息输出。

并将该结点置于就绪队列的队尾,等待下次调度,同时修改队尾指针。

第4步:

若就绪队列非空,则继续执行第1步,直至就绪队列为空。

1.建立合理的PCB数据结构,建立含有8个进程结点的就绪队列,每个进程的要求运行时间随机产生,要求每个进程的要求运行时间不大于15。

2.设置时间片大小(3~6),使用时间片轮转调度算法实现处理机调度。

1.列出实验内容所要求的程序清单,并以截图形式记录相应运行结果;

如果时间片设置值过大或过小,会对进程的调度产生何种影响。

实验项目四进程通信

本实验为综合性实验。

1.了解什么是消息,熟悉消息传送原理。

2.了解和熟悉共享存储机制。

3.掌握消息的发送与接收的实现方法。

任务一消息的发送和接收

消息(message)是一个格式化的可变长的信息

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 其它课程

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1