Linux---进程通信(四)---IPC通信之共享内存

系列文章目录

文章一: Linux—进程通信(一)—进程通信概述
文章二: Linux—进程通信(二)—管道
文章三: Linux—进程通信(三)—信号通信
文章四: Linux—进程通信(四)—IPC通信之共享内存
文章五: Linux—进程通信(五)—IPC通信之消息队列
文章六: Linux—进程通信(六)—IPC通信之信号灯



前言

本文主要介绍了IPC通信的共享内存这一通信方式


一、IPC通信是什么?

IPC通信也是进程间通信的一种通信方式,它的通信对象是IPC对象,也是只存在于内核空间。

IPC对象有三种:

1.共享内存
2.消息队列
3.信号灯

IPC对象类似于文件,下面对IPC和文件I/O函数的比较:

文件IOIPC
openmsgget,shmget,semget
readmsgsnd,shmat,semop
writemsgrecv,shmdt,semop
closemsgctl,shmctl,semctl

二、共享内存

1.共享内存是什么?

共享内存是一块存在于内核空间的缓存,类似与用户空间的数组或malloc函数分配的空间一样。

这里Linux专门提供了查看内核共享内存信息的命令:

查看共享内存

ipcs  -m

删除共享内存对象

ipcrm  -m  id

注意:

1.共享内存和管道不一样,读取之后内容仍在共享内存中
2.共享内存创建之后会一直存在在内核中,直到被删除或者系统关闭

2.共享内存的函数

共享内存的创建

共享内存的创建

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

所需头文件;

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

函数功能:

创建一个共享内存

函数参数:

1.key:
	(1)IPC_PRIVATE
	(2)ftok()函数的返回值
2.size:共享内存的大小
3.shmflg:相当于open函数的权限位,也可以用8进制表示

函数返回值:

1.成功:共享内存的段标识符,即其ID,相当于普通文件的“文件描述符”
2.失败:返回-1

例子1:shmget的使用

#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main()
{
	int shmid;
	shmid=shmget(IPC_PRIVATE,128,0777);
	if(shmid<0)
	{
		printf("create share memory failure\n");
		return -1;
	}
	printf("create share memory success shmid=%d\n",shmid);
	system("ipcs -m");
	return 0;
}

执行结果:
在这里插入图片描述

ftok函数

函数原型:

char ftok(const char *path,char key);

函数功能:

生成key值

函数参数:

1.第一个参数是文件路径和文件名
2.第二个参数是一个字符

函数返回值:

1.成功返回一个key值
2.出错返回-1

注意:

1.ftok函数产生的key值可以实现无亲缘关系的进程之间的通信。
2.用宏创建的key只能用于有亲缘关系的进程之间的通信。

共享内存映射函数

函数原型:

void *shmat(int shmid,const void *shmaddr,int shmflg);

所需头文件;

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

函数功能:

将共享内存的空间,以映射的方式“拷贝”到用户空间

函数参数:

1.第一个是共享内存的ID
2.第二个参数是想要映射到的用户空间的内存地址,NULL为系统自动完成的映射
3.shmflg:
	(1)SHM_RDONLY是共享内存只读
	(2)默认是0,表示共享内存可读写

函数返回值:

1.成功:返回映射后的内存空间的首地址
2.失败:返回NULL

共享内存映射空间删除函数

函数原型:

int shmdt(const void *shmaddr);

所需头文件;

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

函数功能:

将进程中用户空间的地址映射删除

函数参数:

共享内存映射后的地址

函数返回值:

1.成功:返回0
2.失败:返回-1

例子2:共享内存映射

#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
int main()
{
	int shmid;
	char *p;
	int key;
	key=ftok("./a.c","a");
	if(key<0)
	{
		printf("create key failure\n");
		return -2;
	}
	printf("create key success key=%x\n",key);
	shmid=shmget(key,128,IPC_CREAT|0777);
	if(shmid<0)
	{
		printf("create share memory failure\n");
		return -1;
	}
	printf("create share memory success shmid=%d\n",shmid);
	system("ipcs -m");

	p=(char *)shmat(shmid,NULL,0);
	if(p==NULL)
	{
		printf("shmat failure\n");
		return -3;
	}

	fgets(p,128,stdin);
	
	printf("share memory datas:\n");
	printf("%s\n",p);
	
	shmdt(p);
	
	return 0;
}

执行结果:
在例子1的基础上,键盘输入一串字符,接着打印输入的内容于屏幕上。

删除共享内存对象函数

函数原型:

int shmctl(int shmid,int cmd,struct shmid_ds *buf);

所需头文件;

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

函数功能:

删除内核空间中的共享内存。

函数参数:

1.shmid:要操作的共享内存的标识符
2.cmd:
	(1)IPC_STAT(获取共享内存的属性)
	(2)IPC_SET(设置共享内存的属性)
	(3)IPC_RMID(删除对象)
3.buf:一个存储空间,指定IPC_STAT/IPC_SET时用以保存/设置属性。

函数返回值:

成功返回0,失败返回-1

例子3:删除内核空间的共享内存

#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
int main()
{
	int shmid;
	char *p;
	int key;
	key=ftok("./a.c","a");
	if(key<0)
	{
		printf("create key failure\n");
		return -2;
	}
	printf("create key success key=%x\n",key);
	shmid=shmget(key,128,IPC_CREAT|0777);
	if(shmid<0)
	{
		printf("create share memory failure\n");
		return -1;
	}
	printf("create share memory success shmid=%d\n",shmid);
	system("ipcs -m");

	p=(char *)shmat(shmid,NULL,0);
	if(p==NULL)
	{
		printf("shmat failure\n");
		return -3;
	}

	fgets(p,128,stdin);
	
	printf("share memory datas:\n");
	printf("%s\n",p);
	
	shmdt(p);
	shmctl(shmid,IPC_RMID,NULL);
	system("ipcs -m");
	
	return 0;
}

执行结果:

共享内存将被成功删除。

三、C代码实现进程通信实例

例子4:基于共享内存实现父子进程之间的通信

#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
void myfun(int signum)
{
	return;//返回至pause函数之后
}
int main()
{
	int shmid;
	char *p;
	int key;
	int pid;
	shmid=shmget(IPC_PRIVATE,128,IPC_CREAT|0777);
	if(shmid<0)
	{
		printf("create share memory failure\n");
		return -1;
	}
	printf("create share memory success shmid=%d\n",shmid);
	
	pid=fork();
	if(pid>0)
	{
		signal(SIGUSR2,myfun);
		p=(char *)shmat(shmid,NULL,0);
		if(p==NULL)
		{
			printf("parent process shmat failure\n");
			return -3;
		}
		while(1)
		{
			printf("parent process start write share memory:\n");
			fgets(p,128,stdin);	
			kill(pid,SIGUSR1);//通知子进程去读
			pause();//等待子进程读,会被信号唤醒
		}
	}
	if(pid==0)
	{
		signal(SIGUSR1,myfun);//默认处理方式是杀死进程,我们这里需要改变信号处理方式。
		p=(char *)shmat(shmid,NULL,0);
		if(p==NULL)
		{
			printf("parent process shmat failure\n");
			return -3;
		}
		while(1)
		{
			pause();
			printf("share memory datas:\n");
			printf("%s\n",p);
			kill(getppid(),SIGUSR2);	
		}
	}
	shmdt(p);
	shmctl(shmid,IPC_RMID,NULL);
	system("ipcs -m");
	
	return 0;
}

执行结果:

父进程不断地从键盘输入一串字符,子进程及时地打印出这串字符。

例子5:基于共享内存实现无亲缘关系进程之间的通信

编写发送信息的服务器
a进程代码:

#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
struct mybuf
{
	int pid;
	char buf[124];
};
void myfun(int signum)
{
	return;//返回至pause函数之后
}
int main()
{
	int shmid;
	struct mybuf *p;
	int key;
	key=ftok("./a.c","a");
	if(key<0)
	{
		printf("create key failure\n");
		return -2;
	}
	printf("create key success key=%x\n",key);
	shmid=shmget(key,128,IPC_CREAT|0777);
	if(shmid<0)
	{
		printf("create share memory failure\n");
		return -1;
	}
	printf("create share memory success shmid=%d\n",shmid);

	signal(SIGUSR2,myfun);
	p=(struct mybuf)shmat(shmid,NULL,0);
	if(p==NULL)
	{
		printf("shmat failure\n");
		return -3;
	}

	p->pid=getpid();
	pause();
	pid=p->pid;

	while(1)
	{
		printf("parent process start write share memory:\n");
		fgets(p->buf,128,stdin);	
		kill(pid,SIGUSR1);//通知子进程去读
		pause();//等待子进程读,会被信号唤醒
	}
	
	shmdt(p);
	shmctl(shmid,IPC_RMID,NULL);
	system("ipcs -m");
	
	return 0;
}

编写接收信息的客户端
b进程代码:

#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
struct mybuf
{
	int pid;
	char buf[124];
};
void myfun(int signum)
{
	return;//返回至pause函数之后
}
int main()
{
	int shmid;
	struct mybuf *p;
	int key;
	key=ftok("./a.c","a");
	if(key<0)
	{
		printf("create key failure\n");
		return -2;
	}
	printf("create key success key=%x\n",key);
	shmid=shmget(key,128,IPC_CREAT|0777);
	if(shmid<0)
	{
		printf("create share memory failure\n");
		return -1;
	}
	printf("create share memory success shmid=%d\n",shmid);

	signal(SIGUSR1,myfun);
	p=(struct mybuf)shmat(shmid,NULL,0);
	if(p==NULL)
	{
		printf("shmat failure\n");
		return -3;
	}

	pid=p->pid;
	p->pid=getpid();
	kill(pid,SIGUSR2);

	while(1)
	{
		pause();
		printf("share memory datas:\n");
		printf("%s\n",p->buf);
		kill(pid,SIGUSR2);	
	}
	
	shmdt(p);
	shmctl(shmid,IPC_RMID,NULL);
	system("ipcs -m");
	
	return 0;
}

执行结果:

无亲缘关系的两个进程,a进程不断地从键盘输入一串字符,b进程及时地打印出这串字符。

总结

本文运用了信号通信的相关知识,可以看文章:
Linux—进程通信(三)—信号通信
对于fgets有疑问的可以看文章:
Linux----标准IO文件操作函数总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SigmaBull

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值