目录
一、共享内存概述
二、共享内存(IPC资源)的查看——ipcm
三、共享内存的创建——shmget
四、共享内存的控制(删除)——shmctl
五、共享内存的关联——shmat
六、共享内存去关联——shmdt
七、进程间通信
一、共享内存概述
概念:让不同进程,看到同一个内存块的方式,就是共享内存
共享内存可以提高进程间通信的效率,减少数据拷贝带来的开销。在 Linux 系统中,可以使用 shmget、shmat、shmdt 等系统调用来操作共享内存。
优点:相比于管道而言,共享内存不仅能够用于非父子进程之间的通信,而且访问数据的速度也比管道要快。这得益于通信直接访问内存,而管道则需要先通过操作系统访问文件再获得内存数据。
缺点:用于进程间通信时,共享内存本身不支持阻塞等待操作。这是因为当读端读取数据后,数据并不会在内存中清空。因此读端和写端可以同时访问内存空间,即全双工。因为共享内存本质是进程直接访问内存,无法主动停止读取,如果读端不加以限制,那么将持续读取数据。同理,写端也会持续写入数据。换句话说,共享内存本身没有访问控制。
二、共享内存(IPC资源)的查看——ipcm
- 查看:ipcm -m
- 删除:ipcrm -m [shmid]
用shell监视IPC资源:
while :; do ipcs -m; echo "---------"; sleep 1; done
共享内存不由进程控制,而是由os控制
三、共享内存的创建——shmget
创建共享内存时,通过ftok()函数获得的key值保证共享内存在系统中的唯一性
.PHONY:all
all: client serve
client: shm_client.cc
g++ -o $@ $^ -std=c++11
serve: shm_serve.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -rf client serve
comm.hpp
#ifndef _COMM_HPP_
#define _COMM_HPP_
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#define NAME_PATH "."
#define PROJ_ID 0x66
#define MAX_SIZE 4096
key_t GetKey()
{
key_t k = ftok(NAME_PATH, PROJ_ID);
if (k < 0)
{
std::cout << errno << ": " << strerror(errno) << std::endl;
exit(1);
}
return k;
}
int GetShmHelper(key_t k, int flags)
{
int shmid = shmget(k, MAX_SIZE, flags);
if (shmid < 0)
{
std::cout << errno << ": " << strerror(errno) << std::endl;
exit(1);
}
return shmid;
}
int GetShm(key_t k)
{
return GetShmHelper(k, IPC_CREAT);
}
int CreateShm(key_t k)
{
return GetShmHelper(k, IPC_CREAT | IPC_EXCL | 0600);
// IPC_EXCL不能单独使用,若共享内存不存在则创建,若存在则报错
}
#endif
shm_serve.cc
#include "comm.hpp"
int main()
{
key_t k = GetKey();
printf("0x%x\n", k);
int shmid = CreateShm(k);
printf("%d\n", shmid);
return 0;
}
shm_client.cc
#include "comm.hpp"
int main()
{
key_t k = GetKey();
printf("0x%x\n", k);
int shmid = GetShm(k);
printf("%d\n", shmid);
return 0;
}
运行结果
四、共享内存的控制(删除)——shmctl
在头文件中加入删除函数
void DelShm(int shmid)
{
if (shmctl(shmid, IPC_RMID, NULL) == -1)
{
std::cout << errno << ": " << strerror(errno) << std::endl;
exit(1);
}
}
shm_serve.cc
#include "comm.hpp"
int main()
{
key_t k = GetKey();
printf("0x%x\n", k);
int shmid = CreateShm(k);
printf("%d\n", shmid);
sleep(5);
DelShm(shmid);
return 0;
}
运行结果
从对IPC资源的循环监视可见,共享内存在shm_serve运行后产生,5s后销毁
五、共享内存的关联——shmat
在头文件中加入关联函数
void* AttachShm(int shmid)
{
void* start = shmat(shmid, NULL, 0);
if ((long long)start == -1)
{
std::cout << errno << ": " << strerror(errno) << std::endl;
exit(1);
}
return start;
}
shm_serve.cc
#include "comm.hpp"
int main()
{
key_t k = GetKey();
printf("0x%x\n", k);
int shmid = CreateShm(k);
printf("%d\n", shmid);
sleep(2);
char* start = (char*)AttachShm(shmid);
printf("attach success, address start: %p\n", start);
sleep(2);
DelShm(shmid);
return 0;
}
运行结果
serve运行之后,共享内存创建,2s之后关联数变为1,又两秒之后共享内存删除
六、共享内存去关联——shmdt
在头文件加入去关联函数
void DetAttachShm(void* start)
{
if (shmdt(start) == -1)
{
std::cout << errno << ": " << strerror(errno) << std::endl;
exit(1);
}
}
shm_serve.cc
#include "comm.hpp"
int main()
{
key_t k = GetKey();
printf("0x%x\n", k);
int shmid = CreateShm(k);
printf("%d\n", shmid);
sleep(2);
char* start = (char*)AttachShm(shmid);
printf("attach success, address start: %p\n", start);
sleep(2);
DetAttachShm(start);
sleep(2);
DelShm(shmid);
return 0;
}
运行结果
serve运行之后,共享内存创建,无关联
2s之后关联数为1, 又2s之后关联数为0,又2s后共享内存销毁
七、进程间通信
shm_client.cc用同样的方式与共享内存关联和去关联
#include "comm.hpp"
int main()
{
key_t k = GetKey();
printf("0x%x\n", k);
int shmid = GetShm(k);
printf("%d\n", shmid);
sleep(2);
char* start = (char*)AttachShm(shmid);
printf("attach success, address start: %p\n", start);
sleep(2);
DetAttachShm(start);
return 0;
}
先运行serve,再运行client,运行结果
在共享内存关联数为2的时刻,两个进程间可进行通信
shm_serve.cc
#include "comm.hpp"
int main()
{
key_t k = GetKey();
printf("0x%x\n", k);
int shmid = CreateShm(k);
printf("%d\n", shmid);
sleep(2);
char* start = (char*)AttachShm(shmid);
printf("attach success, address start: %p\n", start);
//使用
while (true)
{
printf("client say# %s\n", start);
struct shmid_ds ds;
shmctl(shmid, IPC_STAT, &ds);
printf("获取属性:size(%d) pid(%d) myself(%d) key(0x%x)\n",
ds.shm_segsz, ds.shm_cpid, getpid(), ds.shm_perm.__key);
sleep(1);
}
sleep(2);
DetAttachShm(start);
sleep(2);
DelShm(shmid);
return 0;
}
shm_client.cc
#include "comm.hpp"
int main()
{
key_t k = GetKey();
printf("0x%x\n", k);
int shmid = GetShm(k);
printf("%d\n", shmid);
sleep(2);
char* start = (char*)AttachShm(shmid);
printf("attach success, address start: %p\n", start);
//使用
int count = 1;
const char* message = "hello serve, 我是client,给你发消息 ";
while (true)
{
// pid count message
snprintf(start, MAX_SIZE, "%s[pid(%d)[消息编号:%d]\n", message, getpid(), count++);
sleep(1);
}
sleep(2);
DetAttachShm(start);
return 0;
}
运行结果