共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。它是IPC对象的一种。为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。共享内存允许两个进程访问同一块内存区域,
它们使用同一个 key 值标记
。可运用Linux的系统命令 ipcs -m 查看共享内存信息。
1.1共享内存的创建
函数原型如下:
int shmget(key_t key, size_t size, int shmflg);
其中:
key:一个非零整数,两个进程需保持一致,即两个进程之间通信的钥匙;
size: 申请的共享区的大小,单位是字节。需要是内存页大小的整数倍;
shmflg:同 open 函数的 mode 参数,设置文件访问权限,这里额外多出一个 IPC_CREAT 和 IPC_EXCL,可与 mode 进行或操作。IPC_CREAT 表示共享区不存在则创建,IPC_EXCL 和 IPC_CREAT 共同使用,表示共享区已存在则返回错误。如 0644 | IPC_CREAT
返回值: 返回共享区域的 id 值,用于唯一识别该区域。
1.2共享内存映射
函数原型如下:
void *shmat(int shmid, const void *shmaddr, int shmflg);
其中:
shmid:创建时候返回的 id 值;
shmaddr:将共享内存映射到指定地址,可以为 NULL,此时系统将自动分配地址;
shmflg:同 shmget 函数中的参数,通常为 0;
返回值 : 成功执行后,返回该地址的起始地址,失败返回 -1
1.3撤销映射
函数原型如下:
int shmdt(const void *shmaddr);
其中:
shmaddr : shmat 函数返回的地址。
返回值 : 成功返回 0,失败返回 -1,errno 将被设置为相应的值。
1.4删除共享内存
函数原型如下:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
其中:
shmid : 创建时候返回的 id 值;
cmd : 控制命令;IPC_STAT 状态查询;IPC_SET 在权限允许下,将共享内存状态更新为 buf 中的数据;IPC_RMID 删除共享内存;
buf::指向一个保存着共享内存的模式状态和访问权限的数据结构。
返回值 : 成功返回 0,失败返回 -1,errno 将被设置为相应的值。
1.5缺陷
共享内存也并不完美,共享内存并未提供同步机制,也就是说,在一个服务进程结束对共享内存的写操作之前,并没有自动机制可以阻止另一个进程(客户进程)开始对它进行读取。这明显还达不到我们想要的,我们不单是在两进程间交互数据,还想实现多个进程对共享内存的同步访问,这也正是使用共享内存的窍门所在。基于此,我们通常会用平时常谈到和用到 信号量来实现对共享内存同步访问控制。
1.6共享内存样例代码
Write端:
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
void *shmaddr = NULL;
const char data[] = "Hello World\n";
int shmid;
key_t key = (key_t) 666;
long page_size = sysconf(_SC_PAGESIZE);
int data_size = (strlen(data) + page_size - 1) & (~(page_size - 1));
printf("data size: %d, page size: %ld\n", data_size, page_size);
// 1. create shared memory
shmid = shmget(key, data_size, 0644 | IPC_CREAT);
if (shmid == -1) {
perror("shmget failed\n");
exit(EXIT_FAILURE);
}
// 2. attach shared memory
shmaddr = shmat(shmid, NULL, 0);
if (shmaddr == (void *)-1) {
perror("shmat failed\n");
exit(EXIT_FAILURE);
}
// 3. write data to shared memory
memset(shmaddr, 0, data_size);
memcpy(shmaddr, &data, strlen(data));
// 4. detach shared memory
if (shmdt(shmaddr) == -1) {
perror("shmdt failed\n");
exit(EXIT_FAILURE);
}
printf("write done !\n");
return 0;
}
Read端:
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
void *shmaddr = NULL;
const char data[] = "Hello World\n";
int shmid;
key_t key = (key_t) 666;
long page_size = sysconf(_SC_PAGESIZE);
int data_size = (strlen(data) + page_size - 1) & (~(page_size - 1));
printf("data size: %d, page size: %ld\n", data_size, page_size);
// 1. create shared memory
shmid = shmget(key, data_size, 0644 | IPC_CREAT);
if (shmid == -1) {
perror("shmget failed\n");
exit(EXIT_FAILURE);
}
// 2. attach shared memory
shmaddr = shmat(shmid, NULL, 0);
if (shmaddr == (void *)-1) {
perror("shmat failed\n");
exit(EXIT_FAILURE);
}
// 3. read data to shared memory
printf("read form shead memory: %s\n", (char *)shmaddr);
// 4. detach shared memory
if (shmdt(shmaddr) == -1) {
perror("shmdt failed\n");
exit(EXIT_FAILURE);
}
// 5. delete shared memory
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl delete shared memory failed\n");
exit(EXIT_FAILURE);
}
return 0;
}