操作系统实验报告_Linux进程创建与通信

更新时间:2023-05-14 14:37:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

2011-2012学年第一学期

计算机操作系统实验报告

专 业:

班 级:

学 号:

姓 名:提交日期:2011年11月

1

实验二 Linux进程创建与进程通信

【实验目的】

1. 熟悉有关Linux系统调用;

2. 学习有关Linux的进程创建,理解进程创建后两个并发进程的执行; 3. 通过系统调用wait()和exit(),实现父子进程同步;

4. 掌握管道、消息缓冲等进程通信方法并了解其特点和使用限制。

【实验内容】

1. 父进程创建子进程

实现父进程创建一个子进程,返回后父子进程分别循环输出字符串“The parent process.”及“The child process.”5次,每次输出后使用sleep(1)延时一秒,然后再进入下一次循环。给出源程序代码和运行结果。

程序代码: main() {

int p1,i;

while ((p1=fork())==-1); if (p1>0) for (i=0;i<5;i++) { printf("I am parent.\n"); sleep(1); }

else for (i=0;i<5;i++) { printf("I am child.\n"); sleep(1); } }

运行结果:

The parent process. The child process. The parent process. The child process. The parent process. The child process.

The parent process. The child process. The parent process. The child process. 2. 父子进程同步

修改上题程序,使用exit()和wait()实现父子进程同步,其同步方式为父进程等待子进程的同步,即:子进程循环输出5次,然后父进程再循环输出5次。给出源程序代码和运行结果。

程序代码: main() {

int p1,i;

while ((p1=fork())==-1); if (p1>0) { wait(0); for (i=0;i<5;i++) { printf("I am parent.\n"); sleep(1); } } else { for (i=0;i<5;i++) { printf("I am child.\n"); sleep(1); } exit(0); } }

运行结果: I am parent. I am parent. I am parent. I am parent. I am parent. I am child. I am child. I am child. I am child. I am child.

3. Linux管道通信

编写一个程序,实现以下功能。给出源程序代码和运行结果。 (1)父进程使用系统调用pipe()创建一个无名管道; (2)创建两个子进程,分别向管道发送一条信息后结束;

子进程1发送:Child 1 is sending a message to parent! 子进程2发送:Child 2 is sending a message to parent!

(3)父进程从管道中分别接收两个子进程发来的消息并显示在屏幕上,父进程结束。两个子进程发送信息没有先后顺序要求。

源程序代码: #include<stdio.h> #include<unistd.h> #include<signal.h>main() {

int p1,p2,fd[2]; char outpipe[50];

char inpipe1[50]="Child1 is sending a message to parent!"; char inpipe2[50]="Child2 is sending a messege to parent!"; pipe(fd);

while((p1=fork())==-1); if (p1==0) { lockf(fd[1],1,0); write(fd[1],inpipe1,50); exit(0); } else {

while((p2=fork())==-1); if (p2==0) {

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

write(fd[1],inpipe2,50); exit(0); } else {

wait(0);

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

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

printf("Parent has received first message:\n"); printf("%s\n",outpipe); wait(0);

read(fd[0],outpipe,50); lockf(fd[1],0,0);

printf("Parent has received second message:\n"); printf("%s\n",outpipe); exit(0); }

} }

运行结果:

Child 1 is sending a message to parent! Child 2 is sending a message to parent! 4. Linux消息缓冲通信

编写一个程序,实现以下功能。给出源程序代码和运行结果。

(1)父进程创建一个消息队列和一个子进程,由子进程发送消息,父进程等待子进程结束后接收子进程发来的消息,并显示消息内容。以“end”作为结束消息。

父进程:

#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h>

#include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #define MAX_TEXT 512 struct my_msg { long int my_msg_type; char some_text[MAX_TEXT]; }msg;

main() { int p1,msgid,running=1; char buffer[BUFSIZ]; long int msg_to_receive=0; char *path="./msgchld.exe"; char *argv[3]={"msgchld.exe",NULL};

}

while((p1=fork())==-1); if(p1==0) { execv(path,argv); exit(0); } else { wait(0); while(running) { msgrcv(msgid,&msg,BUFSIZ,msg_to_receive,0); printf("You wrote:%s",msg.some_text); if(strncmp(msg.some_text,"end",3)==0) running=0; } msgctl(msgid,IPC_RMID,0); exit(0); }

子进程:

#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h>

#include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #define MAX_TEXT 512

struct my_msg { long int my_msg_type; char some_text[MAX_TEXT]; }msg;

main() { int msgid,running=1; char buffer[BUFSIZ]; msgid=msgget(1234,0666|IPC_CREAT);

}

{ puts("Enter some text:"); fgets(buffer,BUFSIZ,stdin); msg.my_msg_type=1; strcpy(msg.some_text,buffer); msgsnd(msgid,&msg,MAX_TEXT,0); if(strncmp(msg.some_text,"end",3)==0) running=0; }

exit(0);

运行结果:

子进程运行,显示:“Enter some text:”;输入内容后,运行父进程,显示“You wrote:”和刚输入的内容。

(2)分别编写发送进程和接收进程,由发送进程发送消息,接收进程接收消息。采用先执行发送进程后执行接收进程的方式同步。以“end”作为结束消息。 发送方:

#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #define MAX_TEXT 512 struct my_msg { long int my_msg_type; char some_text[MAX_TEXT]; }msg;

main() { int msgid,running=1; char buffer[BUFSIZ]; msgid=msgget(1234,0666|IPC_CREAT); while(running) {

}

fgets(buffer,BUFSIZ,stdin); msg.my_msg_type=1; strcpy(msg.some_text,buffer); msgsnd(msgid,&msg,MAX_TEXT,0); if(strncmp(msg.some_text,"end",3)==0) running=0; }

exit(0);

接收方:

#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #define MAX_TEXT 512

struct my_msg { long int my_msg_type; char some_text[MAX_TEXT]; }msg;

main() { int msgid,running=1; long int msg_to_receive=0; msgid=msgget(1234,0666|IPC_CREAT); while(running) { msgrcv(msgid,&msg,BUFSIZ,msg_to_receive,0); printf("You wrote:%s",msg.some_text); if(strncmp(msg.some_text,"end",3)==0) running=0; } msgctl(msgid,IPC_RMID,0); exit(0); }

运行结果:与上个实验类似。

(3)模拟C/S通信,要求如下。(选做)

① 模拟客户端(client端)程序client,其功能如下。 a) 显示下列服务功能菜单: Enter your choice: 1. Save money 2. Take money

b)接收用户键入的功能号进行选择;

c) 将用户键入的功能号作为一条信息发送至消息队列,然后结束。 ②模拟服务器端(Server端)程序server,其功能如下。 a) 从消息队列接收Client端发来的一条消息; b)根据消息作如下处理。

若消息为“1”,则创建子进程1,由子进程1加载服务模块save,该模块的作用为显示以下信息:

Your money was saved!

若消息为“2”,则创建子进程2,由子进程2加载服务模块take,该模块的作用为显示以下信息:

Please take your money!

c) 等待子进程终止后,Server进程删除消息队列,然后结束。

【实验提示】

1. C编译器gcc的使用

GCC(GNU Compiler Collection)是目前Linux下最常用的C语言编译器,能够编译用C\C++等语言编写的程序。通过GCC源码文件生成可执行文件的过程要经过4个阶段,分别是预处理、编译、汇编和链接。

格式:gcc 【选项】 【目标文件】源文件

如不使用任何参数将生成一个名为a.out的文件,执行时输入./a.out(./表示在当前路径下)。

2. 进程控制的系统调用 fork系统调用创建新的子进程 格式: pid=int fork()

fork调用返回时,系统中已有两个用户级环境完全相同的进程存在,这两个进程从fork调用中得到的返回值不同,其中子进程得到的返回值为零,父进程得到的返回值是新创建子进程的进程标识号。

3.进程通信的系统调用

pipe系统调用

格式: int pipe (filedes) int filedes [2]; 消息机制

Msgid=msgget(key,msgflg) key_t key; int msgflg;

Msgctl(msgid, cmd, buf) int msgid, cmd; msgqid_ds #buf;

msgsnd(msgid, msgp, msgsz, msgflg) int msgid;

struct msgbuf *msgp; int msgsz, msgflg;

msgrcv (msgid, msgp, msgsz, msgtyp, msgflg ); int msgid;

struct msgbuf *msgp; int msgsz; long msgtyp; int msgflg;

注:系统调用详细说明和使用示例可参考ppt资料。

【实验要求】

1. 观察实验结果,分析结果产生原因。

2. 实验结束一周后需提交实验报告和源程序文件。

【实验思考】

1. 实验中如何实现的父子进程的同步?

答:通过系统调用wait()和exit(),实现父子进程同步。 2. 管道通信的读写进程之间必须满足什么关系?

答:写进程负责将数据从管道写入端写入管道,读进程负责将数据从读出端读出管道。 3. 在使用消息缓冲通信进行通信时,发送和接收者之间的同步机制由谁提供?

答:操作系统根据用户进程提供的key值,在msgque中查找该key值的ipc_perm结构以及与该key值对应的消息队列头q_messages,该队列上挂的是一系列msg_msg数据结构,每个msg_msg数据结构指向一个消息缓冲区msgbuf,而该缓冲区msgbuf中存放的就是需要传递的消息正文。操作系统通过对消息队列链表结构msgque中三个队列的管理来实现消息的发送、接收以及发送与

接收进程之间的同步和互斥。

4. 消息缓冲通信与管道通信有何不同?当两个独立进程之间需传递大量信息时,应该使用哪一种通信方式?

答:无名管道简单方便,但工作在单向通信方式,且只能在创建它的进程及其子孙进程之间共享;消息缓冲可以在任意进程之间通信,而且自带同步工具,使用方便,但是信息复制消耗CPU的时间,不适宜于信息量大或操作频繁的场合。当两个独立进程之间需传递大量信息时,应该使用管道通信。

【参考资料】

【1】《操作系统教程与实验》,胡明庆等编著,清华大学出版社。

【2】《计算机操作系统教程--核心与设计原理》,范策等编著,清华大学出版社。 学习网站:

红帽中国门户网站:; Fedora门户网站中国Linux公社:/cms/index.php; Linux伊甸园:

Linux联盟: Linux红联:

实验三 银行家算法模拟

【开发语言及实现平台或实验环境】

C++/C#

Microsoft Visual Studio 6.0/ Microsoft Visual Studio .NET 2003

【实验目的】

(1)进一步理解利用银行家算法避免死锁的问题;

(2)在了解和掌握银行家算法的基础上,编制银行家算法通用程序,将调试结果显示在计算机屏幕上,再检测和笔算的一致性。 (3)理解和掌握安全序列、安全性算法

【实验要求】

(1)了解和理解死锁;

(2)理解利用银行家算法避免死锁的原理; (3)会使用某种编程语言。

【实验原理】 一、安全状态

指系统能按照某种顺序如<P1,P2,…,Pn>(称为<P1,P2,…,Pn>序列为安全序列),为每个进程分配所需的资源,直至最大需求,使得每个进程都能顺利完成。

二、银行家算法

假设在进程并发执行时进程i提出请求j类资源k个后,表示为Requesti[j]=k。系统按下述步骤进行安全检查:

(1)如果Requesti≤Needi则继续以下检查,否则显示需求申请超出最大需求值的错误。 (2)如果Requesti≤Available则继续以下检查,否则显示系统无足够资源,Pi阻塞等待。 (3)系统试探着把资源分配给进程Pi,并修改下面数据结构中的数值:

Available[j]∶=Available[j]-Requesti[j]; Allocation[i,j]∶=Allocation[i,j]+Requesti[j]; Need[i,j]∶=Need[i,j]-Requesti[j];

(4)系统执行安全性算法,检查此次资源分配后,系统是否处于安全状态。若安全,才正式将资源分配给进程Pi,以完成本次分配;否则, 将本次的试探分配作废,恢复原来的资源分配状态,让进程Pi等待。

三、安全性算法

(1)设置两个向量:

① 工作向量Work: 它表示系统可提供给进程继续运行所需的各类资源数目,它含有m个元素,在执行安全算法开始时,Work∶=Available;

② Finish: 它表示系统是否有足够的资源分配给进程,使之运行完成。开始时先做Finish[i]∶=false; 当有足够资源分配给进程时, 再令Finish[i]∶=true。 (2)从进程集合中找到一个能满足下述条件的进程: ① Finish[i]=false;

② Need[i,j]≤Work[j]; 若找到, 执行步骤(3), 否则,执行步骤(4)。

(3)当进程Pi获得资源后,可顺利执行,直至完成,并释放出分配给它的资源,故应执行:

Work[j]∶=Work[i]+Allocation[i,j]; Finish[i]∶=true; go to step 2;

(4)如果所有进程的Finish[i]=true都满足, 则表示系统处于安全状态;否则,系统处于不安全状态。

【实验步骤】

参考实验步骤如下:

(1) 参考图1-1所示流程图编写安全性算法。

(2)编写统一的输出格式。

每次提出申请之后输出申请成功与否的结果。如果成功还需要输出变化前后的各种数据,

并且输出安全序列。

(3)参考图1-2所示流程图编写银行家算法。

(4)编写主函数来循环调用银行家算法。

【思考题】

(1) 在编程中遇到了哪些问题?你是如何解决的?

答:安全序列不唯一的问题

(2)在安全性算法中,为什么不用变量Available,而又定义一个临时变量work? 答:在安全性算法中,用一个临时变量是为了防止在该时刻不安全的情况下破坏数据的原值,如果不用临时变量work,而是直接用Available变量,那么如果该状态是不安全的,那么进行安全性算法的时候,会对Available进行修改的,破坏了原来的数值。如果该状态是安全的话,直接用Available也没有问题,用work临时变量让程序更全面,更符合算法的要求。

【参考代码】

部分参考代码如下: #include <iostream.h> #include <string.h>

#define M 3 //资源的种类数 #define N 5 //进程的个数

void output(int iMax[N][M],int iAllocation[N][M],int iNeed[N][M],int iAvailable[M],char cName[N]); 输出格式

bool safety(int iAllocation[N][M],int iNeed[N][M],int iAvailable[M],char cName[N]); bool banker(int iAllocation[N][M],int iNeed[N][M],int iAvailable[M],char cName[N]);

void main() { int i,j; //当前可用每类资源的资源数 int iAvailable[M]={3,3,2}; //系统中N个进程中的每一个进程对M类资源的最大需求 int iMax[N][M]={{7,5,3},{3,2,2},{9,0,2},{2,2,2},{4,3,3}}; //iNeed[N][M]每一个进程尚需的各类资源数 //iAllocation[N][M]为系统中每一类资源当前已分配给每一进程的资源数 int iNeed[N][M],iAllocation[N][M]={{0,1,1},{2,0,0},{3,0,2},{2,1,1},{0,0,2}}; //进程名 char cName[N]={'a','b','c','d','e'}; bool bExitFlag=true; //退出标记 char ch; //接收选择是否继续提出申请时传进来的值 bool bSafe; //存放安全与否的标志 //计算iNeed[N][M]的值 for(i=0;i<N;i++) for(j=0;j<M;j++) iNeed[i][j]=iMax[i][j]-iAllocation[i][j]; //输出初始值

//统一的

output(iMax,iAllocation,iNeed,iAvailable,cName); //判断当前状态是否安全 bSafe=safety(iAllocation,iNeed,iAvailable,cName); //是否继续提出申请 while(bExitFlag) { cout<<"\n"<<"继续提出申请?\ny为是;n为否。\n"; cin>>ch; switch(ch) { case 'y':

//cout<<"调用银行家算法"; bSafe=banker(iAllocation,iNeed,iAvailable,cName); if (bSafe) //安全,则输出变化后的数据 output(iMax,iAllocation,iNeed,iAvailable,cName); break; case 'n': cout<<"退出。\n"; bExitFlag=false; break; default: cout<<"输入有误,请重新输入:\n"; } } }

//输出

void output(int iMax[N][M],int iAllocation[N][M],int iNeed[N][M],int iAvailable[M],char cName[N]) { int i,j; cout<<"\n\t Max \tAllocation\t Need \t Available"<<endl; cout<<"\tA B C\tA B C\tA B C\t A B C"<<endl; for(i=0;i<N;i++) { cout<<cName[i]<<"\t"; for(j=0;j<M;j++) cout<<iMax[i][j]<<" "; cout<<"\t"; for(j=0;j<M;j++)

cout<<iAllocation[i][j]<<" "; cout<<"\t"; for(j=0;j<M;j++) cout<<iNeed[i][j]<<" "; cout<<"\t"; cout<<" "; //Available只需要输出一次 if (i==0) for(j=0;j<M;j++) cout<<iAvailable[j]<<" "; cout<<endl; } }

//安全性算法,进行安全性检查;安全返回true,并且输出安全序列,不安全返回false,并输出不安全的提示;

bool safety(int iAllocation[N][M],int iNeed[N][M],int iAvailable[M],char cName[N]) { }

//定位ch对应的进程名在数组中的位置 //没找见返回-1,否则返回数组下标 int locate(char cName[N],char ch) {

int i;

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

if (cName[i]==ch) //找到 return i; //未找到 return -1; }

//提出申请,返回提出申请的进程名对应的下标 int request(char cName[N],int iRequest[M]) {

int i,loc; char ch;

bool bFlag=true; //判断输入的进程名是否有误 while(bFlag) {

//输出进程名 for(i=0;i<N;i++)

cout<<cName[i]<<"\t"; //输入提出申请的进程名

cout<<"\n输入提出资源申请的进程名:\n"; cin>>ch;

//定位ch对应的进程名在进程名数组中的位置 loc=locate(cName,ch); //没找到,重新输入 if (loc==-1)

cout<<"\n您输入的进程名有误!请重新输入"; //找到,退出循环 else bFlag=false; }

//输入提出申请的资源数

cout<<"输入申请各类资源的数量:\n"; for(i=0;i<M;i++) cin>>iRequest[i];

//返回提出申请的进程名对应的下标 return loc; }

bool banker(int iAllocation[N][M],int iNeed[N][M],int iAvailable[M],char cName[N]) { }

【参考界面】

【实验总结】

本次操作系统的三次实验课主要围绕“Linux进程创建与进程通信”和“银行家算法”进行。第一、二节课,根据题目要求和自己的理解编代码,并结合老师给的参考代码在Linux系统虚拟机上实验,大体上顺利地做出正确的实验结果。第三节课,参照实验指导书开始银行家算法的实验,所编写的程序实现了银行家算法的功能,并在其基础上考虑了输出显示的格式等问题。通过查询资料、与同学讨论、多次调试解决了大部分问题,收获非常大。掌握了进程产生死锁原因,为什么要避免死锁以及银行家算法的数据结构,算法的执行过程,加深了对银行家算法的理解。

本文来源:https://www.bwwdw.com/article/h56e.html

Top