什么是共享内存
共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。共享存储允许两个或多个进程共享一个给定的存储区。因为数据不需要在客户进程和服务器进程之间复制,直接在内存上操作,所以共享内存的速度也就提高了。所以这是最快的一种IPC。
因为共享内存是将同一内存映射到他们的地址空间,所有进程共享同一块内存,共享内存在各种进程间通信方式中具有最高的效率,使用时最好加上同步机制比如信号量。
共享内存操作流程
①进程间通信标识KEY值的生成:ftok函数生成键值
②shmget函数创建共享内存空间
③shmat函数获取第一个可用共享内存空间的地址
④shmdt函数进行分离(对共享存储段操作结束时的步骤,并不是从系统中删除共享内存和结构)
⑤shmctl函数进行删除共享存储空间
共享内存API
shmget
int shmget(key_t key, size_t size, int shmflg): 创建共享内存 ——获得共享存储标识符
key:键值。
size:共享存储段长度,以字节为单位。
shmflg:如果要创建新的共享内存,需要使用IPC_CREAT,IPC_EXCL以及权限。
shmat
void *shmat(int shmid, const void *shmaddr, int shmflg):关联共享内存
创建后还不能马上使用,需要连接到进程的地址空间。
shm_id:是由shmget函数返回的共享内存标识。
shm_addr:指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
shm_flg:是一组标志位,通常为0。具体参数详解可参考(《UNIX环境高级编程》)
返回值:出错返回-1,成功返回该段所连接的实际地址。
shmdt
int shmdt(const void *shmaddr) : 进程与共享存储段shmaddr分离
shmaddr以前调用shmat时的返回值。
shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf);: 操作共享内存(大多为销毁)cmd设置为IPC_RMID时表示可以删除共享内存。
cmd:采取操作使其在shmid指定的段上执行,如下三个参数。
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖struct shmid_ds *buf的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为struct shmid_ds *buf结构中给出的值
IPC_RMID:删除共享内存段
buf: 若cmd为IPC_RMID,则可以将最后一位设置为0。
信号量+共享内存(同步)代码
jc1.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main()
{
void *shmadd;
struct sembuf set;
int *t;
key_t key=ftok("jc2.c",0);
key_t shm_key=ftok("jc1.c",0);
printf("key:%d\n",key);
int sem_id=semget(key,1,IPC_CREAT|0666);
if(sem_id<0)
{
perror("semget");
printf("create error!\n");
}else{
printf("create successfullyftok\n");
}
if(semctl(sem_id,0,SETVAL,1))
printf("set val error\n");
set.sem_num=0;
set.sem_op =-1;
set.sem_flg=SEM_UNDO;
if(!semop(sem_id,&set,1))
{
int shm_id=shmget(shm_key,sizeof(int),IPC_CREAT|IPC_EXCL|0666);
printf("%d\n",shm_id);
if(shm_id<0)
perror("shmget");
shmadd=shmat(shm_id,0,0);
if(shmadd<0)
perror("shmat");
t=(int*)shmadd;
*t=3;
int val=semctl(sem_id,0,GETVAL,0);
printf("val=%d\n",val);
shmdt(shmadd);
}else
perror("semop");
set.sem_num=0;
set.sem_op =1;
set.sem_flg=SEM_UNDO;
semop(sem_id,&set,1);
printf("write over\n");
}
jc2.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main()
{
void *shmadd;
struct sembuf set;
int *t;
key_t key=ftok("jc2.c",0);
key_t shm_key=ftok("jc1.c",0);
printf("key:%d\n",key);/**/
int sem_id=semget(key,0,0666);/*获取已有的信号量*/
printf("%d\n",sem_id);
if(sem_id<0)
{
perror("semget");
printf("create error!\n");
}else{
printf("create successfullyftok\n");
}
int val=semctl(sem_id,0,GETVAL,0);/*查看信号量值*/
printf("val=%d",val);
set.sem_num=0;
set.sem_op =-1;
set.sem_flg=0;
if(!semop(sem_id,&set,1))/*请求信号量控制资源*/
{
int shm_id=shmget(shm_key,0,0666);/*打开已有的共享内存段*/
if(shm_id<0)
perror("shmget");
shmadd=shmat(shm_id,0,0);/*挂载共享内存段到进程地址空间*/
if(shmadd<0)
perror("shmat");
t=(int*)shmadd;
printf("this is t:%d\n",*t);
shmdt(shmadd);/*与shmadd分离*/
shmctl(shm_id,IPC_RMID,0);/*删除共享内存段,其实这里可以加个判断共享内存段的连接计数是否为0,若为0,则删除共享内存*/
}else
perror("semop");
set.sem_num=0;
set.sem_op =1;
set.sem_flg=SEM_UNDO;
semop(sem_id,&set,1);/*释放所占资源*/
printf("read over\n");
if(semctl(sem_id,0,IPC_RMID))/*删除此信号量集*/
perror("semctl");
}
结果