操作系统实验导航
实验一:银行家算法 https://blog.csdn.net/weixin_46291251/article/details/115384510
实验二:多级队列调度和多级反馈队列调度算法 https://blog.csdn.net/weixin_46291251/article/details/115530582
实验三:动态分区式内存管理 https://blog.csdn.net/weixin_46291251/article/details/115772341
实验四:Linux下多进程通信 https://blog.csdn.net/weixin_46291251/article/details/116274665
实验五:进程通信的三种方式 https://blog.csdn.net/weixin_46291251/article/details/116301250
实验六:Linux文件系统实验 https://blog.csdn.net/weixin_46291251/article/details/116423798
实验七:自制简单U盘引导程序 https://blog.csdn.net/weixin_46291251/article/details/116427629
实验八:磁盘调度算法 https://blog.csdn.net/weixin_46291251/article/details/116431907
实验九:请求分页系统中的置换算法 https://blog.csdn.net/weixin_46291251/article/details/116443021
学习笔记:操作系统复习笔记 https://blog.csdn.net/weixin_46291251/article/details/117086851

背景:(关于进程通信的几种方式)

题目描述:

编写一主程序可以由用户选择如下三种进程通信方式:

  1. 使用管道来实现父子进程之间的进程通信
    子进程向父进程发送自己的进程标识符,以及字符串“is sending a message to parent”。父进程则通过管道读出子进程发来的消息,将消息显示在屏幕上,然后终止。

  2. 使用共享存储区来实现两个进程之间的进程通信
    进程 A 创建一个长度为 512 字节的共享内存,并显示写入该共享内存的数据;进程 B 将共享内存附加到自己的地址空间,并向共享内存中写入数据。

  3. 使用消息缓冲队列来实现 client 进程和 server 进程之间的通信
    server 进程先建立一个关键字为 SVKEY(如 75)的消息队列,然后等待接收类型为 REQ(例如 1)的消息;
    在收到请求消息后,它便显示字符串“serving for client”和接收到的 client 进程的进程标识数,表示正在为 client 进程服务;
    然后再向 client 进程发送应答消息,该消息的类型是 client 进程的进程标识数,而正文则是 server 进程自己的标识ID。
    client 进程则向消息队列发送类型为 REQ 的消息(消息的正文为自己的进程标识 ID) 以取得 sever 进程的服务,并等待 server 进程发来的应答;
    然后显示字符串“receive reply from”和接收到的 server 进程的标识 ID。

算法设计:

管道:

对管道的设计类似于上一篇文章:Linux下两个子进程通过管道(pipe)通信

共享缓冲区:

共享内存是操作系统常采用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种通信方式需要依靠某种同步机制,如互斥锁和信号量等。

共享内存通过系统调用 shmget()、shmat()、shmdt()、shmctl()来实现.
  • 系统调用:shmget()
    原型:int shmget(key_t key, int size, int shmflg)
    参数:
    Key:共享存储区的名字;
    Size:共享存储区的大小(以字节计);
    Shmflag:用户设置的标志,如 IPC_CREAT,IPC_CREAT 表示若系统中尚无指名的共享存储区,则由核心建立一个共享存储区;如系统中已有共享存储区,便忽略 IPC_CREAT。
    返回值:
    成功返回共享内存的标识符;不成功返回-1
  • 系统调用:shmat()
    原型:void* shmat(int shmid, const void *shmaddr, int shmflg);
    参数:
    Shmid:共享存储区的标识符;
    Shmaddr:用户给定的,将共享存储区附接到进程的虚拟地址空间;
    Shmflg:规定共享存储区的读、写权限,以及系统是否应对用户规定的地址做舍入操作。其值为 SHM_RDONLY 时,表示只能读,其值为 0 时,表示可读、可写,其值为 SHM_RND(取整)时,表示操作系统在必要时舍去这个地址。返回值:
    该系统调用的返回值为共享存储区所附接到的进程虚地址。
  • 系统调用:shmdt()
    原型:int shmdt(void *shmaddr)参数:
    Shmaddr:要断开连接的虚地址,亦即以前由连接的系统调用 shmat()所返回的虚地址。
    返回值:
    调用成功时,返回 0 值,调用不成功,返回-1 值。
  • 系统调用:shmctl()
    原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    参数:
    Shmid:由 shmget 所返回的标识符;
    Cmd:是操作命令,可以分多种类型:
    (1)用于查询有关共享存储区的情况。如其长度、当前连接的进程数、共享区的创建者标识符。
    (2)用于设置或改变共享存储区的属性,如共享存储区的许可权、当前连接的进程计数等。
    (3)对共享存储区的加锁和解锁命令。
    (4)删除共享存储区标识符等。
    Buf:用户缓冲区地址。返回值:
    调用成功则返回 0,如果失败则返回-1。
创建共享缓冲区并等待被写入,然后读取:
//共享存储
        shmid=shmget(SHMKEY,512,0777|IPC_CREAT); /*创建共享存储区*/
        printf("进程%d创建了共享存储区:\n",getpid());
        addr=shmat(shmid,0,0);        /*获取首地址*/
        *addr=-1;
        while (*addr == -1);//阻塞,用于进程同步
        printf("进程%d读取共享存储区:%d\n",getpid(),*addr);
   
等待共享缓冲区被创建,然后写入:
 //共享存储
        shmid=shmget(SHMKEY,512,0777);      /*打开共享存储区*/
        addr=shmat(shmid,0,0);           /*获得共享存储区首地址*/
        while(*addr != -1);//阻塞,用于进程同步
        
        int xie = getpid();
        printf("进程%d写入共享存储区:%d\n",getpid(),xie);
        *addr= xie;

消息缓冲队列:

自定义消息数据结构:
struct msgform  //消息结构
{
   long mtype; 
   char mtext[250];  //文本长度
}; 
需要用到的函数: msgget()、msgsnd()、msgrcv()
  • 原型:int msgget(key_t key,int msgflg)
    参数:
    key:消息队列关联的键。
    msgflg:消息队列的建立标志和存取权限。
    返回值:成功执行时,返回消息队列标识值。失败返回-1,
  • 原型:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    参数:
    msqid:消息队列的识别码。
    msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构。
    msgsz:消息的大小。
    msgtyp:从消息队列内读取的消息形态。如果值为零,则表示消息队列中的所有消息都会被读取。
    msgflg:用来指明核心程序在队列没有数据的情况下所应采取的行动。如果 msgflg 和常数 IPC_NOWAIT 合用,则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当 msgflg 为 0 时, msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式。
    返回值:当成功执行时, msgrcv()返回拷贝到 mtext 数组的实际字节数。失败返回-1,
server() 端
void server() 
{   
   int c_pid,s_pid,msgqid;//client的pid 和 消息队列识别码
   struct msgform msg;
   msgqid=msgget(SVKEY,0777|IPC_CREAT);  //创建 75#消息队列 
   while(1){
        msgrcv(msgqid,&msg,250,1,0);  //接收client进程标识数消息 
        c_pid=*(int *)msg.mtext;  //获得 cilent 进程标识数
        if(msg.mtype==REQ){
            printf("(server):serving for client pid=%d\n",c_pid); 
            break;
        }
   }
   

   msg.mtype=c_pid;  //消息类型为 client 进程标识数
   s_pid = getpid();
   *(int *)msg.mtext=s_pid; //获取 server 进程标识数 
   //printf("ser修改后的 mtype=%d,,mtext=%d\n",(int)msg.mtype,*(int *)msg.mtext); //调试信息
   msgsnd(msgqid,&msg,sizeof(int),0);  //发送消息 
   exit(0); 
} 
client() 端
void client() 
{ 
   int pid,msgqid;//client的pid 和 消息队列识别码
   struct msgform msg;
   msgqid=msgget(SVKEY,0777);  //打开 75#消息队列
   
   pid=getpid();  //获取client进程标识符
   *(int *)msg.mtext=pid;  //pint指针指向client进程标识符
   msg.mtype=REQ;  //消息类型为 1
   //printf("cli修改后的 mtype=%d,,mtext=%d\n",(int)msg.mtype,*(int *)msg.mtext); //调试信息

   msgsnd(msgqid,&msg,sizeof(int),0);  //发送消息msg入msgqid消息队列
   while(1){
        msgrcv(msgqid,&msg,250,pid,0);  //从队列msgqid接收消息msg 
        if(msg.mtype!=REQ){
            printf("(client):receive reply from pid=%d\n",*(int *)msg.mtext);  //显示 server进程标识数
            exit(0); 
        }
   }
} 

实现代码:

/*
操作系统上机题目6:进程通信
1.利用管道实现父子进程的通信
2.使用共享存储区实现两个进程的通信
    shmget();//创建共享内存。
    shmat();//映射共享内存
    shmdt();//撤销映射
原型:int shmget(Key_t key,int size,int shmflg);
    key:共享内存的键值
    size:共享内存大小
    shmflg:权限位
    返回值:共享内存段标识符

3.使用消息缓冲队列实现client和server的通信


*/

#include <signal.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/msg.h>
#define  SHMKEY  82
#define  SVKEY 75 //定义关键字SVKEY
#define  REQ  1

struct msgform  //消息结构
{
   long mtype; 
   char mtext[250];  //文本长度
}; 

void server() 
{   
   int c_pid,s_pid,msgqid;//client的pid 和 消息队列识别码
   struct msgform msg;
   msgqid=msgget(SVKEY,0777|IPC_CREAT);  //创建 75#消息队列 
   while(1){
        msgrcv(msgqid,&msg,250,1,0);  //接收client进程标识数消息 
        c_pid=*(int *)msg.mtext;  //获得 cilent 进程标识数
        if(msg.mtype==REQ){
            printf("(server):serving for client pid=%d\n",c_pid); 
            break;
        }
   }
   

   msg.mtype=c_pid;  //消息类型为 client 进程标识数
   s_pid = getpid();
   *(int *)msg.mtext=s_pid; //获取 server 进程标识数 
   //printf("ser修改后的 mtype=%d,,mtext=%d\n",(int)msg.mtype,*(int *)msg.mtext); //调试信息
   msgsnd(msgqid,&msg,sizeof(int),0);  //发送消息 
   exit(0); 
} 
void client() 
{ 
   int pid,msgqid;//client的pid 和 消息队列识别码
   struct msgform msg;
   msgqid=msgget(SVKEY,0777);  //打开 75#消息队列
   
   pid=getpid();  //获取client进程标识符
   *(int *)msg.mtext=pid;  //pint指针指向client进程标识符
   msg.mtype=REQ;  //消息类型为 1
   //printf("cli修改后的 mtype=%d,,mtext=%d\n",(int)msg.mtype,*(int *)msg.mtext); //调试信息

   msgsnd(msgqid,&msg,sizeof(int),0);  //发送消息msg入msgqid消息队列
   while(1){
        msgrcv(msgqid,&msg,250,pid,0);  //从队列msgqid接收消息msg 
        if(msg.mtype!=REQ){
            printf("(client):receive reply from pid=%d\n",*(int *)msg.mtext);  //显示 server进程标识数
            exit(0); 
        }
   }
} 


int childpid[10]={-1,-1,-1,-1},status=999;

int main(){

	int flg,fd[2];
	flg = pipe(fd); //创建管道
    if (flg == -1)
        perror("pipe error");

    int  shmid,i,* addr;//用于共享存储区通信

	childpid[0]=getpid();//012分别是父进程和两个子进程的pid

	for(int i=1;i<=2;i++){
		int pid=fork();
		if(pid!=0)
			childpid[i] =pid;
		else{
			childpid[i] =getpid();
			break;
		}
	}
    if(getpid()==childpid[0]){//父进程

    }
    
    if(getpid()==childpid[1]){//子进程1
        //利用管道给父进程发送消息
        close(fd[0]);
        char child[100]="is sending a message to parent !\0";
        write(fd[1],child,strlen(child)+1);

        //共享存储
        shmid=shmget(SHMKEY,512,0777|IPC_CREAT); /*创建共享存储区*/
        printf("进程%d创建了共享存储区:\n",getpid());
        addr=shmat(shmid,0,0);        /*获取首地址*/
        *addr=-1;
        while (*addr == -1);//阻塞,用于进程同步
        printf("进程%d读取共享存储区:%d\n",getpid(),*addr);
   
        server(); 

    }
   
    if(getpid()==childpid[2]){//子进程2
       //接受来自子进程1 的管道消息
        close(fd[1]);
        printf("%d  ",childpid[1]);
        char msg[100];
        read(fd[0],msg,sizeof(msg));
            printf("%s\n",msg);
        

        //共享存储
        shmid=shmget(SHMKEY,512,0777);      /*打开共享存储区*/
        addr=shmat(shmid,0,0);           /*获得共享存储区首地址*/
        while(*addr != -1);//阻塞,用于进程同步
        
        int xie = getpid();
        printf("进程%d写入共享存储区:%d\n",getpid(),xie);
        *addr= xie;
      
        client(); 
    }

    shmctl(shmid,IPC_RMID,0);     /*撤消共享存储区,归还资源*/
    return 0;
}
 //system("ipcs  -m");//显示共享内存段的信息
//printf("@@@@@@@@@@@@@@@@@@@@@@");

运行结果:

在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐