图控大叔
构图传递思想
阅读从未如此简单!!!
01
前言
继续刚!今天分享的内容是Linux下IPC机制的通信方式:共享内存,之前我们已经分享了消息队列,下一篇我们将分享IPC机制的通信方式:信号量。继续刚,干就完事了!
02
IPC回顾
IPC
内核当中为了增强进程与进程之间数据交互及效率的一种机制的对象
英文名
Inter Process Communication
中文名
进程间通信
IPC里面包括什么:
消息队列、信号量、共享内存
03
细述共享内存
共享内存:
1、速度最快的一种进程间通信方式:
2、在内核空间(物理内存)中开辟一块内存出来,
映射给不同的进程的虚拟内存中,
这样我们就可在不同的进程中访问同一块内存
IPC通用API
IPC通信通用API
key_t ftok(const char *pathname, int proj_id);
函数功能:获取一个IPC对象的key,
方便我们在后期通过这一个key创建一个IPC对象,
其可以为共享内存或者消息队列或者信号量
参数:
pathname:一个路径名,用来通过和一个路径与该函数的第二个参数去得到一个IPC的key
proj_id:这个是一个不为0的小于或等于255的数值,用来跟pathname组合得到IPC的key
返回值:
成功返回一个IPC对象的key值,失败则返回-1,errno会被设置
共享内存相关函数速览
需包含头文件
#include
#include
#include
相关函数速览
获取共享内存的对象idint shmget(key_t key, size_t size, int shmflg);
映射共享内存到进程的虚拟内存
void *shmat(int shmid, const void *shmaddr, int shmflg);
取消共享内存的映射
int shmdt(const void *shmaddr);
控制或获取共享内存的属性
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
获取共享内存的对象id
获取共享内存的对象idint shmget(key_t key, size_t size, int shmflg);
函数功能:
获取共享内存的ID,包括可以创建一个指定内存大小的共享内存出来
参数:
key:IPC对象的key值
size:所指定的共享内存的大小为多大,
这个指定的大小系统要求我们是PAGE_SIZE大小的倍数(4096字节的倍数)
一般提前宏定义,如:
#define PAGE_SIZE 4096
shmflg:共享内存操作标志位:
IPC_CREAT:
如果key值所对应的IPC对象中没有共享内存,
则去创建一个共享内存出来
IPC_EXCL:
如果这个宏跟上面的创建标志并用,
代表IPC对象存在共享内存则报错
此外,
在引用IPC_CREAT的基础上,
我们可以或上去一个mode值,
以代表创建的共享内存的权限,
这个mode值其实就是open函数里面一样原理
返回值:
成功则返回共享内存的id,失败则返回-1,并且errno会被设置
映射共享内存到进程的虚拟内存
映射共享内存到进程的虚拟内存
void *shmat(int shmid, const void *shmaddr, int shmflg);
函数功能:
映射共享内存到我们这个进程的虚拟内存当中。
函数参数:
shmid:shmget这个函数所获取到的共享内存的ID
shmaddr:
如果这个参数为NULL,
则由系统帮我们决定映射共享内存的地址到虚拟内存的位置
如果这个参数不为NULL,
并且SHM_RND也被指定,
则系统会按照SHMLBA的倍数对齐方式来映射这一块内存。
如果这个参数不为NULL,
同时也没有指定其他参数,
则shmaddr必须得是页大小的倍数去设置
shmflg:共享内存映射的操作标志位
0:
代表其按照默认的共享内存去操作
SHM_RND:
系统自动以 SHMLBA倍数去映射共享内存
SHM_EXEC:
指定共享内存映射其执行权限
SHM_RDONLY:
指定共享内存映射其成为只读访问权限
SHM_REMAP:
指定共享内存重新映射至指定的shmaddr中
函数返回值:
成功返回映射到的虚拟内存的地址,失败(void *) -1,errno会被设置
取消共享内存的映射
取消共享内存的映射
int shmdt(const void *shmaddr);
函数功能:
取消共享内存的映射
参数:
shmaddr:原本虚拟内存的映射地址
返回值:
成功返回0,失败返回-1,errno会被设置
控制或获取共享内存的属性
控制或获取共享内存的属性
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
函数功能:
控制或获取共享内存的属性
参数:
shmid:指定要操作的共享内存的id
cmd:操作命令:
IPC_STAT:获取共享内存的信息放到buf这个参数中,这个操作需要我们指定共享内存拥有读权限
IPC_SET:用来将buf这个参数中的信息设置到共享内存中
IPC_RMID:删除一个共享内存,其中该函数的第三个参数buf是忽略的,删除后会唤醒所有正在操作这个共享内存的进程,并且返回错误
IPC_INFO:获取一个内核级别的共享内存的信息,这个信息是一个struct shminfo的结构体数据,里面包含声明了多个内核级别的共享内存的信息值
MSG_INFO:linux中特有参数,用来获取共享内存的信息,获取的信息struct shminfo类型数据
MSG_STAT:在linux中引用这个参数相当于引用IPC_STAT参数,获取一个struct shmid_ds结构体数据,引用这个参数的时候我们的msqid这个参数的意义就变了,并不是共享内存的id值,而是传一个内核共享内存的数组下标,所以这个参数对我们的使用意义不大
SHM_LOCK:给共享内存加锁,使其他进程在访问共享内存的时候不能操作
SHM_UNLOCK:给共享内存解锁
说明:SHM_LOCK与SHM_UNLOCK大家谨慎点,小编试了一下,翻车了
返回值:
如果命令设置了IPC_INFO,MSG_INFO,MSG_INFO,
则成功返回其内核的下标资源,
其他则返回0,
失败返回-1,
errno会被设置
上下滚动查看更多
参考代码:read.c#include
#include
#include
#include
#include
#define PAGE_SIZE 4096
int main(void){
key_t key;
int shm_id;
int retval;
char *shm_addr;
//获取一个IPC对象
key = ftok(".", 1);
if(key == -1)
{
perror("获取IPC对象的key值出错");
goto get_ipc_key_err;
}
/*
获取共享内存的ID,如果共享内存不存在则创建出来,权限为0644
key:代表IPC对象的key值
PAGE_SIZE:申请的共享内存的大小,必须是以一页为倍数(4096字节)
IPC_CREAT|0644:不存在则创建,创建权限为0644
*/
shm_id = shmget( key, PAGE_SIZE, IPC_CREAT|0644);
if(shm_id == -1)
{
perror("获取共享内存出错");
goto get_shmid_err;
}
shm_addr = shmat(shm_id, NULL, 0);
if(shm_addr == (void *)-1)
{
perror("映射共享内存出错");
goto map_shm_err;
}
printf("共享内存的数据为=%s\n", shm_addr);
retval = shmdt(shm_addr);
if(retval == -1)
{
perror("取消内存映射出错");
goto unmap_shm_err;
}
struct shmid_ds buf[sizeof(struct shmid_ds)];
//删除共享内存
//shmctl( shm_id, IPC_RMID, NULL);
shmctl( shm_id, IPC_STAT, buf);
printf("共享内存总字节数为:%d\n",buf->shm_segsz);
return 0;
unmap_shm_err:
map_shm_err:
shmctl( shm_id, IPC_RMID, NULL);
get_shmid_err:
get_ipc_key_err:
return -1;
}
参考代码:write
#include
#include
#include
#include
#include
#define PAGE_SIZE 4096
int main(void){
key_t key;
int shm_id;
int retval;
char *shm_addr;
//获取一个IPC对象
key = ftok(".", 1);
if(key == -1)
{
perror("获取IPC对象的key值出错");
goto get_ipc_key_err;
}
/*
获取共享内存的ID,如果共享内存不存在则创建出来,权限为0644
key:代表IPC对象的key值
PAGE_SIZE:申请的共享内存的大小,必须是以一页为倍数(4096字节)
IPC_CREAT|0644:不存在则创建,创建权限为0644
*/
shm_id = shmget( key, PAGE_SIZE, IPC_CREAT|0644);
if(shm_id == -1)
{
perror("获取共享内存出错");
goto get_shmid_err;
}
shm_addr = shmat(shm_id, NULL, 0);
if(shm_addr == (void *)-1)
{
perror("映射共享内存出错");
goto map_shm_err;
}
scanf("%s", shm_addr);
if(retval == -1)
{
perror("取消内存映射出错");
goto unmap_shm_err;
}
struct shmid_ds buf[sizeof(struct shmid_ds)];
bzero(buf,sizeof(struct shmid_ds));
//删除共享内存
//shmctl( shm_id, IPC_RMID, NULL);
shmctl( shm_id, IPC_STAT, buf);
printf("共享内存总字节数为:%d\n",buf->shm_segsz);
return 0;
unmap_shm_err:
map_shm_err:
shmctl( shm_id, IPC_RMID, NULL);
get_shmid_err:
get_ipc_key_err:
return -1;
}
其他说明
1、唯一身份凭据
key+id
2、加锁解锁问题
自带加解锁操作,但是小编没有成功过
3、读写问题
一次性读写,二次写将覆盖上一次的数据
04
结尾
本次关于共享内存的分享,到这里就结束了,如果读者发现其中的内容存在纰漏,希望指出,谢谢!