共享内存(shared memory)
共享内存是进程间共享数据的一种最快的方法,一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
共享内存允许两个或者多个进程共享给定的存储区域。
使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当 去读、写这些数据,否则会造成共享内存中数据的混乱。
在使用共享内存进行通信时,要注意进程间的同步,控制同步的问题需要使用信号量。
在Linux操作系统中共享内存限制值如下:
- 共享存储段的最大字节数:33554432
- 共享存储段的最小字节数:1
- 系统中共享存储段的最大段数:4096
- 每个进程共享存储段的最大段数:4096
每个共享内存的对象都有其制定的定义类型,该结构体为 shmid-ds ,定义形式如下:
struct shmid_ds
{
struct ipc_perm shm_perm; /* 共享内存的 ipc_perm 结构对象 */
int shm_segsz; /* 共享内存区域字节大小 */
ushort shm_lkcnt; /* 共享内存区域被锁定的时间*/
pid_t shm_cpid; /* 创建该共享内存的进程ID */
pid_t shm_lpid; /* 最近一次调用shmop()函数的进程ID */
ulong shm_nattch; /* 使用该共享内存的进程数 */
time_t shm_atime; /* 最近一次附加操作的时间 */
time_t shm_dtime; /* 最近一次分离操作的时间 */
time_t shm_ctime; /* 最近一次改变的时间 */
};
共享内存的操作函数
1.shmget() 函数
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size,int shmflg);
功能:
创建或打开一块共享内存区域,如果已经存在一块共享内存区域,那么,该函数可以打开这个已存在的共享内存
参数:
- key:IPC键值
- size:该共享存储段的长度(字节)
- shmflg:用来标识函数的行为
IPC_CREAT:如果不存在就创建
IPC_EXCL:如果已经存在则返回失败
IPC_NOWAIT:调用进程会立即返回。若发生错误 则返回-1
SHM_R:可读
SHM_W:可写
返回值:
成功:返回共享内存标识符
失败:返回-1
使用shell命令操作共享内存:
- 查看共享内存 ipcs -m
- 删除共享内存 ipcrm -m shmid
2.shmat() 函数
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能: 将共享内存区域附加到指定进程的地址空间中
参数:
- shmid:共享内存标识符。
- shmaddr:共享内存映射地址(若为NULL则由系统自动指定),推荐使用NULL
- shmflg:共享内存段的访问权限和映射条件
0:共享内存具有可读可写权限
SHM_RDONLY:只读
SHM_RND:(shmaddr非空时才有效)
没有指定SHM_RND则此段连接到shmaddr所指 定的地址上(shmaddr必需页对齐);
指定了SHM_RND则此段连接到shmaddrshmaddr%SHMLAB 所表示的地址上。
返回值:
成功:返回共享内存段首地址
失败:返回 -1
注:
shmat函数使用的时候第二个和第三个参数一般设为NULL和0,即系统自动指定共享内存地址,并且共享内存可读可写。
3.shmdt() 函数
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:
将共享内存和当前进程分离(仅仅是断开联系并不删除共享内存)
参数:
- shmaddr:共享内存映射地址
返回值: 成功返回 0,失败返回 -1
4.shmctl() 函数
#include <sys/ipc.h>
#include <sys/shm.h>
int shmshmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:共享内存空间的控制。
参数:
- shmid:共享内存标识符
- cmd:函数功能的控制
IPC_RMID:删除;
IPC_SET:设置shmid_ds参数;
IPC_STAT:保存shmid_ds参数;
SHM_LOCK:锁定共享内存段(超级用户);
SHM_UNLOCK:解锁共享内存段. - buf:shmid_ds数据类型的地址,用来存放或更 改消息队列的属性
返回值:成功返回 0,失败返回 -1。
代码
shared_memory_write.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSZ 2048
int main(int argc, char *argv[])
{
int shmid;
int ret;
key_t key;
char *shmadd;
key = ftok(".", 2012);
if(key == -1)
{
perror("ftok");
}
/*创建共享内存*/
shmid = shmget(key, BUFSZ, SHM_R|SHM_W|IPC_CREAT);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
/*映射*/
shmadd = shmat(shmid, NULL, 0);
if(shmadd < 0)
{
perror("shmat");
_exit(-1);
}
/*拷贝数据至共享内存区*/
printf("copy data to shared-memory\n");
bzero(shmadd, BUFSZ);
strcpy(shmadd, "data in shared memory\n");
return 0;
}
shared_memory_read.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSZ 2048
int main(int argc, char *argv[])
{
int shmid;
int ret;
key_t key;
char *shmadd;
key = ftok(".", 2012);
if(key==-1)
{
perror("ftok");
}
system("ipcs -m");
/*打开共享内存*/
shmid = shmget(key, BUFSZ, SHM_R|SHM_W);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
/*映射*/
shmadd = shmat(shmid, NULL, 0);
if(shmadd < 0)
{
perror("shmat");
exit(-1);
}
/*读共享内存区数据*/
printf("copy data from shared-memory\n");
printf("data = [%s]\n", shmadd);
/*分离共享内存和当前进程*/
ret = shmdt(shmadd);
if(ret < 0)
{
perror("shmdt");
exit(1);
}
else
{
printf("deleted shared-memory\n");
}
/*删除共享内存*/
shmctl(shmid, IPC_RMID, NULL);
system("ipcs -m");
return 0;
}