进程通信的三种方式(管道、消息缓冲队列、共享缓冲区)
管道:消息缓冲队列:共享缓冲区:
操作系统实验导航
实验一:银行家算法 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
背景:(关于进程通信的几种方式)
题目描述:
编写一主程序可以由用户选择如下三种进程通信方式:
-
使用管道来实现父子进程之间的进程通信
子进程向父进程发送自己的进程标识符,以及字符串“is sending a message to parent”。父进程则通过管道读出子进程发来的消息,将消息显示在屏幕上,然后终止。 -
使用共享存储区来实现两个进程之间的进程通信
进程 A 创建一个长度为 512 字节的共享内存,并显示写入该共享内存的数据;进程 B 将共享内存附加到自己的地址空间,并向共享内存中写入数据。 -
使用消息缓冲队列来实现 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("@@@@@@@@@@@@@@@@@@@@@@");
运行结果:
更多推荐
所有评论(0)