共享内存
- 共享内存区是最快的IPC形式。⼀旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执⾏进入内核的系统调⽤来传递彼此的数据。
- 先简单介绍一下,也有把共享内存叫做共享存储,共享存储允许两个或者多个进程共享一个给定的存储区。因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的一种 IPC。使用共享存储时要掌握的诀窍就是,在多个进程之间同步访问一个给定的存储区。若服务器进程正在将数据放入共享存储区,则在他做完这一操作之前,客户进程不应该去取这些数据。
- 共享存储就是在多个进程将同一个文件映射到它们的地址空间。共享存储和内存映射的文件的不同之处在于,共享存储没有相关的文件。共享存储段是内存的匿名段。
共享内存函数
- shmget 函数
- 功能:用来创建共享内存
- 原型
int shmget(key_t key, size_t size, int shmflg);
- 参数
- key:这个共享内存段名字,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget()函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.
- 不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某个资源,程序对所有共享内存的访问都是间接的,程序先通过调用shmget()函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget()函数的返回值),只有shmget()函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。
- size:共享内存以字节为单位指定需要共享的内存容量。
- 这个大小以字节为单位。实现通常将其向上取为系统页长的整数倍,如果应用指定的size值并非系统页长的整数倍,那么最后一页的余下部分是不可使用的。
- 如果创建一个新段(通常在服务器进程中),则必须指定其size。如果正在引用一个现存的段(一个客户进程),则将size指定为0,。当创建一个新段是,段内的内容初始化为0。
- shmflg:shmflg是权限标志,由九个权限标志构成,它们的用法和创建文件时使用mode模式标志是一样的。它的作用也与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。
- 返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回 -1
shmat 函数
- 功能:将共享内存段连接到进程地址空间
- 原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
- 参数
- shmid : 共享内存标识
- shmaddr : 指定连接的地址
- shmflg : 它的两个可能取值是 SHM_RND和SHM_RDONLY
- 返回值:成功返回⼀个指针,指向共享内存第⼀个字节;失败返回 -1。如果shmat成功执行,那么内核将使与该共享存储段相关的shmid_ds结构中的shm_nattch计数器值加1。
- 注意:
- shmaddr 为NULL,则此段连接到由内核选择的一个可用地址上,则是推荐的使用方式。
- shmaddr不为 NULL 且 shmflg ⽆ SHM_RND 标记,则以shmaddr为连接地址。
- shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会⾃动向下调整为SHMLBA的整数倍。
- 公式:shmaddr - (shmaddr % SHMLBA)
- 除非只计划在一种硬件上运行应用程序(这在当今是不大可能的),否则不应指定共享内存段所连接到地址。而是应当指定shmaddr为NULL,以便由系统来选择地址。
- shmflg=SHM_RDONLY,表示连接操作用来只读共享内存,否则以读写方式连接此段。
- 当对共享内存的操作结束时,则调用shmdt 与该段分离。注意:这不从系统中删除其标识符以及相关的数据结构。该标识符依然存在,直至某个进程(一般是服务器进程)带IPC_RMID命令的调用shmctl 特地删除它为止。
shmdt 函数
- 功能:将共享内存段与当前进程脱离
- 原型
int shmdt(const void *shmaddr);
- 参数
- 返回值:成功返回0;失败返回-1
- 注意:将共享内存段与当前进程脱离不等于删除共享内存段
shmctl 函数
- 功能:⽤用于控制共享内存
- 原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- 参数
- shmid:由shmget返回的共享内存标识码
- cmd:将要采取的动作(有三个可取值)
- IPC_STAT:取此段的shmid_ds结构,并将它存储在由buf指向的结构中。
- IPC_SET:按buf指向的结构体中的值设置与此共享存储段相关的shmid_ds结构中的字段。
- IPC_RMID:从系统中删除该共享内存
命令 | 说明 |
---|
IPC_STAT | 把shmid_ds结构中的数据设置为共享内存的当前关联值 |
IPC_SET | 在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值 |
IPC_RMID | 删除共享内存段 |
- buf:指向⼀个保存着共享内存的模式状态和访问权限的数据结构
- 返回值:成功返回0;失败返回-1
来看一个共享内存的代码
1 #include<unistd.h>
2 #include<stdio.h>
3 #include<stdlib.h>
4 #include<sys/shm.h>
5
6 #define IPC_KEY 0x1234567
7
8 int main()
9 {
10 int shmid1 = 0;
11 shmid1 = shmget(IPC_KEY,32, IPC_CREAT | 0664);
12 if(shmid1 < 0)
13 {
14 perror("shmget error\n");
15 return -1;
16 }
17 void *shm_start = shmat(shmid1,NULL,0);
18 if(shm_start == (void*)-1)
19 {
20 perror("shmat error");
21 return -1;
22 }
23
24 int i = 1;
25 while(1)
26 {
27 sprintf(shm_start,"%s-----%d\n","hello",i++);
28 sleep(1);
29 }
30
31
32 return -1;
33 }
1 #include<unistd.h>
2 #include<stdio.h>
3 #include<stdlib.h>
4 #include<sys/shm.h>
5
6 #define IPC_KEY 0x1234567
7
8 int main()
9 {
10 int shmid = -1;
11 shmid = shmget(IPC_KEY,32, IPC_CREAT|0664);
12 if(shmid < 0)
13 {
14 perror("shmget error\n");
15 return -1;
16 }
17 void *shm_start = shmat(shmid,NULL,0);
18 if(shm_start == (void*)-1)
19 {
20 perror("shmat error");
21 return -1;
22 }
23 while(1)
24 {
25 printf("不断的问自己:%s",shm_start);
26 sleep(1);
27 }
28 }
![在这里插入图片描述](https://img-blog.csdnimg.cn/20181228170448961.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNDIxOTE5,size_16,color_FFFFFF,t_70)
在来看一个共享内存的相关代码
comm.h
1 #ifndef _COMM_H_
2
3 #define _COMM_H_
4
5 #include <stdio.h>
6 #include <sys/types.h>
7 #include <sys/ipc.h>
8 #include <sys/shm.h>
9 #define PATHNAME "."
10 #define PROJ_ID 0x6666
11 #define KEY 0x1234567
12
13 int createShm(int size);
14 int destroyShm(int shmid);
15 int getShm(int size);
16
17 #endif
comm.c
1 #include "comm.h"
2 static int commShm(int size, int flags)
3 {
4 key_t _key = ftok(PATHNAME, PROJ_ID);
5 if(_key < 0){
6 perror("ftok");
7 return -1;
8 }
9 int shmid = 0;
10 if( (shmid = shmget(KEY, size, flags)) < 0){
11 perror("shmget");
12 return -2;
13 }
14 return shmid;
15 }
16 int destroyShm(int shmid)
17 {
18 if(shmctl(shmid, IPC_RMID, NULL) < 0){
19 perror("shmctl");
20 return -1;
21 }
22 return 0;
23 }
24
25 int createShm(int size)
26 {
27 return commShm(size, IPC_CREAT|IPC_EXCL|0666);
28 }
29
30
31 int getShm(int size)
32 {
33 return commShm(size, IPC_CREAT);
34 }
server.c
1 #include "comm.h"
2
3 int main()
4 {
5 int shmid = createShm(4096);
6 char *addr = shmat(shmid, NULL, 0);
7 sleep(2);
8 int i = 0;
9 while(i < 26)
10 {
11 printf("client# %s\n", addr);
12 sleep(1);
13 i++;
14 }
15
16 shmdt(addr);
17 sleep(2);
18 destroyShm(shmid);
19 return 0;
20 }
client.c
1 #include "comm.h"
2 #include<string.h>
3
4 int main()
5 {
6 int shmid = getShm(4096);
7 if(shmid <= 0)
8 {
9 perror("creat error!\n");
10 return -1;
11 }
12 sleep(1);
13 char *addr = shmat(shmid, NULL, SHM_RND);
14 if(addr == NULL)
15 {
16 perror("connet error!");
17 return -1;
18 }
19 sleep(2);
20 int i = 0;
21 while(i<26){
22 addr[i] = 'A'+i;
23 i++;
24 addr[i] = 0;
25 sleep(1);
26 }
27 shmdt(addr);
28 sleep(2);
29 return 0;
30 }
![在这里插入图片描述](https://img-blog.csdnimg.cn/2018123013541986.png)