Linux编程基础之进程间通信之四:共享内存
一、共享内存的概述
共享内存是一种最为高效的进程间通信方式。因为进程可以直接读写内存,不需要任何数据的复制。为了在多个进程间交换信息,内核专门留出了一块内存区。这段内存区可以由需要访问的进程将其映射到自己的私有地址空间。因此,进程就可以直接读写这一内存区而不需要进行数据的复制,从而大大提高了效率。其原理基本示意图如下所示:
二、共享内存的一般使用方法和基本原理
1、一般使用方法
共享内存的实现分为两个步骤,第一步是创建共享内存,这里用到的函数是shmget(),也就是从内存中获得一段共享内存区域,第二步映射共享内存,也就是把这段创建的共享内存映射到具体的进程空间中,这里使用的函数是shmat()。到这里,就可以使用这段共享内存了,也就是可以使用不带缓冲的I/O 读写命令对其进行操作。除此之外,当然还有撤销映射的操作,其函数为shmdt()。在共享内存使用完毕之后调用shmctl()函数将创建的共享内存销毁。
2、主要函数
a、创建或者获得一个共享内存
int shmget(key_t key, int size, int shmflg)
key : 共享内存的键值,多个进程可以通过它访问同一个共享内存,其中有个特殊值IPC_PRIVATE。它用于创建当前进程的私有共享内存
size : 共享内存区大小
shmflg : 同open()函数的权限位,也可以用八进制表示法
返回值:成功返回共享内存段标识符,失败返回-1
b、映射共享内存
char *shmat(int shmid, const void *shmaddr, int shmflg)
shmid : 要映射的共享内存区标识符
shmaddr : 将共享内存映射到指定地址(若为0 则表示系统自动分配地址并把该段共享内存映射到调用进程的地址空间)
shmflg : SHM_RDONLY:共享内存只读;默认0:共享内存可读写
返回值:成功返回被映射的段地址,失败返回-1
c、撤销映射共享内存
int shmdt(const void *shmaddr)
shmaddr : 被映射的共享内存段地址
返回值:成功返回0,失败返回-1
d、共享内存控制函数
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid :要映射的共享内存区标识符
cmd : 控制共享内存的命令,比如IPC_RMID,销毁一个共享内存
buf : argument is a pointer to a shmid_ds structure
返回值:返回0成功,返回-1失败
三、测试
编写一个测试共享内存的例子:
shmem_write.c : 定义一块共享内存,向共享内存中写入一个消息
shmem_read.c : 将共享内存中的消息打印出来
shmem_write.c的具体实现如下:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
/* 定义共享内存的大小 */
#define SHMEM_SIZE 256
/* 向共享内存中写入一个消息,消息由用户传入
* usage : shmem_write <msg>
*/
int main(int argc, char *argv[])
{
key_t key;
int shmid;
char *buffer;
if(2 != argc)
{
printf("usage : %s <msg>\n", argv[0]);
return -1;
}
/* 构造一个共享内存的键值 */
key = ftok("key_file", 0x3);
if(-1 == key)
{
printf("ftok error!\n");
return -1;
}
/* 创建一块共享内存 */
shmid = shmget(key, SHMEM_SIZE, IPC_CREAT | 0666);
if(-1 == shmid)
{
printf("shmget error!\n");
return -1;
}
/* 将共享内存映射到当前进程空间 */
buffer = (unsigned char*)shmat(shmid, 0, 0);
if(buffer == (void *)-1)
{
printf("shmat error!\n");
return -1;
}
/* 将传入的内如写入到共享内存当中 */
strncpy(buffer, argv[1], strlen(argv[1]));
/* 撤销共享内存在当前进程的映射 */
shmdt(buffer);
return 0;
}
shmem_read.c的具体实现如下:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
/* 定义共享内存的大小 */
#define SHMEM_SIZE 256
/* 读出共享内存当中的内容
*
*/
int main(void)
{
key_t key;
int shmid;
int ret;
char *buffer;
/* 构造访问共享内存的键值 */
key = ftok("key_file", 0x03);
if(-1 == key)
{
printf("ftok error!\n");
return -1;
}
/* 创建一块共享内存 */
shmid = shmget(key, SHMEM_SIZE, IPC_CREAT | 0666);
if(-1 == shmid)
{
printf("shmget error!\n");
return -1;
}
/* 将共享内存映射到当前进程空间 */
buffer = (unsigned char*)shmat(shmid, 0, 0);
if(buffer == (void *)-1)
{
printf("shmat error!\n");
return -1;
}
/* 将共享内存中的内容读取出来 */
printf("The msg is %s\n", buffer);
/* 撤销共享内存在当前进程的映射 */
shmdt(buffer);
/* 删除这块共享内存 */
ret = shmctl(shmid, IPC_RMID, NULL);
if(-1 == ret)
{
printf("shmctl error!\n");
return -1;
}
return 0;
}
编译并运行结果如下所示: