linux进程通讯 -- 共享内存

POSIX内存映射机制

内存映射函数:mmap munmap msync
函数原型:
#include <unistd.h>
#include <sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
int msync(const void *start, size_t length, int flags); //把对内存区域所做的更改同步到文件
int munmap(void *start, size_t length);

void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
作用:用来将某个文件内容映射到内存中,对该内存区域的存取即是直接对该文件内容的读写。通过这样可以加快文件访问速度。返回值:成功,返回映射的内存的起始地址。
第一个参数 start 指向欲对应的内存起始地址,通常设为 NULL,代表让系统自动选定地址,对应成功后该地址会返回。
第二个参数 length 代表将文件中多大的部分对应到内存。
第三个参数 prot 代表映射区域的保护方式有下列组合:
 PROT_EXEC 映射区域可被执行
 PROT_READ 映射区域可被读取
 PROT_WRITE 映射区域可被写入
 PROT_NONE 映射区域不能存取
第四个参数 flags 会影响映射区域的各种特性:
MAP_FIXED 如果参数 start 所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。
MAP_SHARED 对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”
MAP_ANONYMOUS 建立匿名映射。此时会忽略参数 fd,不涉及文件,而且映射区域无法和
其他进程共享。
MAP_DENYWRITE 只允许对映射区域的写入操作,而不能对 fd 指向的文件进行读写,对该文件直接写入的操
作将会被拒绝。
MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。
在调用 mmap()时必须要指定 MAP_SHARED 或 MAP_PRIVATE。
第五个参数 fd 为 open()返回的文件描述词,代表欲映射到内存的文件。
第六个参数 offset 为文件映射的偏移量,通常设置为 0,代表从文件最前方开始对应,offset 必须是分页大小的整数倍。

•munmap():用来取消参数 start 所指的映射内存起始地址,参数 length 则是欲取消的内存大小。当进程结束,映射内存会自动解除,但关闭对应的文件描述词时不会解除映射。返回值:如果解除映射成功则返回 0,否则返回

写入
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
main()
{
	int fd = open("mm.dat",O_RDWR|O_CREAT); //创建一个新文件并以读写方式打开
	char buf[20] = {0};
	write(fd,buf,sizeof(buf));//文件不能为空,写入 20 个 0,保证文件不为空
	lseek(fd,0,SEEK_SET);//重新定位到文件头
	char*p=(char*)mmap(NULL,20*sizeof(char),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	memset(p, 0, 20); //最好先初始化为 0
	memcpy(p, "hello world", 12);//写 hello 到内存
	sleep(10);
	munmap(p, 20);
	close(fd);
}
读出
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
main()
{
	int fd = open("mm.dat",O_RDWR); //需要打开同一个文件
	char *p=(char*)mmap(NULL,20*sizeof(char),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	puts(p);
	munmap(p, 20);
	close(fd);
}

在这里插入图片描述
System V 共享内存机制: shmget shmat shmdt shmctl
共享内存也是进程间(进程间不需要有继承关系)通信的一种常用手段。一般 OS 通过内存映射与页交换技术,使进程的内存空间映射到不同的物理内存,这样能保证每个进程运行的独立性,不至于受其它进程的影响。但可以通过共享内存的方式,使不同进程的虚拟内存映射到同一块物理内存,一个进程往这块物理内存的更新数据,另外的进程可以立即看到这块物理内存的修改。

内存映射和共享内存的区别:
内存映射:跟普通文件的读写相比,加快对文件/设备的访问速度。
共享内存:多进程间进行通信。

原理及实现:system V IPC 机制下的共享内存本质是一段特殊的内存区域,进程间需要共享的数据被放在该共享内存区域中,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。这样一个使用共享内存的进程可以将信息写入该空间,而另一个使用共享内存的进程又可以通过简单的内存读操作获取刚才写入的信息,使得两个不同进程之间进行了一次信息交换,从而实现进程间的通信。共享内存允许一个或多个进程通过同时出现在它们的虚拟地址空间的内存进行通信,而这块虚拟内存的页面被每个共享进程的页表条目所引用,同时并不需要在所有进程的虚拟内存都有相同的地址。进程对象对于共享内存的访问通过 key(键)来控制,同时通过 key 进行访问权限的检查。

函数定义如下:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
key_t ftok(const char *pathname, int proj_id);
int shmget(key_t key, int size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

函数 ftok 用于创建一个关键字,可以用该关键字关联一个共享内存段。
参数 pathname 为一个全路径文件名,并且该文件必须可访问。
参数 proj_id 通常传入一非 0 字符。
通过 pathname 和 proj_id 组合可以创建唯一的 key(对任何进程都是唯一且相同的)。
如果调用成功,返回一关键字,否则返回-1。

函数 shmget 用于创建或打开一共享内存段,该内存段由函数的第一个参数标识。函数成功则返回一个该共享内存段的唯一标识号(唯一的标识了这个共享内存段),对任何进程都是唯一且相同的,后面会用到。参数 key 是一个与共享内存段相关联的关键字,如果事先已经存在一个与指定关键字关联的共享内存段,则直接返回该内存段的标识。key 的值既可以用 ftok 函数产生,也可以是 IPC_RPIVATE(用于创建一个只属于创建进
程的共享内存,主要用于父子通信),表示总是创建新的共享内存段。
参数 size 指定共享内存段的大小,以字节为单位。
参数 shmflg 是一掩码合成值,可以是访问权限值与(IPC_CREAT 或 IPC_EXCL)的合成。IPC_CREAT 表示如果不存在该内存段,则创建它。IPC_EXCL 表示如果该内存段存在,则函数返回失败结果(-1)。如果调用成功,返回内存段标识,否则返回-1。

函数 shmat 将共享内存段映射到进程空间的某一地址。
参数 shmid 是共享内存段的标识 通常应该是 shmget 的成功返回值。
参数 shmaddr 指定的是共享内存连接到当前进程中的地址位置。通常是 NULL,表示让系统来选择共享内存出现的地址。
参数 shmflg 是一组位标识,通常为 0 即可。
如果调用成功,返回映射后的进程空间的首地址,否则返回(void*)-1。

函数 shmdt 用于将共享内存段与进程空间分离。
参数 shmaddr 通常为 shmat 的成功返回值。
函数成功返回 0,失败时返回-1.注意,将共享内存分离并没删除它,只是使得该共享内存对当前进程不再可用。

函数 shmctl 是共享内存的控制函数,可以用来删除共享内存段。
参数 shmid 是共享内存段标识 通常应该是 shmget 的成功返回值
参数 cmd 是对共享内存段的操作方式,可选为 IPC_STAT,IPC_SET,IPC_RMID。通常为 IPC_RMID,表示删除共享内存段。
参数 buf 是表示共享内存段的信息结构体数据,通常为 NULL

在亲缘之间的共享内存

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PERM S_IRUSR | S_IWUSR //表示用户可读可写 即 0600
int main(void) 
{
	/*在两个有亲属关系进程间通信,KEY 采用 IPC_PRIVATE 由系统自选*/
	int shmid = shmget(IPC_PRIVATE, 1024, PERM);
	if(shmid == -1) /* 创建 byte 的共享内存*/
	{
		fprintf(stderr,"Create Share Memory Error:%s\n\a",strerror(errno));
		exit(1);
	}
	if(fork() > 0) /* 父进程代码*/
	{
		char *p_addr = (char*)shmat(shmid, NULL, 0); // //将共享内存映射至本进程空间某一地址
		memset(p_addr, '\0', 1024); //初始化为 0
		strncpy(p_addr, "share memory", 1024); //存入(写入)内容
		printf("parent %d Write buffer: %s\n", getpid(), p_addr);
		wait(NULL); //防止僵尸进程
		sleep(2);
		shmctl(shmid, IPC_RMID, 0);/*删除共享内存,用 ipcs -m 看共享内存*/
		exit(0);
	}
	else /* 子进程代码*/
	{
		sleep(5); //让父有足够的时间写
		char *c_addr = (char*)shmat(shmid, NULL, 0); //将共享内存映射至本进程空间某一地址
		printf("Client pid=%d,shmid =%d Read buffer: %s\n",getpid(),shmid,c_addr);
		exit(0);
	}
}

在这里插入图片描述
不同进程之间的

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h> //头文件包含
#include <sys/types.h>
main()
{
	key_t key = ftok("111.txt",10); //1. 写入端先用 ftok 函数获得 key
	if(key == -1)
	{
		perror("ftok");
		exit(-1);
	}
	int shmid = shmget(key,1024,IPC_CREAT); //2. 写入端用 shmget 函数创建一共享内存段
	if(shmid == -1)
	{
		perror("shmget");
		exit(-1);
	}
	char *pMap = (char *)shmat(shmid, NULL, 0); //3. 获得共享内存段的首地址
	if(pMap==-1){
		perror("shmat");
		exit(-1);
	}
	strcpy(pMap,"hello world");//4. 往共享内存段中写入内容
	if(shmdt(pMap) == -1) //5. 关闭共享内存段
	{
		perror("shmdt");
		exit(-1);
	}
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
main()
{
	key_t key = ftok("b.dat",1); //1. 读入端用 ftok 函数获得 key
	if(key == -1)
	{
		perror("ftok");
		exit(-1);
	}
	int shmid = shmget(key,1024,IPC_CREAT); //2. 读入端用 shmget 函数打开共享内存段
	if(shmid == -1)
	{
		perror("shmget");
		exit(-1);
	}
	char *pMap = (char *)shmat(shmid, NULL, 0); //3. 获得共享内存段的首地址
	printf("receive the data:%s\n",pMap); //4. 读取共享内存段中的内容
	if(shmctl(shmid, IPC_RMID, 0) == -1) //5. 删除共享内存段
	{
		perror("shmctl");
		exit(-1);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值