Linux——进程间通信(共享内存,消息队列)

共享内存

共享内存通过内核对象,使得不同的进程在自己的虚拟地址空间上分配一块空间映射到相同的物理内存空间上,这块物理内存空间对于映射到上面的每个进程而言都是可以访问的。(临界资源)
在这里插入图片描述
共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc()分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

共享内存的使用

与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单。它们声明在头文件 sys/shm.h 中。
1.获取或创建内核对象,并且制定共享内存的大小(系统分配物理空间是,按照页进行分配)

int shmget(key_t key, int size, int flag);

只是创建内核对象,并申请物理空间

  • key_t key:与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget()函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.

不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某个资源,程序对所有共享内存的访问都是间接的,程序先通过调用shmget()函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget()函数的返回值),只有shmget()函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。

  • int size:size以字节为单位指定需要共享的内存容量
  • int flag:falg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。
    2.分配自己虚拟地址空间映射到共享内存的物理空间上
void *shmat(int shmid, void *addr, int flag);
  • shmid:shmid是由shmget()函数返回的共享内存标识。
  • void *addr:addr指定共享内存连接到当前进程中的地址位置,通常为NULL,表示让系统来选择共享内存的地址。
  • int flag:flag是一组标志位,通常为0。

调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.

3.断开当前进程与共享内存的映射

int shmdt(const void *addr);

4.操作共享内存的方法

int shmctl(int shmid, int cmd, struct shmid_t *buf);
  • int shmid:shmid是shmget()函数返回的共享内存标识符。
  • int cmd:command是要采取的操作,它可以取下面的三个值 :

IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段

  • struct shmid_t *buf:buf是一个结构指针,它指向共享内存模式和访问权限的结构

因为有连接计数器,除非最后一个进程与该共享段断开连接,则删除该共享段。否则,并不会真正删除该共享段,但是共享内存的内核对象会被立即删除,不能使用shmat方法与该段连接。
一个进程调用该方法删除后,不会影响之前已经和该共享存储段连接的进程

在这里插入图片描述

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

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>

#include"sem.h"

#define READSEM 1
#define WRITESEM 0

int main()
{
	int shmid = shmget((key_t)1234,128,0664 | IPC_CREAT);
	assert(shmid != -1);

	char *ptr = (char*)shmat(shmid,NULL,0);
	assert(ptr != (char*)-1);
	
	int initVal[] = {1,0};
	int semid = SemGet(1234,intVal,2);
	assert(semid != -1);
	
	//A进程写
	while(1)
	{
		SemP(semid,WRITESEM);
		printf("Input:");
		
		fgets(ptr,127,stdin);
		
		SemV(semid,READSEM);
		
		if(strncmp(ptr,"end",3) == 0)
		{
			break;
		}
	}
	
	shmdt(ptr);
	exit(0);
}
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>

#include"sem.h"

#define READSEM 1
#define WRITESEM 0

int main()
{
	int shmid = shmget((key_t)1234,128,0664 | IPC_CREAT);
	assert(shmid != -1);

	char *ptr = (char*)shmat(shmid,NULL,0);
	assert(ptr != (char*)-1);
	
	int initVal[] = {1,0};
	int semid = SemGet(1234,intVal,2);
	assert(semid != -1);
	
	//B进程读
	while(1)
	{
		SemP(semid,READSEM);
		
		if(strncmp(ptr,"end",3) == 0)
		{
			break;
		}
		
		int i = 0;
		for(;i < strlen(ptr) - 1;i++)
		{
			printf("%c",toupper(ptr[i]));
			fflush(stdout);
			sleep(1);
		}
		printf("\n");
		SemV(semid,WRITESEM);
	}
	
	shmdt(ptr);
	exit(0);
}

从上面的代码中我们可以看出:
共享内存是最快的IPC,在通信过程中少了两次数据的拷贝。(相较于管道)

命令管理共享内存

查看 ipcs -m
删除 ipcrm -m shmid

消息队列

管道和共享内存:字节流数据
消息:数据报(类型+数据)
队列:优先级队列
可以指定类型来读取,在相同类型下,按照先进先出的顺序
在这里插入图片描述
消息队列的操作:

1.创建和访问一个消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflag);
  • key:某个消息队列的名字,用ftok()产生
  • msgflag:有两个选项IPC_CREAT和IPC_EXCL,单独使用IPC_CREAT,如果消息队列不存在则创建之,如果存在则打开返回;单独使用IPC_EXCL是没有意义的;两个同时使用,如果消息队列不存在则创建之,如果存在则出错返回。
  • 返回值:成功返回一个非负整数,即消息队列的标识码,失败返回-1
    2.把一条消息添加到消息队列中
int msgsnd(int msqid, const void *msqp, size_t msqsz, int msqflg);
  • msgid:由msgget函数返回的消息队列标识码
  • msqp:指针指向准备发送的消息
  • msqsz:msqp指向的消息的长度(不包括消息类型的long int长整型)
  • msgflg:默认为0
  • 返回值:成功返回0,失败返回-1

消息结构一方面必须小于系统规定的上限,另一方面必须以一个long int长整型开始,接受者以此来确定消息的类型

struct msgbuf {
long mtye;
char mtext[1]; };

3.从一个消息队列接受消息

ssize_t msgrcv(int msqid, void *msqp, size_t msqsz, long msqtyp, int msqflg);

参数:与msgsnd相同
返回值:成功返回实际放到接收缓冲区里去的字符个数,失败返回-1
如果指定的空间不能将消息的数据完全存储下来,则本次读取不成功

4.消息队列的控制函数

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
  • msqid:由msgget函数返回的消息队列标识码
  • cmd:有三个可选的值

IPC_STAT 把msqid_ds结构中的数据设置为消息队列的当前关联值
IPC_SET 在进程有足够权限的前提下,把消息队列的当前关联值设置为msqid_ds数据结构中给出的值
IPC_RMID 删除消息队列

  • 返回值:成功返回0,失败返回-1

msg.h

#program once
typedef struct msgbuf
{
	long mtype;
	char mtext[128];
}MsgBuf;

发送

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

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

#include"msg.h"

int main(int argc,char *argv[])
{
	//argv[1]:type
	//argv[2]:data
	if(argc < 3)
	{
		printf("Please input type and data\n");
		exit(0);
	}
	
	MsgBuf mess;
	memset(&mess,0,sizeof(mess));
	sscanf(argv[1],"%d",&mss.mtype);
	strcpy(msss.mtext,argv[2]);
	
	int msgid = msgget((key_t)1234,IPC_CREAT | 0664);
	assert(msgid != -1);

	msgsnd(msgid,&mess,strlen(mess,mtext),0);
	
	exit(0);
}

接收(注意读取问题)

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

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

#include"msg.h"

int main(int argc,char *argv[])
{
	if(argc < 2)
	{
		printf("please input type\n");
		exit(0);
	}
	
	MsgBuf mess;
	memset(&mess,0,sizeof(mess));
	
	long type = 0;
	sscanf(argv[1],"%d",&type);
	
	int msgid = msgget((key_t)1234,IPC_CREAT | 0664);
	assert(msgid != -1);
	
	msgrcv(msgid,&mess,127,type,0);//改成5看看结果,消息截取
	printf("type:%d,data :%s\n",mess.mtype,mess.mtext);
	
	exit(0);
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值