OS实验三【进程通信】

一、实验目的

1、了解和熟悉Linux支持的消息通信机制管道道通信共享存储区机制信息量机制

2、掌握利用Linux系统的进程通信机制(IPC)实现进程间交换数据的方法

二、实验内容

1、进程通信

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

Child 1 is sending a message!

Child 2 is sending a message!

父进程则从管道中读出来自两个了进程的信息,显示在屏幕上。

要求父进程先接收子进程P1发来的消息,然后再接收子进程P2发来的消息。(可以通过sleep()将自身进入睡眠)

2、消息的创建,发送和接收

(1) 使用系统调用msgget (), msgsnd (), msgsrv ()及msgctl () 编制一长度为1K的消息(如个人通信录信息)的发送和接收程序.

(2) 使用共享存储区相关的系统调用  shmget (),shmat (),sgmdt (),shmctl (),编制一个与上述功能相同的程序.

(3) 比较上述两种消息通信机制中数据传输的时间。

三、设计原理(或方案)及相关算法

1. 进程通信:

创建一条管道,子进程写入数据,父进程写出数据。在父进程中使用 wait() 函数,这样在子进程执行完毕之前,父进程一直要等待。

调用pipe()建立一条管道,两个子进程分别向管道写入一句话,在父进程中使用wait()函数,使父进程等待子进程执行结束,依次输出P1、P2发来的消息。

2. 消息的创建,发送和接收

2.1:

(1)用一个程序作为“引子”,先后fork()两个进程,SERVER和CLIENT,进行通信

(2)SERVER端建立一个Key为75的消息队列,等待其他进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出SERVER。SERVER每接受到一个消息后显示一句“(Server)received”,然后发送一个返回消息给CLIENT端,显示一句“(Server)sent”。

(3)CLIENT端使用key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后一个消息,即是SERVER端需要的结束信号。CLIENT每发送一条消息后显示一句“(Client)sent”,等接受SERVER端的返回消息后,显示一句“(Client)received”,再在发送下一条消息。

(4)父进程在SERVER和CLIENT都退出后结束。

2.2:

(1)为了便于操作和观察结果,用一个 程序为“引子”,先后fork( )两个子进程,SERVER 和 CLIENT,进行通信。

(2)SERVER端建立一个KEY为75的共享区,并将第一个字节置为-1.作为数据空的标志.等待其他进程发来的消息.当该字节的值发生变化时,表示收到了该消息,进行处理.然后再次把它 的值设为-1.如果遇到的值为0,则视为结束信号,取消该队列,并退出SERVER.SERVER每接 收到一次数据后显示”(server)received”.

(3)CLIENT端建立一个为75的共享区,当共享取得第一个字节为-1时, Server端空闲,可发送 请求. CLIENT 随即填入9到0.期间等待Server端再次空闲.进行完这些操作后, CLIENT 退出. CLIENT每发送一次数据后显示”(client)sent”.

(4)父进程在SERVER和CLIENT均退出后结束.

四、结果分析

题目一:Pipe()函数实现管道通信

(1)进入源代码文件放置目录,并对原文件进行编译

(2)运行

题目二:

1.使用系统调用msgget(), msgsnd(), msgsrv()及msgctl()编制一长度为1K的消息(如个人通信录信息)的发送和接收程序.

(1)编译

 

(2)运行

每当Client发送一个消息后,server接收该消息,Client再发送下一条。“(Client)sent”和“(server)received”的字样在屏幕上交替出现。

2. 使用共享存储区相关的系统调用  shmget(),shmat(),sgmdt(),shmctl(),编制一个与上述功能相同的程序

在运行的过程中,发现每当client发送一次数据后,server要等大约0.1秒才有响应。同样,之后client又需要等待大约0.1秒才发送下一个数据。当client端发送了数据后,并没有任何措施通知server端数据已经发出,需要由client的查询才能感知。此时,client端并没有放弃系统的控制权,仍然占用CPU的时间片。只有当系统进行调度时,切换到了server进程,再进行应答。

3. 比较上述两种消息通信机制中数据传输的时间

当数据量比较少时,第一种方式传输数据比利用第二种方式传输数据所有的时间要少一些。

当消息队列和共享区建立好后, 共享区的数据传输受到了系统硬件的支持, 不耗费多余的资源; 而消息传递,由软件进行控制和实现, 需要消耗一定的CPU资源. 因此, 共享区更适合频繁和大量的数据传输。

五、源程序

 1. pipe.c

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

int main(){
    int fd[3], pid1, pid2;
    char OutPipe[100], InPipe[100];   //建立管道文件,并将文件描述词通过数组返回
pipe(fd);  //父进程建立管道
    while ((pid1 = fork()) == -1) ;    //创建子进程pid1,直到成功
if (pid1 == 0){
        lockf(fd[1], 1, 0);  //给fd[1]文件上锁 ,实现进程互斥
        sprintf(OutPipe, "Child process 2 is sending a message!");     //格式化字符串
        write(fd[1], OutPipe, 50);
        sleep(1);    //休眠
        lockf(fd[1], 0, 0);    //给fd[1]文件解锁
        exit(0);
    }else{    //执行父进程
        while ((pid2 = fork()) == -1) ;   //创建子进程pid2,直到成功
        if (pid2 == 0){
            lockf(fd[1], 1, 0);
            sprintf(OutPipe, "Child process 1 is sending a message!");  
            write(fd[1], OutPipe, 50);
            sleep(1);
            lockf(fd[1], 0, 0);
            exit(0);   //关闭pid2
        }else{
            read(fd[0], InPipe, 50);
            printf("%s\n", InPipe);
            wait(0);
  //从fd[0]代表的读端 读取50字节 保存在buf缓冲区之后,文件的当前读写指针向后移动50字节
            read(fd[0], InPipe, 50);
            printf("%s\n", InPipe);
            exit(0);   //结束父进程
        }
    }
    return 0;
}

 2.1 MessageOne

#include <stdio.h> 
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#define MSGKEY 75          //定义关键词MEGKEY
struct msgform                //定义消息结构
{
    long mtype;
    char mtexe[1030];         //文本长度
}msg;
int msgqid,i;
 
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);       //发送消息msg入msgid消息队列
    }
    exit(0);
}
 
void SERVER(){ 
  msgqid=msgget(MSGKEY,0777|IPC_CREAT); //创建一个所有用户都可以读、写、执行的队列
 do{
      msgrcv(msgqid,&msg,1030,0,0);    //从队列msgid接受消息msg
      printf("(server)receive\n");
  }while(msg.mtype!=1);             //消息类型为1时,释放队列
      msgctl(msgqid, IPC_RMID,0);      //消除消息队列的标识符
      exit(0);
}
 
int main(){
  if(fork())       //父进程
	  SERVER();
    else           //子进程
	  CLIENT();
    wait(0);
    wait(0);
}

 2.2 MessageTwo

#include<stdlib.h>
#include<unistd.h>
#include<stdio.h>
#include<wait.h>
#include<sys/types.h>
#include<sys/msg.h>
#include<sys/ipc.h>
#include <sys/shm.h>
#define SHMKEY  75                   //定义共享区关键词
int shmid,i;
int *addr;
 
void CLIENT(){    
    int i;    
    shmid=shmget(SHMKEY,1024,0777);    //获取共享区,长度1024,关键词SHMKEY
    addr=shmat(shmid,0,0);                //共享区起始地址为addr    
    for(i=9;i>=0;i--) {        
        while(*addr!= -1);                          
        printf("(client)sent\n");                 //打印(client)sent      
        *addr=i;                             //把i赋给addr  
    }    
    exit(0);
}
 
void SERVER(){     
    shmid=shmget(SHMKEY,1024,0777|IPC_CREAT);    //创建共享区   
    addr=shmat(shmid,0,0);                           //共享区起始地址为addr 
    do {    
        *addr=-1;    
        while(*addr==-1);    
        printf("(server)received\n");               //服务进程使用共享区    
    }
    while(*addr);    
    shmctl(shmid,IPC_RMID,0);    
    exit(0);
    }
 
 int main(){    
    if(fork())
		SERVER();    
    if(fork())
		CLIENT();    
    wait(0);    
    wait(0);
}

猜你喜欢

转载自blog.csdn.net/qq_45037155/article/details/123977871