操作系统实验实验1.docx

上传人:b****7 文档编号:25434459 上传时间:2023-06-08 格式:DOCX 页数:27 大小:250.27KB
下载 相关 举报
操作系统实验实验1.docx_第1页
第1页 / 共27页
操作系统实验实验1.docx_第2页
第2页 / 共27页
操作系统实验实验1.docx_第3页
第3页 / 共27页
操作系统实验实验1.docx_第4页
第4页 / 共27页
操作系统实验实验1.docx_第5页
第5页 / 共27页
点击查看更多>>
下载资源
资源描述

操作系统实验实验1.docx

《操作系统实验实验1.docx》由会员分享,可在线阅读,更多相关《操作系统实验实验1.docx(27页珍藏版)》请在冰豆网上搜索。

操作系统实验实验1.docx

操作系统实验实验1

广州大学学生实验报告

开课学院及实验室:

计算机科学与工程实验室2015年11月10日

实验课程名称

操作系统实验

成绩

实验项目名称

进程管理与进程通信

指导老师

(***报告只能为文字和图片,老师评语将添加到此处,学生请勿作答***)

1、实验目的

1.1、掌握进程的概念,明确进程的含义

1.2、认识并了解并发执行的实质

2.1、掌握进程另外的创建方法

2.2、熟悉进程的睡眠、同步、撤消等进程控制方法

3.1、进一步认识并发执行的实质

3.2、分析进程竞争资源的现象,学习解决进程互斥的方法

4.1、了解守护进程

5.1、了解什么是信号

5.2、INUX系统中进程之间软中断通信的基本原理

6.1、了解什么是管道

6.2、熟悉UNIX/LINUX支持的管道通信方式

7.1、了解什么是消息

7.2、熟悉消息传送的机理

8.1、了解和熟悉共享存储机制

二、实验内容

1.1、编写一段程序,使用系统调用fork()创建两个子进程。

当此程序运行时,在系统中有一个父进程和两个子进程活动。

让每一个进程在屏幕上显示一个字符:

父进程显示'a',子进程分别显示字符'b'和字符'c'。

试观察记录屏幕上的显示结果,并分析原因。

1.2、修改上述程序,每一个进程循环显示一句话。

子进程显示'daughter…'及'son……',父进程显示'parent……',观察结果,分析原因。

2.1、用fork()创建一个进程,再调用exec()用新的程序替换该子进程的内容

2.2、利用wait()来控制进程执行顺序

3.1、修改实验

(一)中的程序2,用lockf()来给每一个进程加锁,以实现进程之间的互斥

3.2、观察并分析出现的现象

4.1、写一个使用守护进程(daemon)的程序,来实现:

创建一个日志文件/var/log/Mydaemon.log;

每分钟都向其中写入一个时间戳(使用time_t的格式);

5.1、用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上来的中断信号(即按^c键);捕捉到中断信号后,父进程用系统调用kill()向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止:

Childprocess1iskilledbyparent!

Childprocess2iskilledbyparent!

父进程等待两个子进程终止后,输出如下的信息后终止:

Parentprocessiskilled!

5.2、用软中断通信实现进程同步的机理

6.1、编写程序实现进程的管道通信。

用系统调用pipe()建立一管道,二个子进程P1和P2分别向管道各写一句话:

Child1issendingamessage!

Child2issendingamessage!

父进程从管道中读出二个来自子进程的信息并显示(要求先接收P1,后P2)。

7.1、消息的创建、发送和接收。

使用系统调用msgget(),msgsnd(),msgrev(),及msgctl()编制一长度为1k的消息发送和接收的程序。

8.1、编制一长度为1k的共享存储区发送和接收的程序。

三、实验原理

1、程既是一个独立拥有资源的基本单位,又是一个独立调度的基本单位。

一个进程实体由若干个区(段)组成,包括程序区、数据区、栈区、共享存储区等。

每个区又分为若干页,每个进程配置有唯一的进程控制块PCB,用于控制和管理进程。

2、进程是进程映像的执行过程,也就是正在执行的进程实体。

3、fork()返回值意义如下:

0:

在子进程中,pid变量保存的fork()返回值为0,表示当前进程是子进程。

>0:

在父进程中,pid变量保存的fork()返回值为子进程的id值(进程唯一标识符)。

4、NIX/LINUX中fork()是一个非常有用的系统调用,但在UNIX/LINUX中建立进程除了fork()之外,也可用与fork()配合使用的exec()。

5、lockf(files,function,size)用作锁定文件的某些段或者整个文件。

6、每个信号都对应一个正整数常量(称为signalnumber,即信号编号。

定义在系统头文件中),代表同一用户的诸进程之间传送事先约定的信息的类型,用于通知某进程发生了某异常事件。

每个进程在运行时,都要通过信号机制来检查是否有信号到达。

若有,便中断正在执行的程序,转向与该信号相对应的处理程序,以完成对该事件的处理;处理结束后再返回到原来的断点继续执行。

实质上,信号机制是对中断机制的一种模拟,故在早期的UNIX版本中又把它称为软中断。

7、信号的发送,是指由发送进程把信号送到指定进程的信号域的某一位上。

如果目标进程正在一个可被中断的优先级上睡眠,核心便将它唤醒,发送进程就此结束。

一个进程可能在其信号域中有多个位被置位,代表有多种类型的信号到达,但对于一类信号,进程却只能记住其中的某一个。

8、当一个进程要进入或退出一个低优先级睡眠状态时,或一个进程即将从核心态返回用户态时,核心都要检查该进程是否已收到软中断。

当进程处于核心态时,即使收到软中断也不予理睬;只有当它返回到用户态后,才处理软中断信号。

9、管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信的一个共享文件,又称为pipe文件。

10、消息机制允许由一个进程给其它任意的进程发送一个消息。

11、该机制可使若干进程共享主存中的某一个区域,且使该区域出现(映射)在多个

进程的虚地址空间中。

四、实验设备

Linux系统

五、实验程序

1、创建子进程相关代码

#include

#include

intmain()

{

intp1,p2;

while((p1=fork())==-1);/*创建子进程p1*/

if(p1==0)putchar('b');

else

{

while((p2=fork())==-1);/*创建子进程p2*/

if(p2==0)putchar('c');

elseputchar('a');

}

}

修改后:

#include

#include

intmain()

{

intp1,p2,i;

while((p1=fork())==-1);/*创建子进程p1*/

if(p1==0)

for(i=0;i<10;i++)

printf("daughter%d\n",i);

else

{

while((p2=fork())==-1);/*创建子进程p2*/

if(p2==0)

for(i=0;i<10;i++)

printf("son%d\n",i);

else

for(i=0;i<10;i++)

printf("parent%d\n",i);

}

}

 

2、exec,wait相关代码

#include

#include

#include

#include

voidmain()

{

intpid;

pid=fork();/*创建子进程*/

switch(pid)

{

case-1:

/*创建失败*/

printf("forkfail!

\n");

exit

(1);

case0:

/*子进程*/

execl("/bin/ls","ls","-1","-color",NULL);

printf("execfail!

\n");

exit

(1);

default:

/*父进程*/

wait(NULL);/*同步*/

printf("lscompleted!

\n");

exit(0);

}

}

 

3、lockf相关代码

#include

#include

#include

intmain()

{

intp1,p2,i;

while((p1=fork())==-1);/*创建子进程p1*/

if(p1==0)

{

lockf(1,1,0);/*加锁,这里第一个参数为stdout(标准输出设备的描述符)*/

for(i=0;i<10;i++)

printf("daughter%d\n",i);

lockf(1,0,0);/*解锁*/

}

else

{

while((p2=fork())==-1);/*创建子进程p2*/

if(p2==0)

{

lockf(1,1,0);/*加锁*/

for(i=0;i<10;i++)

printf("son%d\n",i);

lockf(1,0,0);/*解锁*/

}

else

{

lockf(1,1,0);/*加锁*/

for(i=0;i<10;i++)

printf("parent%d\n",i);

lockf(1,0,0);/*解锁*/

}

}

}

 

4、守护神程序:

#include

#include

#include

#include

#include

intmain(){

time_tt;

FILE*fp;

fp=fopen("/var/log/Mydaemon.log","a");

pid_tpid;

pid=fork();

if(pid>0){

printf("Daemononduty!

\n");

exit(0);

}

elseif(pid<0){

printf("Can'tfork!

\n");

exit(-1);

}

while

(1){

if(fp>=0){

sleep(60);

printf("Daemononduty!

\n");

t=time(0);

fprintf(fp,"Thecurrenttimeis%s\n",asctime(localtime(&t)));

}

}

fclose(fp);

}

 

5、通信中断关代码

#include

#include

#include

#include

#include

voidwaiting(),stop();

intwait_mark;

intmain()

{

intp1,p2,stdout;

while((p1=fork())==-1);/*创建子进程p1*/

if(p1>0)

{

while((p2=fork())==-1);/*创建子进程p2*/

if(p2>0)

{

wait_mark=1;

signal(SIGINT,stop);/*接收到^c信号,转stop*/

waiting();

kill(p1,16);/*向p1发软中断信号16*/

kill(p2,17);/*向p2发软中断信号17*/

wait(0);/*同步*/

wait(0);

printf("Parentprocessiskilled!

\n");

exit(0);

}

else

{

wait_mark=1;

signal(17,stop);/*接收到软中断信号17,转stop*/

waiting();

lockf(stdout,1,0);

printf("Childprocess2iskilledbyparent!

\n");

lockf(stdout,0,0);

exit(0);

}

}

else

{

wait_mark=1;

signal(16,stop);/*接收到软中断信号16,转stop*/

waiting();

lockf(stdout,1,0);

printf("Childprocess1iskilledbyparent!

\n");

lockf(stdout,0,0);

exit(0);

}

}

voidwaiting()

{

while(wait_mark!

=0);

}

voidstop()

{

wait_mark=0;

}

 

6、管道相关代码:

#include

#include

#include

#include

#include

intpid1,pid2;

intmain()

{

intfd[2];

charoutpipe[100],inpipe[100];

pipe(fd);/*创建一个管道*/

while((pid1=fork())==-1);

if(pid1==0)

{

lockf(fd[1],1,0);

sprintf(outpipe,"child1processissendingmessage!

");

/*把串放入数组outpipe中*/

write(fd[1],outpipe,50);/*向管道写长为50字节的串*/

sleep(5);/*自我阻塞5秒*/

lockf(fd[1],0,0);

exit(0);

}

else

{

while((pid2=fork())==-1);

if(pid2==0)

{lockf(fd[1],1,0);/*互斥*/

sprintf(outpipe,"child2processissendingmessage!

");

write(fd[1],outpipe,50);

sleep(5);

lockf(fd[1],0,0);

exit(0);

}

else

{

wait(0);/*同步*/

read(fd[0],inpipe,50);/*从管道中读长为50字节的串*/

printf("%s\n",inpipe);

wait(0);

read(fd[0],inpipe,50);

printf("%s\n",inpipe);

exit(0);

}

}

}

7、消息的相关代码:

lient.c

#include

#include

#include

#include

#include

#defineMSGKEY75

structmsgform

{

longmtype;

charmtext[1000];

}msg;

intmsgqid;

voidclient()

{

inti;

msgqid=msgget(MSGKEY,0777);

for(i=10;i>=1;i--)

{

msg.mtype=i;

printf("(client)sent\n");

msgsnd(msgqid,&msg,1024,0);

}

exit(0);

}

intmain()

{

client();

}

server.c

#include

#include

#include

#include

#include

#defineMSGKEY75

structmsgform

{

longmtype;

charmtext[1000];

}msg;

intmsgqid;

voidserver()

{

msgqid=msgget(MSGKEY,0777|IPC_CREAT);/*创建75#消息队列*/

do

{

msgrcv(msgqid,&msg,1030,0,0);/*接收消息*/

printf("(server)received\n");

}while(msg.mtype!

=1);

msgctl(msgqid,IPC_RMID,0);/*删除消息队列,归还资源*/

exit(0);

}

intmain()

{

server();

}

8、共享存储机制的相关代码

#include

#include

#include

#include

#include

#include

#include

#include

#defineSHMKEY75

#defineSHMSZ128

intshmid,i;

int*addr;

voidclient()

{

inti;

shmid=shmget(SHMKEY,1024,0777);/*打开共享存储区*/

addr=shmat(shmid,0,0);/*获得共享存储区首地址*/

for(i=9;i>=0;i--)

{

while(*addr!

=-1);

printf("(client)sent\n");

*addr=i;

}

exit(0);

}

voidserver()

{

shmid=shmget(SHMKEY,1024,0777|IPC_CREAT);/*创建共享存储区*/

addr=shmat(shmid,0,0);/*获取首地址*/

do

{

*addr=-1;

while(*addr==-1);

printf("(server)received\n");

}while(*addr);

shmctl(shmid,IPC_RMID,0);/*撤消共享存储区,归还资源*/

exit(0);

}

intmain()

{

while((i=fork())==-1);

if(!

i)

server();

system("ipcs-m");

while((i=fork())==-1);

if(!

i)

client();

wait(0);

wait(0);

}

六、实验结果

实验1.1

本来从进程并发执行来看,各种情况都有可能。

上面的三个进程没有同步措施,父进程与子进程的输出内容会叠加在一起。

输出次序带有随机性。

实验1.2

由实验1.1可知各种情况都有可能由于函数,而且printf()在输出字符串时不会被中断,因此,字符串内部字符顺序输出不变。

但由于进程并发执行的调度顺序和父子进程抢占处理机问题,输出字符串的顺序和先后随着执行的不同而发生变化。

这与打印单字符的结果相同。

实验2.1~2.2

程序在调用fork()建立一个子进程后,马上调用wait(),使父进程在子进程结束之前,一直处于睡眠状态。

子进程用exec()装入命令ls,exec()后,子进程的代码被ls的代码取代,这时子进程的PC指向ls的第1条语句,开始执行ls的命令代码。

wait()给我们提供了一种实现进程同步的简单方法。

实验3.1~3.2

加锁和没加锁还是有区别的。

没加锁,子进程的for循环会被“打断”,也就是说,10个daughter和10个son会互相穿插。

而加锁以后,尽管10个son和10个daughter会或先或后出现,但是不会互相穿插,以10个10个这样的完整形式输出。

这是因为加锁以后,屏幕输出要等解锁指令发出后,才会让第二个进程执行输出,在第一个进程没有解锁前,其余进程不能输出。

实验4.1

终端:

日志:

fprintf(fp,"Thecurrenttimeis%s\n",asctime(localtime(&t)))只是把当前时间输入到缓冲区,并没有写到Mydaemon.log文件里面,所以并不会输出时间。

唯有在fprintf最后写上fflush(fp)才能强制输出时间戳到Mydaemon.log文件里面。

实验5.1~5.2

屏幕上无反应,按下^C后,显示Parentprocessiskilled!

上述程序中,signal()都放在一段程序的前面部位,而不是在其他接收信号处。

这是因为signal()的执行只是为进程指定信号值16或17的作用,以及分配相应的与stop()过程链接的指针。

因而,signal()函数必须在程序前面部分执行。

实验6.1

延迟5秒后显示:

child1processissendingmessage!

再延迟5秒:

child2processissendingmessage!

实验7.1

应当是每当client发送一个消息后,server接收该消息,client再发送下一条。

也就是说“(client)sent”和“(server)received”的字样应该在屏幕上交替出现。

实际的结果大多是,先由client发送了两条消息,然后server接收一条消息。

此后client、server交替发送和接收消息。

最后server一次接收两条消息。

client和server分别发送和接收了10条消息,与预期设想一致。

实验8.1

在运行过程中,发现每当client发送一次数据后,server要等待大约0.1秒才有响应。

同样,之后client又需要等待大约0.1秒才发送下一个数据。

七、问题回答分析

(1.1)系统是怎样创建进程的?

答:

系统通过fork()系统调用来创建父进程对应的子进程,创建成功将返回一个值“0”给子进程,父进程会被返回子进程的id值(大于0),若创建失败返回“-1”。

(1.2)当首次调用新创建进程时,其入口在哪里?

答:

入口就是fork()返回给创建的子进程的pid值。

(2.1)可执行文件加载时进行了哪些处理?

答:

获得目录下所有文件的信息,然后把目录倒序,再加载到子进程中。

(2.2)什么是进程同步?

wait()是如何实现进程同步的?

答:

进程同步就是不同进程按照一定的顺序先后执行,而不是没有顺序的或先或后执行。

Wait()是要等待子进程中exit()返回正常软中断的信号时才结束等待,执行父进程wait()后面的代码。

Wait和exit是配合使用的,在进程的同步上面,一般wait放在父进程中用来等待子进程的exit返回值。

(3)无习题。

答:

略。

(4)无习题。

答:

略。

(5.1)该程序段前面部分用了两个wait(0),它们起什么作用?

答:

等待两个子进程exit()的返回值再继续执行下面代码。

(5.2)该程序段中每个进程退出时都用了语句exit(0),为什么?

答:

子进程正常退出时,返回值给wait(),再执行父进程。

(5.3)为何预期的结果并未显示出?

答:

由于父子进程都同时接收到^c中断信号,所以两个子进程在创

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

当前位置:首页 > 自然科学 > 物理

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

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