收藏 分销(赏)

操作系统实验_实验1.doc

上传人:s4****5z 文档编号:8795203 上传时间:2025-03-02 格式:DOC 页数:16 大小:329.50KB 下载积分:10 金币
下载 相关 举报
操作系统实验_实验1.doc_第1页
第1页 / 共16页
操作系统实验_实验1.doc_第2页
第2页 / 共16页


点击查看更多>>
资源描述
广州大学学生实验报告 开课学院及实验室:计算机科学与工程实验室 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( )向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止: Child process1 is killed by parent! Child process2 is killed by parent! 父进程等待两个子进程终止后,输出如下的信息后终止: Parent process is killed! 5.2、用软中断通信实现进程同步的机理 6.1、编写程序实现进程的管道通信。用系统调用pipe( )建立一管道,二个子进程P1和P2分别向管道各写一句话: Child 1 is sending a message! Child 2 is sending a message! 父进程从管道中读出二个来自子进程的信息并显示(要求先接收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、每个信号都对应一个正整数常量(称为signal number,即信号编号。定义在系统头文件<signal.h>中),代表同一用户的诸进程之间传送事先约定的信息的类型,用于通知某进程发生了某异常事件。每个进程在运行时,都要通过信号机制来检查是否有信号到达。若有,便中断正在执行的程序,转向与该信号相对应的处理程序,以完成对该事件的处理;处理结束后再返回到原来的断点继续执行。实质上,信号机制是对中断机制的一种模拟,故在早期的UNIX版本中又把它称为软中断。 7、信号的发送,是指由发送进程把信号送到指定进程的信号域的某一位上。如果目标进程正在一个可被中断的优先级上睡眠,核心便将它唤醒,发送进程就此结束。一个进程可能在其信号域中有多个位被置位,代表有多种类型的信号到达,但对于一类信号,进程却只能记住其中的某一个。 8、当一个进程要进入或退出一个低优先级睡眠状态时,或一个进程即将从核心态返回用户态时,核心都要检查该进程是否已收到软中断。当进程处于核心态时,即使收到软中断也不予理睬;只有当它返回到用户态后,才处理软中断信号。 9、管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信的一个共享文件,又称为pipe文件。 10、消息机制允许由一个进程给其它任意的进程发送一个消息。 11、该机制可使若干进程共享主存中的某一个区域,且使该区域出现(映射)在多个 进程的虚地址空间中。 四、实验设备 Linux系统 五、实验程序 1、创建子进程相关代码 #include <stdio.h> #include<unistd.h> int main( ) { int p1,p2; while((p1=fork( ))==-1); /*创建子进程p1*/ if (p1==0) putchar('b'); else { while((p2=fork( ))==-1); /*创建子进程p2*/ if(p2==0) putchar('c'); else putchar('a'); } } 修改后: #include<stdio.h> #include<unistd.h> int main( ) { int p1,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<stdio.h> #include<stdlib.h> #include<unistd.h> #include <sys/wait.h> void main( ) { int pid; pid=fork( ); /*创建子进程*/ switch(pid) { case -1: /*创建失败*/ printf("fork fail!\n"); exit(1); case 0: /*子进程*/ execl("/bin/ls","ls","-1","-color",NULL); printf("exec fail!\n"); exit(1); default: /*父进程*/ wait(NULL); /*同步*/ printf("ls completed !\n"); exit(0); } } 3、lockf相关代码 #include <stdio.h> #include <unistd.h> #include <sys/wait.h> int main( ) { int p1,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 <stdio.h> #include <time.h> #include <stdlib.h> #include <unistd.h> #include<sys/time.h> int main(){ time_t t; FILE *fp; fp=fopen("/var/log/Mydaemon.log","a"); pid_t pid; pid=fork(); if(pid>0){ printf("Daemon on duty!\n"); exit(0); } else if(pid<0){ printf("Can't fork!\n"); exit(-1); } while(1){ if(fp>=0){ sleep(60); printf("Daemon on duty!\n"); t=time(0); fprintf(fp,"The current time is %s\n",asctime(localtime(&t))); } } fclose(fp); } 5、通信中断关代码 #include <stdio.h> #include <stdlib.h> #include<sys/wait.h> #include <signal.h> #include <unistd.h> void waiting( ),stop( ); int wait_mark; int main( ) { int p1,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("Parent process is killed!\n"); exit(0); } else { wait_mark=1; signal(17,stop); /*接收到软中断信号17,转stop*/ waiting( ); 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); /*接收到软中断信号16,转stop*/ waiting( ); lockf(stdout,1,0); printf("Child process 1 is killed by parent!\n"); lockf(stdout,0,0); exit(0); } } void waiting( ) { while(wait_mark!=0); } void stop( ) { wait_mark=0; } 6、管道相关代码: #include <unistd.h> #include <signal.h> #include <stdio.h> #include<stdlib.h> #include<sys/wait.h> int pid1,pid2; int main( ) { int fd[2]; char outpipe[100],inpipe[100]; pipe(fd); /*创建一个管道*/ while ((pid1=fork( ))==-1); if(pid1==0) { lockf(fd[1],1,0); sprintf(outpipe,"child 1 process is sending message!"); /*把串放入数组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,"child 2 process is sending message!"); 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<sys/types.h> #include<stdio.h> #include<sys/msg.h> #include<sys/ipc.h> #include<stdlib.h> #define MSGKEY 75 struct msgform { long mtype; char mtext[1000]; }msg; int msgqid; void client() { int i; msgqid=msgget(MSGKEY,0777); for(i=10;i>=1;i--) { msg.mtype=i; printf("(client)sent\n"); msgsnd(msgqid,&msg,1024,0); } exit(0); } int main( ) { client( ); } server.c #include<sys/types.h> #include<sys/msg.h> #include<sys/ipc.h> #include<stdio.h> #include<stdlib.h> #define MSGKEY 75 struct msgform { long mtype; char mtext[1000]; }msg; int msgqid; void server( ) { 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); } int main( ) { server( ); } 8、共享存储机制的相关代码 #include<sys/types.h> #include<sys/shm.h> #include<sys/ipc.h> #include<unistd.h> #include<stdlib.h> #include<stdio.h> #include<sys/wait.h> #include<signal.h> #define SHMKEY 75 #define SHMSZ 128 int shmid,i; int *addr; void client( ) { int i; 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); } void server( ) { 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); } int main( ) { 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,"The current time is %s\n",asctime(localtime(&t)))只是把当前时间输入到缓冲区,并没有写到Mydaemon.log文件里面,所以并不会输出时间。唯有在fprintf最后写上fflush(fp)才能强制输出时间戳到Mydaemon.log文件里面。 实验5.1~5.2 屏幕上无反应,按下^C后,显示 Parent process is killed! 上述程序中,signal( )都放在一段程序的前面部位,而不是在其他接收信号处。这是因为signal( )的执行只是为进程指定信号值16或17的作用,以及分配相应的与stop( )过程链接的指针。因而,signal( )函数必须在程序前面部分执行。 实验6.1 延迟5秒后显示:child 1 process is sending message! 再延迟5秒:child 2 process is sending message! 实验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中断信号,所以两个子进程在创建之后,signal就得到中断信号,子进程正常中止,并没有执行signal之后的代码,返回正常中止的信号给wait(0),父进程得到子进程正常中止的信号后就执行后面的代码,输出“Parent process is killed!”。 (5.4) 程序该如何修改才能得到正确结果? 答:在终端发送指令给父进程,或着更换成如下代码: #include<stdio.h> #include<signal.h> #include<unistd.h> #include<stdlib.h> #include<sys/wait.h> void waiting( ),stop( ),nye( ); int wait_mark; int main( ) { int p1,p2,stdout; while((p1=fork( ))==-1); if (p1>0) { while((p2=fork( ))==-1); if(p2>0) { wait_mark=1; signal(SIGINT,stop); waiting( ); kill(p1,16); kill(p2,17); wait(0); /*同步*/ wait(0); printf("Parent process is killed!\n"); exit(0); } else { wait_mark=1; signal(SIGINT,nye); signal(17,stop); waiting( ); lockf(stdout,1,0); printf("Child process 2 is killed by parent!\n"); lockf(stdout,0,0); exit(0); } } else { wait_mark=1; signal(SIGINT,nye); signal(16,stop); waiting( ); lockf(stdout,1,0); printf("Child process 1 is killed by parent!\n"); lockf(stdout,0,0); exit(0); } } void waiting( ) { while(wait_mark!=0); } void stop( ) { wait_mark=0; } void nye( ) { } (6.1)程序中的sleep(5)起什么作用? 答:sleep(5)的作用是延长子进程占用管道的时间,并没有让子进程1先输出子进程2后输出的作用。 (6.2) 子进程1和2为什么也能对管道进行操作? 答:因为该pipe管道属于无名管道,调用pipe( )的父进程及其子孙进程均能识别此文件描述符,能利用该文件(管道)进行通信。 (7.1)无习题。 答:略c (8.1)比较两种消息通信机制中数据传输的时间。 答:由于两种机制实现的机理和用处都不一样,难以直接进行时间上的比较。如果比较其性能,应更加全面的分析。 (1)消息队列的建立比共享区的设立消耗的资源少。前者只是一个软件上设定的问题,后者需要对硬件的操作,实现内存的映像,当然控制起来比前者复杂。如果每次都重新进行队列或共享的建立,共享区的设立没有什么优势。 (2)当消息队列和共享区建立好后,共享区的数据传输,受到了系统硬件的支持,不耗费多余的资源;而消息传递,由软件进行控制和实现,需要消耗一定的cpu的资源。从这个意义上讲,共享区更适合频繁和大量的数据传输。 (3)消息的传递,自身就带有同步的控制。当等到消息的时候,进程进入睡眠状态,不再消耗cpu资源。而共享队列如果不借助其他机制进行同步,接收数据的一方必须进行不断的查询,白白浪费了大量的cpu资源。可见,消息方式的使用更加灵活。
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服