操作系统实验实验1分析解析.docx

上传人:b****3 文档编号:5428142 上传时间:2022-12-16 格式:DOCX 页数:29 大小:1.06MB
下载 相关 举报
操作系统实验实验1分析解析.docx_第1页
第1页 / 共29页
操作系统实验实验1分析解析.docx_第2页
第2页 / 共29页
操作系统实验实验1分析解析.docx_第3页
第3页 / 共29页
操作系统实验实验1分析解析.docx_第4页
第4页 / 共29页
操作系统实验实验1分析解析.docx_第5页
第5页 / 共29页
点击查看更多>>
下载资源
资源描述

操作系统实验实验1分析解析.docx

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

操作系统实验实验1分析解析.docx

操作系统实验实验1分析解析

广州大学学生实验报告

开课学院及实验室:

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

实验课程名称

操作系统

成绩

实验项目名称

进程管理与进程通信

指导老师

陈康民

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

进程管理

(一)进程的创建实验

一、实验目的

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

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

二、实验内容

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

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

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

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

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

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

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

三、实验步骤

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

代码:

#include

main()

{

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');

}

}

运行结果:

bca,bac,abc,……都有可能。

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

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

代码:

#include

main()

{

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);

}

}

结果:

parent…

son…

daughter..

daughter..

或parent…

son…

parent…

daughter…等

四、分析原因

除strace外,也可用ltrace-f-i-S./executable-file-name查看以上程序执行过程。

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

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

输出次序带有随机性。

2、由于函数printf()在输出字符串时不会被中断,因此,字符串内部字符顺序输出不变。

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

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

五、思考题

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

linux系统创建进程都是用fork()系统调用创建子进程。

Init程序以/etc/inittab为脚本文件来创建系统的新进程的。

新进程还克以创建新进程。

创建新进程是通过克隆老进程或当前进程来创建的。

新进程的创建实用系统调用sys_fork()或sys_clone(),并且在内核模式下完成。

在系统调用结束时,系统从物理内存中分配一个心的task_struct()数据结构和进程堆栈,同时获得一个唯一的标示此进程的标识符。

 通过FORK函数调用促使内核创建一个新的进程,该进程是调用进程的一个精确的副本。

新创建的进程为子进程,调用的FORK进程为父进程,而且创建出来的是两个副本,即,两个进程,但他们互不干扰。

 

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

首次创建进程时,入口在进程1那里。

(二)进程的控制实验

一、实验目的

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

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

二、实验内容

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

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

 

三、参考程序

#include

#include

main()

{

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);

}

}

4、运行结果

 

5、思考

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

解:

可执行文件加载时首先是创建一个新进程的fork系统调用,然后用于实现进程自我终止的exit系统调用;改变进程原有代码的exec系统调用;用于将调用进程挂起并等待子进程终止的wait系统调用;获得进程标识符的getpid系统调用等处理过程。

(2)什么是进程同步?

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

解:

进程同步是指对多个相关进程在执行次序上进行协调,以使并发执行的主进程之间有效地共享资源和相互合作,从而使程序的执行具有可在现行。

 

首先程序在调用fork()机那里了一个子进程后,马上调用wait(),使父进程在子进程调用之前一直处于睡眠状态,这样就使子进程先运行,子进程运行exec()装入命令后,然后调用wait(0),使子进程和父进程并发执行,实现进程同步。

 

(三)进程互斥实验

实验目的

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

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

实验内容

1、修改实验

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

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

#include

#include

main( )

{

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);/*解锁*/

}

}

}

 

运行结果

parent…

son…

daughter..

daughter..

或parent…

son…

parent…

daughter…

大致与未上锁的输出结果相同,也是随着执行时间不同,输出结果的顺序有所不同。

四、分析原因

上述程序执行时,不同进程之间不存在共享临界资源(其中打印机的互斥性已由操作系统保证)问题,所以加锁与不加锁效果相同。

 

(四)守护进程实验

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

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

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

注意:

要root权限才能在/var/log创建文件。

守护神程序:

#include

#include

#include

main(){

time_tt;//建立time_t格式变量

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);//关闭文件

}

 

运行结果

 

 

进程通信

(一)信号机制实验

实验目的

1、了解什么是信号

2、熟悉LINUX系统中进程之间软中断通信的基本原理

实验内容

1、编写程序:

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

Childprocess1iskilledbyparent!

Childprocess2iskilledbyparent!

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

Parentprocessiskilled!

2、分析利用软中断通信实现进程同步的机理

参考程序

#include

#include

#include

voidwaiting(),stop();

intwait_mark;

main()

{

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;

}

运行结果

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

分析原因

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

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

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

本方法通信效率低,当通信数据量较大时一般不用此法。

思考

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

答:

父进程等待两个子进程终止。

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

答:

该程序中每个进程退出时都用了语句exit(0),这是进程的正常终止。

3、为何预期的结果并未显示出?

答:

因为只执行成功两个子进程,但是并没有调用两个子进程P1,P2。

当signal()让父进程捕捉从键盘上来的信号(按下^C或者break键时),只有捕捉到信号后,父进程用系统调用kill()向两个子进程发出信号。

当子进程捕捉到信号后才能输出信息,之后父进程输出信息。

4、程序该如何修改才能得到正确结果?

答:

#include

 #include

#include 

Void waiting(),stop(),alarming(); 

Intwait_mark; Main() 

Int P1,P2,stdout; 

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

If (p2=fork()); /*创建子进程P2*/

 { 

Wait_mark=1; 

Signal(SIGINT,stop); /*接收到^C信号,转stop*/ 

Signal(SIGALRM,alarming);/*接收SIGALRM*/ Waiting(); 

Kill(P1,16); /*向P1发软中断信号16*/ Kill(P2,17); /*向P2发软中断信号17*/ 

Wait(0);  /*同步*/ 

Wait(0); 

Printf(“Parent process is killed!

\n”); Exit(0);

 } 

Else { 

Wait_mark=1; 

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

Signal(17,stop); 

Signal(SIGINT,SIG_IGN);/*忽略^C信号*/ 

While(wait_mark!

=0); 

Lockf(stdout,1,0); 

Printf(“Child process 2 is killed by parent!

\n); 

Lockf(stdout,0,0); 

Exit(0); 

}

 Else { 

Wait_mark=1; 

Signal(16,stop);

Signal(SIGINT,SIG_IGN);

 While(wait_mark!

=0) Lockf(stdout,1,0); 

Printf(“Child process 1 is killed by parent!

\n”); Lockf(stdout,0,0); Exit(0);   }

 } 

Void waiting() 

Sleep(5); 

if(wait_mark!

=0); 

kill(getpid(),SIGALRM); 

Void alarming()

 { 

Wait_mark=0;

 } 

Void stop();

 { 

Wait_mark=0 

}

 

(二)进程的管道通信实验

实验目的

1、了解什么是管道

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

实验内容

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

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

Child1issendingamessage!

Child2issendingamessage!

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

参考程序

#include

#include

#include

intpid1,pid2;

main()

{

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);

}

}

}

运行结果

延迟5秒后显示

child1processissendingmessage!

再延迟5秒

child2processissendingmessage!

 

思考题

1、程序中的sleep(5)起什么作用?

答:

自我阻塞5秒。

这样做的目的是令读进程把管道中的已有数据读完后,暂时进入睡眠状态等待,直至写进程又将数据写入管道后,再将读进程唤醒。

2、子进程1和2为什么也能对管道进行操作?

答:

因为他们的读指针和写指针都指向了管道的索引节点。

(三)消息的发送与接收实验

实验目的

1、了解什么是消息

2、熟悉消息传送的机理

实验内容

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

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

参考程序

1、client.c

#include

#include

#include

#defineMSGKEY75

structmsgform

{longmtype;

charmtext[1000];

}msg;

intmsgqid;

voidclient()

{

inti;

msgqid=msgget(MSGKEY,0777);/*打开75#消息队列*/

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

{

msg.mtype=i;

printf(“(client)sent\n”);

msgsnd(msgqid,&msg,1024,0);/*发送消息*/

}

exit(0);

}

main()

{

client();

}

2、server.c

#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);

}

main()

{

server();

}

程序说明

1、为了便于操作和观察结果,编制二个程序client.c和server.c,分别用于消息的发送与接收。

2、server建立一个Key为75的消息队列,等待其它进程发来的消息。

当遇到类型为1的消息,则作为结束信号,取消该队列,并退出server。

server每接收到一个消息后显示一句“(server)received。

3、client使用key为75的消息队列,先后发送类型从10到1的消息,然后退出。

最后一个消息,即是server端需要的结束信号。

client每发送一条消息后显示一句“(client)sent”。

4、注意:

二个程序分别编辑、编译为client与server。

执行:

./server&

ipcs-q

./client。

运行结果

从理想的结果来说,应当是每当client发送一个消息后,server接收该消息,client再发送下一条。

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

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

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

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

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

开启server

开启client,发送信息

Server收到信息

思考

message的传送和控制并不保证完全同步,当一个程序不在激活状态的时候,它完全可能继续睡眠,造成了上面的现象,在多次sendmessage后才recievemes

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

当前位置:首页 > 医药卫生 > 基础医学

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

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