共享内存,指两个或多个进程共享一个给定的存储区。
可以从指针间接赋值思考。比如:malloc一段物理内存,然后有几个指针同时指向它,进行间接赋值。
目录
特点:
1,共享内存是最快的一种IPC,直接操作内存。
2,多个进程可以同时操作,所以需要进行同步。(这算是缺点)
共享内存没有同步机制,当多个进程同事向共享内存读写数据时,我们需要使用互斥锁,读写锁,信号量,条件变量等来确保数据一致性。
3,信号量+共享内存通常结合一起使用,信号量用来同步对共享内存的访问。
API:
1)shmget()函数:创建共享内存。
int shmget(key_t key,size_t size,int shmflg);
#程序先通过调用shmget函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget函数的返回值),只有shmget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。
2)shmat()函数 :连接共享内存到当前进程的地址空间。
void *shmat(int shm_id,const void *shm_addr,int shmflg); 返回一个指向共享内存第一个字节的指针,失败返回-1。
3)shmdt()函数 :当前进程分离共享内存。
int shmdt(const void *shmaddr); shmaddr是shmat函数返回的地址指针。
4)shmctl()函数 :控制共享内存。
int shmctl(int shm_id,int command,struct shmid_ds *buf);
控制函数看第二个参数提供的命令,command是要采取的操作,它可以取下面的三个值 :
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段
shmdt()和shmctl()的区别:
1,当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,但是,这并不是从内核真正地删除这个段,而是把相关shmid_ds结构的 shm_nattch域的值减1,当这个值为0时,内核才从物理上删除这个共享段。
2,IPC_RMID 命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生在最后一个进程离开这个共享段时。
3,shmdt(addr)使进程中的shmaddr指针无效化,不可以使用,但是保留空间。
shmctl(shmid,IPC_RMID,0) 删除共享内存,彻底不可用,释放空间。
调用顺序:
如下,完整版是5步,第5步删除要根据是读/写(获取/生成)进行区分,写(生成)操作时则不需要第5步。
#define MEM_KEY 0x2234
int shmid;
void *shm;
struct share_type *shared;
//1:创建共享内存
shmid = shmget((key_t)MEM_KEY, sizoef(struct share_type), 0666|IPC_CREAT);
//2:将共享内存连接到当前进程的地址空间
shm = shmat(shmid, 0, 0);
//3:设置共享内存
shared = (struct share_type *)shm;
//4: 从进程中分离共享内存
shmdt(shm);
//5:删除共享内存,第二个参数为command, 选项IPC_RMID为删除
shmctl(shmid, IPC_RMID, 0);
扩展:
linux终端命令ipcs可以查看当前系统的消息队列,共享内存等。
ipcrm -q MessageID //删除消息队列
ipcrm -m SharedMemoryID //删除共享内存段
ipcrm -M SharedMemoryKey
ipcrm -s SemaphoreID //删除信号量