共享内存
共享内存是被多个进程共享的一块物理内存。
- 物理内存
- 映射到各个进程的虚拟内存空间
- 各进程通过虚拟内存空间地址来操作共享内存
- 进程间共享数据的最快方法
- 本身不提供同步机制,可通过信号量同步
- 子进程不继承父进程创建的共享内存,而是与父进程继续共享那块内存;在子进程中映射的虚拟地址和父进程是一样的。
函数
创建共享内存
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
其中key为用户指定的共享内存键值;size为共享内存大小;shmflg为IPC_CREAT 、 IPC_EXCL等权限的组合。
如果创建成功,则返回内核中共享内存的标识ID。如果失败则返回-1。
控制共享内存
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
其中shmid是共享内存的标识ID;buf为指向共享内存属性结构体的指针;cmd可以取一下命令宏:
IPC_STAT 获取共享内存的属性
IPC_SET 设置共享内存的属性
IPC_RMID 删除共享内存
SHM_LOCK 锁定共享内存页面(物理内存不和外存进行换入换出操作)
SHM_UNLOCK 解除锁定共享内存页面
映射共享内存
#include <sys/shm.h>
void* shmat(int shmid, char *shmaddr, int shmflg);
其中shmid是共享内存标识ID;shmaddr映射到进程虚拟空间的地址,一般设置为0(即由操作系统分配);shmflg来设置内存的特性,一般也设置为0;
成功则返回虚拟内存空间的地址;失败则返回-1。
解除映射共享内存
#include <sys/shm.h>
void* shmdt(char *shmaddr);
shmaddr为映射到进程虚拟空间的地址。
简单实例
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int shmid;
//创建共享内存
if((shmid = shmget(IPC_PRIVATE,1024,
IPC_CREAT|IPC_EXCL|0777))<0)
{
perror("shmget error");
exit(1);
}
//映射共享内存
int *p = (int *)shmat(shmid,0,0);
if(p == (int *)-1)
{
perror("shmat error");
exit(1);
}
//对共享内存写入数据
*p = 100;
*(p+1) = 200;
*(p+2) = 300;
//解除共享内存映射
shmdt(pi);
//删除共享内存
shmctl(shmid,IPC_RMID,NULL);
exit(0);
}
共享内存只有一个,使用完毕之后,可以在创建它的进程中删除,也可以在其他进程中删除。
共享内存的同步
使用信号量的PV操作进行同步。
为共享内存定义两个信号量,分别为S1,S2,将这两个信号量的值都初始化为0。
数据生产者:写入数据——V(S1)——P(S2)
数据消费者:P(S1)——读取数据——V(S2)
以上信号量的使用将实现如下逻辑:
1.生产者在写入数据之前,消费者处于阻塞状态,等待生产者写入数据而后读取;
2.生产者写入数据之后,信号量S1被加1,从而允许消费者读取数据;另一方面S2减1,导致生产者被阻塞;
3.在数据消费者的进程中,P(S1)之后允许进程使用共享内存资源,进程不阻塞,从而可以执行读取数据的操作。
4.消费者读完数据之后,对S2加1,解除数据生产者的阻塞状态,使其进程继续运行,产生数据。
如此,共享内存中数据的读写则实现了同步。
创建信号量集(该信号量集中有semnums个信号量)
int semid = semget(IPC_PRIVATE,semnums,
IPC_CREAT|IPC_EXCL|0777);
初始化信号量集(用semun结构体进行初始化)
int i = 0;
union semun un;
unsigned short *array =
(unsigned short *)calloc(semnums,
sizeof(unsigned short));
for(i=0;i<semnums;i++)
{
array[i]=value;
}
un.array = array;
if(semctl(semid,0,SETALL,un)<0)
{
perror("semctl error");
exit(-1);
}
free(array);
P操作(减值操作,使用sembuf结构体。给信号量集semid其中的信号量semnum减去value)
struct sembuf ops[]={{semnum, -value, SEM_UNDO}};
if(semop(semid,ops,sizeof(ops)/sizeof(struct sembuf))<0)
{
perror("semop error");
}
V操作(加值操作,使用sembuf结构体。给信号量集semid其中的信号量semnum加去value)
struct sembuf ops[]={{semnum, value, SEM_UNDO}};
if(semop(semid,ops,sizeof(ops)/sizeof(struct sembuf))<0)
{
perror("semop error");
}
销毁信号量集
if(semctl(semid,0,IPC_RMID,NULL)<0)
{
perror("semctl");
}