0%

进程通信-共享内存

阅读更多

1 共享内存(shared memory)

1.1 什么是共享内存

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

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

1.2 为什么共享内存速度最快

对比其它的进程间通信机制,都是进程1将数据写到公共资源上,进程2去读,这里面就包含了两次拷贝,第一次将数据从用户空间拷贝到内核,第二次将数据从内核拷贝到用户空间。再来看共享内存机制:进程1将数据写到共享内存,然后无需任何拷贝,进程2可以直接访问到这部分数据,所以就节省了两次拷贝的时间

2 共享内存的使用

2.1 创建和获取共享内存

1
2
3
4
#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);
  1. 第二个参数size:指定共享内存大小,最好设置为4096的整数倍,因为操作系统以页为基本单位来分配共享内存

2.2 销毁

1
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  1. 第二个参数cmd:传IPC_RMID即可
  2. 第三个参数buf:传入NULL即可

2.3 挂接与去挂接

1
2
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
  1. shmaddr可以设为你想映射的虚拟地址,但一般不自己指定,因为你不知道哪里合适,所以设为NULL即可。shmflg缺省为0

3 C源码

comm.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#pragma once  

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

#define _PATH_NAME_ "/tmp"
#define _PROJ_ID_ 0x666

int create_shm(int size);
int get_shm();
int destroy_shm(int shm_id);
void * at(int shm_id);
int dt(void *addr);

comm.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include "comm.h"  
#include <stdio.h>

static int comm_shm(int size, int flag)
{
key_t key = ftok(_PATH_NAME_, _PROJ_ID_);
if (key < 0)
{
perror("ftok");
return -2;
}

return shmget(key, size, flag);
}

int create_shm(int size)
{
int flag = IPC_CREAT | IPC_EXCL | 0644;
return comm_shm(size, flag);
}

int get_shm()
{
int flag = IPC_CREAT;
return comm_shm(0, flag);
}

int destroy_shm(int shm_id)
{
return shmctl(shm_id, IPC_RMID, NULL);
}

void * at(int shm_id)
{
return shmat(shm_id, NULL, 0);
}

int dt(void *addr)
{
return shmdt(addr);
}

server.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>  
#include <unistd.h>
#include "comm.h"

int main()
{
int shm_id = create_shm(4096);
char *addr = (char *)at(shm_id);

int i = 0;
while (1)
{
addr[i] = 'A';
addr[i + 1] = 0;
++i;
i %= 4096;
sleep(1);
}

dt(addr);
destroy_shm(shm_id);

return 0;
}

client.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>  
#include <unistd.h>
#include "comm.h"

int main()
{
int shm_id = get_shm();
char *addr = (char *)at(shm_id);

int i = 0;
while (1)
{
printf("%s\n", addr);
sleep(1);
}

dt(addr);

return 0;
}

4 参考