概念
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。
特点
共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
因为多个进程可以同时操作,所以需要进行同步。
信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
原理
创建
1、创建共享内存
2、进程A连接共享内存,写入数据(这里需要给进程A一个睡眠时间:两个进程同时操作需要同步,进程A写入数据后睡眠一定时间,在这个时间内进程B将数据读取,实现数据交换)
3、进程A断开连接
4、进程B连接共享内存,读取数据
5、进程B断开连接
6、释放公共内存
常用API
头文件
#include <sys/types.h>
#include <sys/shm.h>
//以下几个API都包含以上两个头文件
shmget函数
功能
创建或获取一个共享内存
函数原型
int shmget(key_t key, size_t size, int shmflg);
参数解读
key | 由ftok生成的key标识,标识系统的唯一IPC资源 |
size | 需要申请共享内存的大小。在操作系统中,申请内存的最小单位为页,一页是4k字节,为了避免内存碎片,我们一般申请的内存大小为页的整数倍,即以兆为单位 |
shmflg | 如果要创建新的共享内存,需要使用IPC_CREAT,IPC_EXCL,后面需要加权限标志,权限标志与文件的读取操作一样。如果是已经存在的,可以使用IPC_CREAT或直接传0(只需获取而不用创建,yi) |
返回值
成功时返回一个新建或已经存在的的共享内存标识符,取决于shmflg的参数。失败返回-1并设置错误码。
shmat函数
功能
第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间,即将共享内存映射进进程中。
函数原型
void *shmat(int shm_id, const void *shm_addr, int shmflg);
参数解读
shm_id | 由shmget函数返回的共享内存标识 |
*shm_addr | 指定共享内存连接到当前进程中的地址位置,通常为空(为0),表示让系统为我们安排共享内存的地址 |
shmflg | 若指定了SHM RDONLY位,则以只读方式连接此段,否则以读写方式连接此段(输入0即可,表示映射进的共享内存可读可写) |
返回值
成功返回共享存段的指针(虚拟地址),并且内核将使其与该共享存段相关的shmid_ds(第四个函数的第三个参数)结构中的shm_nattch计数器加1 (类似于引用计数,即内存占用计入总内存) ; 出错返回-1。
shmdt函数
功能
当一个进程不需要共享内存的时候,就需要去关联。该函数并不删除所指定的共享内存区,而是将之前用shmat函数连接好的共享内存区脱离目前的进程。
函数原型
int shmdt(const void *shmaddr);
参数解读
*shmaddr:是shmat函数返回的地址指针,只需将其返回值的函数变量名(代码地址)写入即可
返回值
调用成功时返回0,失败时返回-1。
shmctl函数
功能
控制共享内存
函数原型
int shmctl(int shm_id, int command, struct shmid_ds *buf);
参数解读
shm_id | shmget函数返回的共享内存标识符 | ||||||
command | command是要采取的操作,它可以取下面的三个值
| ||||||
*buf | buf是一个结构指针,它指向共享内存模式和访问权限的结构(不关心,一般写0) |
返回值
成功时返回0,失败返回-1并设置错误码。
代码示例
shmw.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <string.h>
int main()
{
key_t key;
int shmid;
char *shmaddr = NULL;
key = ftok(".",1);//当前目录建立IPC
shmid = shmget(key,1024*4,IPC_CREAT|0666);//以可读可写方式开辟一个4兆大小的共享内存
if(shmid == -1)
{
printf("create gxdl failed\n");
exit(-1);
}
shmaddr = shmat(shmid,0,0);//将共享内存映射到进程中
printf("connect success!\n");
strcpy(shmaddr,"hello word!");
sleep(5);//因为写入和读取是同步,所以此时执行完写入代码后需等待读取数据后一起关闭共享内存
shmdt(shmaddr);//断开连接共享内存
shmctl(shmid,IPC_RMID,0);//删除共享内存
printf("over\n");
return 0;
}
shmr.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <string.h>
int main()
{
key_t key;
int shmid;
char *shmaddr = NULL;
key = ftok(".",1);
shmid = shmget(key,1024*4,0);//这里只需找到已经开辟的共享内存,所以参数为0即可
if(shmid == -1)
{
printf("create gxdl failed\n");
exit(-1);
}
shmaddr = shmat(shmid,0,0);//映射共享内存
printf("connect success!\n");
printf("content is %s\n",shmaddr);//将共享内存数据打印出来
shmdt(shmaddr);//断开共享内存
printf("over\n");
return 0;
}
shmw.c程序运行,写入端往共享内存写入数据,并让其等待5s,这5s内shmr.c程序运行,读取端读取共享内存数据并将内容打印出来,5s后两者同时关闭共享内存输出over。
fork函数的补充
功能
系统IPC键值的格式转换函数,系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
函数原型
key_t ftok( const char * fname, int id );
参数解读
fname | 就是你指定的文件名(已经存在的文件名),一般使用当前目录 |
id | 子序号。虽然是int类型,但是只使用8bits(1-255) |
例如
key_t key;
key = ftok(".", 1); //当前文件只需加.