共享内存是三个IPC机制中的一个。它允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在进行的进程之间传递数据的一种非常有效的方式。

大多数的共享内存的实现,都把由不同进程之间共享的内存安排为同一段物理内存.

首先我们都知道我们执行的每一个程序,它看到的内存其实都是虚拟内存,虚拟内存需要进行页表的映射将进程地址映射到物理内存,具体处理大致如下面的图

共享内存特点和优势

当中共享内存的大致原理相信我们可以看明白了,就是让两个进程地址通过页表映射到同一片物理地址以便于通信,

你可以给一个区域里面写入数据,理所当然你就可以从中拿取数据,这也就构成了进程间的双向通信,而且共享内存

是IPC通信当中传输速度最快的通信方式没有之一,理由很简单,客户进程和服务进程传递的数据直接从内存里存取、放入,

数据不需要在两进程间复制,没有什么操作比这简单了。再者用共享内存进行数据通信,它对数据也没啥限制。

最后就是共享内存的生命周期随内核。

即所有访问共享内存区域对象的进程都已经正常结束,共享内存区域对象仍然在内核中存在(除非显式删除共享内存

区域对象),在内核重新引导之前,对该共享内存区域对象的任何改写操作都将一直保留;简单地说,共享内存区域对象

的生命周期跟系统内核的生命周期是一致的,而且共享内存区域对象的作用域范围就是在整个系统内核的生命周期之内。

但是,共享内存也并不完美,共享内存并未提供同步机制,也就是说,在一个服务进程结束对共享内存的写操作

之前,并没有自动机制可以阻止另一个进程(客户进程)开始对它进行读取。这明显还达不到我们想要的,我们不单

是在两进程间交互数据,还想实现多个进程对共享内存的同步访问,这也正是使用共享内存的窍门所在。基于此,

我们通常会用平时常谈到和用到 信号量来实现对共享内存同步访问控制。

1.创建共享内存shmget

原型:int shmget(key_t key, size_t size, intshmflg)

返回值: 创建成功,则返回一个非负整数,即共享内存标识;

如果失败,则返回-1.

key:    //程序需要提供一个参数key,它为共享内存段提供一个外部名。(每个IPC对象都与一个键 即key相关联,然后此键再由内核变换为标识符)。还有一个特殊的键值IPC_PRIVATE, 它用于创建一个只属于该创建进程的新共享内存,通常不会用到;

size:   //以字节为单位指定需要共享的内存容量。

shmflag: //包含9个比特的权限标志,它们的作用与创建文件时使用的mode标志是一样。由IPC_CREAT定义的一个特殊比特位,同时必须和权限标志按位或才能创建一个新的共享内存段。

(注意:若想创建的新IPC结构没有引用具有同一标识符的现有的IPC结构,就要同时指定IPC_CREAT 和 IPC_EXCL;共享内存属IPC中一种,它同样如此)

权限标志对共享内存非常有用,因为它允许一个进程创建的共享内存可以被共享内存的创建者所拥有的进程写入,同时其它用户创建的进程只能读取共享内存。我们可以利用这个功能来提供一种有效的对数据进行只读访问的方法,通过将数据放共享内存并设置它的权限,就可以避免数据被其他用户修改。

2.将共享内存端挂载到自己地址空间shmat

第一次创建共享内存段时,它不能被任何进程访问。要想启动对该内存的访问,必须将其连接到一个进程的地址空间

该函数原型:void *shmat(int shmid, const void *shmaddr, intshmflg)

返回值:调用成功返回挂载的虚拟地址空间起始地址,失败返回NULL

int shmid           //是由shmget函数返回的共享内存标识。

const void *shmaddr  //指定共享内存连接到当前进程中的地址位置,通常为0,表示让系统来选择          共享内存的地址。

int shmflg     //是一组标志位,通常为0。它还可取:SHM_RND,用以决定是否将当前共享内存段连接到指定的shmaddr上。该参数和shm_addr联合使用,用来控制共享内存连接的地址,除非只计划在一种硬件上运行应用程序,否则不要这样指定。填0让操作系统自己选择是更好的方式。

SHM_RDONLY单独使用则是指让它使连接的内存段只读,否则以读写方式连接此内存段

3. 与共享内存段分离 shmdt

原型:int shmdt(const void *shmaddr)

shm_addr: shmat返回的地址指针。

成功时,返回0,

失败时,返回-1.

NOTE:

仅仅是共享内存分离但并未删除它,其标识符及其相关数据结构都在;直到某个进程的IPC_RMID命令的调用shmctl特地删除它为止

只是使得该共享内存对当前进程不再可用。

4.shmctl 共享内存控制函数

#include #include原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf)

shm_id : 是shmget返回的共享内存标识符。

cmd: 它可以取3个值:

IPC_STAT  把shmid_ds结构中的数据设置为共享内存的当前关联值

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

IPC_RMID  删除共享内存段

buf:是一个指针,包含共享内存模式和访问权限的结构。

buf指向的shmid_ds结构体 一定要包含下列一些参数:

structshmid_ds {

uid_t shm_perm.uid;

uid_t shm_perm.gid;

mode_t shm_perm.mode;

简单用共享内存来再两进程间交换数据,比如交换一个结构体

代码如下:

1 #include

2 #include

3 #include

4 #include

5 #include

6 #include

7 typedef structStu8 {9 intage;10 char name[10];11 }Stu;12

13 int main( void)14 {15 Stu s;16 strcpy(s.name, "jack");17 //创建共享内存段

18 int id = shmget(1234, 8, IPC_CREAT|0644);19 if( id == -1)perror( "shmget"),exit( 1);20 //挂载到进程的地址空间

21 Stu* p = ( Stu*)shmat( id, NULL, 0);22

23 int i =0;24 while( 1)25 {26 s.age = i++;27 memcpy(p, &s, sizeof(Stu)); //写到共享段中

28 sleep( 2);29 }30 return 0;31 }

write.c

1 #include

2 #include

3 #include

4 #include

5 #include

6 typedef structStu7 {8 intage;9 char name[10];10 }Stu;11 int main( void)12 {13 int id = shmget(1234, 8, 0);14 if( id == -1)perror( "shmget"),exit( 1);15

16 Stu* p = ( Stu*)shmat( id, NULL, 0);17 while( 1)18 {19 printf("age= %d, name= %s\n", p->age, p->name);20 sleep(2);21 }22 return 0;23 }

read.c

执行结果如下:

优点:我们可以看到使用共享内存进行进程间的通信真的是方便而高效,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。

缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进

行进程间的同步工作。

上面只是共享内存的一些简单的应用,当多个进程对共享内存进行访问时,并没有保证同步,所以我们还需要用其它的机制来实现它的同步机制,要解决此,通常会用到信号量(PV操作)来实现。但要基于此的实现,前提还需要熟悉信号量的操作以及这里的共享内存使用。要用共享内存模拟做出一个带同步机制"先进先出"的消息通道,对我等萌新并不太容易,所以还得放到以后再实现了。。

对UNIX系统来说, 共享内存 分为一般 共享内存 和映像文件 共享内存 两种,对windows实际上只有映像文件 共享内存 一种。所以 java 应用中也是只能创建映像文件 共享内存 。使用 共享内存 ,有如下几个特点: 1、可以被多个进程打开访问。 2、读写操作的进程在执行读写操作时其他进程不能进行写操作。 3、多个进程可以交替对某一 共享内存 执行写操作。 4、一个进程执行了内存的写操作后,不影响其... 操作系统内的并发执行进程可以是独立的也可以是协作的: 如果一个进程不能影响其他进程或受其他进程影响,那么该进程是独立的,换句话说,不与任何其他进程共享数据的进程是独立的; 如果一个进程能影响其他进程或受其他进程所影响,那么该进程是协作的。换句话说,与其他进程共享数据的进程为协作进程。提供环境允许进程协作,具有许多理由: 信息共享:由于多个用户可能对同样的信息感兴趣(例如共享文件),所以应提供环境以允许并发访问这些信息。 JAVA 进程间通信 的方法主要有以下几种:   (1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。   (2)命名管道(named pipe):命名管道克服了管道没有名字的限制,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。   (3)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于 进程间通信 外,进程还可以发送 信号给进程本身。   (4)消息(Message)队列:消息队列是消息的链接表,包括 ipc -两个单独的 Java 桌面应用程序之间的通信我正在寻找开发两个单独的(但相关的) Java 桌面应用程序。我希望一个应用程序能够触发另一个应用程序,传递可以随后进行编辑和传回的数据,即通信将是两种方式。 如果其他应用程序已经在运行,我希望他们之间进行交流,即我不想仅通过命令行传递参数,等等。一般来说,为了达到这个目的,我应该考虑哪些策略/技术?William asked 2020-07-08T18... 1 共享内存 对应应用开发的意义对熟知UNIX系统应用开发的程序员来说, IPC (InterProcessCommunication)机制是非常熟悉的, IPC 基本包括 共享内存 、信号灯操作、消息队列、信号处理等部分,是开发应用中非常重要的必不可少的工具。其中 共享内存 IPC 机制的关键,对于数据共享、系统快速查询、动态配置、减少资源耗费等均有独到的优点。对应UNIX系统来说, 共享内存 分为一般 共享内存 和映... 宏观上并行围观上串行,cpu用中断控制资源,并发大大提高了程序的利用率。 进程在操作系统中都有一个户口,用于表示这个进程。这个户口操作系统被称为PCB(进程控制块),在linux中具体实现是 task_struct数据结构,它记录了一下几个类型的信息: 1.状态信息 在内核中留出一段内存区,可以由需要访问的多个进程把内存区的地址映 射到自己的进程中,可以操作这段空间,可以进行通信。进程就可以直接读写这段空间,避免拷贝,提高效率。其实这种通信的话,在进行通信的时候,一但 共享内存 的值发生改变,那么其内存空间的值也就发生改变了。· 2. 映射 共享内存 ,把 共享内存 的空间映射到进程中(以地址提供地址)2、 映射 共享内存 ,把 共享内存 的空间映射到进程中(以地址提供地址)4. 撤销 共享内存 映射。5. 删除 共享内存 对象。使用 共享内存 需要用到的函数。.3、撤销 共享内存 映射。..