共享内存及其用mmap实现共享内存

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Comedly/article/details/52082040


一、什么是共享内存

顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

 

特别提醒:

共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,如信号量。

 

二、共享内存的使用,在Linux中提供了一组函数接口用于使用共享内存。

1、共享内存的创建函数

函数原型int shmget(key_t key,size_t size,int shmflg);

 

参数

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

 

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


(2)Size:以字节为单位指定需要共享的内存容量。

(3)Shmflg:权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。

 

2、启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。

函数原型:void *shmat(int shm_id,const void *shm_addr,int shmflg)

 

参数;

(1)shm_id :由shmget函数返回的共享内存标识,

(2)shm_addr:指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统选择共享内存的地址,

(3)shm_flg:标志位,通常为0。

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

 

3、用于将共享内存从当前进程中分离。

函数原型:int shmdt(const void *shmaddr);

参数:为shmat函数返回的地址空间,调用成功返回0,失败时返回-1.

 

4、控制共享内存函数

函数原型:int shmctl(int shm_id,int command,struct shmid_ds *buf)

 

参数

(1)shm_id:共享内存标识符

(2)Command:采取的操作:

IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。

IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值

IPC_RMID:删除共享内存段

 

(3)buf:结构指针,指向共享内存模式和访问权限的结构。

 shmid_ds结构如下:

wKioL1eXkPrC-X3KAABrM43-su4856.png-wh_50

测试代码:

    shm.h

wKiom1eXkPrwnwlpAAA8iyABe6I062.png-wh_50

    shm.c

wKioL1eXkPqh5ZafAABX5YXYabk427.png-wh_50

wKioL1eXkPuh3xIsAAAoS0PXhUc131.png-wh_50

    test.c

wKiom1eXkPvCh9t6AABCZ6xTKdY190.png-wh_50

wKioL1eXkPvj80qvAAApIqyfG-k977.png-wh_50


共享内存mmap

1、特点:

1进程相关的

(2)与XSI共享内存一样,需要与同步原语一起使用

(3)只能是有共同祖先的进程才能使用

 

2、系统调用mmap()用于共享内存的方式:

(1)使用普通文件提供的内存映像:

适用于任何进程之间。此时,需要打开或创建一个文件,然后再调用mmap()

典型调用代码如下:

fd=open(name, flag, mode); if(fd<0) ...

ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0);

(2)使用特殊文件提供匿名内存映像:

适用于具有亲缘关系的进程之间:

适用于具有亲缘关系的进程之间。由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用fork()。那么在调用fork()之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。一般来说,子进程单独维护从父进程继承下来的一些变量。而mmap()返回的地址,却由父子进程共同维护。对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式

 

函数原型:void *mmap(void *addr,size_t len,int prot,int flag,int fd,off_t offset)

 

参数

1、addr:映射区的开始地址,设置为0时表示系统决定映射区的起始地址

2、len:映射区的长度,单位为字节

3、prot:期望的内存保护标志,取一下几个值:

PORT_EXEC:页内容可以被执行  PROT_READ:页内容可被读

PROT_WRITE:页内容可被写     PROT_NONE:页内容不可访问

4、fd:文件描述符。

5、offset:被映射对象内容的起点。

6、Flags(必须要有MAP_CHARED标志):指定映射对象的类型,映射选项是否可以和映射页共存。

MAP_SHARED:与其他所有映射这个对象的进程共享映射空间;

MAP_PRIVATE:建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。

MAP_FIXED:使用指定的映射起始地址

测试代码:

mmap.c

wKiom1eXkgbg41VBAABNRS3WFWQ495.png-wh_50

wKiom1eXkgayKSpfAAAqc3yxplE056.png-wh_50

wKioL1eXkgahAa1vAAAPoLtcDtg055.png-wh_50

运行结果:

wKioL1eXkgXBl5-8AAAGKGUVwY4592.png-wh_50


猜你喜欢

转载自blog.csdn.net/Comedly/article/details/52082040
今日推荐