Linux进程间通信(下)之共享内存实践
上节和上上节我们分享了Linux进程间通信的管道、消息队列、信号以及信号量的基本原理和实践,文章如下:
这节我们就来分享一下Linux的最后一种进程间通信的方式:共享内存。
1、什么是共享内存
共享内存就是两个不相关的进程之间可以直接访问同一段内存,共享内存在两个正在运行的进程之间共享和传递数据起到了非常有效的方式。在不同的进程之间共享的内存通常安排为同一段物理内存,进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以直接访问共享内存中的地址。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程;其实就是映射一段能够被其它内存所访问到的内存,这段内存由一个进程创建,但是多个进程都可以去访问。共享内存是最快的IPC方式,它是通过其它通信方式的效率不足而专门设计的。往往都是和其它通信机制配合使用,来实现进程间的同步和通信。
共享内存的使用和信号量其实也是差不多的,都是使用接口的形式,共享内存的接口比信号量的接口更加的简单,我们一起去了解下共享内存的使用。
共享内存函数由shmget、shmat、shmdt、shmctl
四个函数组成。我们下面来分析每一个函数的用法。
1.1、创建共享内存
int shmget(key_t key, size_t size, int shmflg);
第一个参数是共享内存段的命名,shmget成功时返回一个关于key相关的标识符,用于后续的共享内存函数。当调用失败返回-1。其它进程也可以通过shmget函数返回值访问同一个共享内存。第二个参数是指定共享内存的容量;第三个shmflg是一个权限标志,它的作用和open和mode函数都是相同的,当共享内存不存在的时候则通过IPC_CREAT来创建。共享内存的权限标准和文件读写的权限一样。
1.2、启动对共享内存的访问
void *shmat(int shm_id, const void *shm_addr, int shmflg);
当我们第一次创建完共享内存时,它还不能被任何进程访问,shmat函数就是用来启动对共享内存的访问,并把共享内存连接到当前进程的地址空间。
shm_id是由shmget函数返回的共享内存标识;shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。最后一个参数是标志位通常都是0。调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1。
1.3、共享内存从当前内存中分离
int shmdt(const void *shmaddr);
这个函数只是从共享内存中分离而不是删除,这一点要分清楚,对于初学者而言这里很容易掉坑,使共享内存在当前进程中不可再用。
参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1。
1.4、控制共享内存
int shmctl(int shm_id, int command, struct shmid_ds *buf);
第一个参数是shaget函数返回的共享内存标识符;command参数是要采取的操作,它由 IPC_STAT、IPC_SET和IPC_RMID组成,分别IPC_STAT代表把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值;IPC_SET代表如果进程有足够的权限,就可以把共享内存的当前关联值设置为shmid_ds结构中给出的值;IPC_RMID代表删除共享内存段。第三个参数buf代表一个结构指针,它指向共享内存的模式或访问权限的结构。
shmid_ds结构至少包括以下成员:
struct shmid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};
2、共享内存案例
shm_snd.c
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024
int main()
{
int shmid;
char *shmptr;
//创建共享内存
shmid = shmget(0x66, SHM_SIZE, IPC_CREAT|0666);
//创建失败
if(shmid < 0)
{
perror("shmget");
return -1 ;
}
//对共享内存的访问
shmptr = shmat(shmid, 0, 0);
if (shmptr == (void *)-1)
{
perror("shmat");
return -2 ;
}
// 往共享内存写数据
strcpy(shmptr, "shmat write ok");
shmdt(shmptr);
return 0 ;
}
shm_rcv.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024
int main()
{
int shmid;
char *shmptr;
shmid = shmget(0x66, SHM_SIZE, IPC_CREAT|0666);
if(shmid < 0)
{
perror("shmget");
return -1 ;
}
shmptr = shmat(shmid, 0, 0);
if (shmptr == (void *)-1)
{
perror("shmat");
return -2 ;
}
// 从共享内存读数据
printf("read:%sn", shmptr);
shmdt(shmptr);
return 0 ;
}
运行结果:
先分别编译shm_snd.c和shmrcv.c这两个程序,生成shmrcv和shmsnd这两个可执行程序。
接下来,首先执行shmsnd,会得到以下结果:
什么都没有?共享内存创建成功了吗?当然是成功了,可以通过ipcs –m
命令查看:
如图上图所示,nattch项下的数字为0那个就是刚刚使用shmsnd这个可执行程序创建的一段共享内存。当然,我们还往共享内存发了shmat write ok
这个字符串,下面运行shmrcv这个程序,看看是否能把写进共享内存的数据读出来。
成功读出。同样的,也可以删除共享内存,如何删除?也一样有两种方法。
(1)使用ipcrm –m shmid可以删除共享内存
如上图,我们已经知道0x66的shmid为1835021,所以只要执行ipcrm –m 1835021
命令即可删除,如下图所示,成功删除。
(2)使用shmctl 函数写入IPC_RMID指令删除共享内存
shmrm.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(void)
{
int shmid ;
//同样,首先先打开共享内存
shmid = shmget(0x66 , 0 , 0);
if(-1 == shmid)
{
perror("open shmkey 0x66 fail");
return -1 ;
}
//成功的话,向shmctl写入参数,IPC_RMID表示立刻删除,后面的参数被忽略,为0
int ret ;
//写入的是参数
ret = shmctl(shmid , IPC_RMID , NULL);
if(ret < 0)
{
perror("remove shm fail");
return -2 ;
}
printf("remove key:%d success ... n" , 0x66);
return 0 ;
}
运行结果:
- 通过jenkins API去build一个job
- Django---分页器、中间件
- 启动jenkins服务错误
- 如果未来的AI拥有意识,你舍得不理它吗?
- centos下安装python3
- jboss:在standalone.xml中设置系统属性(system-properties)
- iptables
- Django-form表单
- 比较git commit 两个版本之间次数
- eclipse: workspace出错导致无法启用的解决
- 【node错误】/usr/bin/env: node: No such file or directory
- Django比较相等或者不相等的模板语法ifequal / ifnotequal
- 使用testNGListenter来自定义日志
- 通过代码去执行testNG用例
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- TensorFlow2.X学习笔记(8)--TensorFlow高阶API之构建模型、训练模型
- dotnet 使用 AsyncQueue 创建高性能内存生产者消费者队列
- TensorFlow2.X学习笔记(7)--TensorFlow中阶API之losses、metrics、optimizers、callbacks
- 前端须知的 Cookie 知识小结
- TensorFlow2.X学习笔记(6)--TensorFlow中阶API之特征列、激活函数、模型层
- sortable.js——Vue 数据更新问题
- 【项目实战】环境搭建
- TensorFlow2.X学习笔记(5)--TensorFlow中阶API之数据管道
- TensorFlow2.X学习笔记(4)--TensorFlow低阶API之AutoGraph相关研究
- TensorFlow2.X学习笔记(3)--TensorFlow低阶API之张量
- TensorFlow2.X学习笔记(2)--TensorFlow的层次结构介绍
- 深入浅出 Vue 中的 key 值
- TensorFlow2.X学习笔记(1)--TensorFlow核心概念
- 【项目实战】ODS 层创建&数据接入
- webpack3 升级到 webpack4 小记