目录

一 共享内存的作用

二 共享内存的实现

2.1 shm系统调用

2.1.1 共享内存的创建

2.1.2 共享内存的关联与分离

2.1.3 共享内存的设置

2.1.4 测试案例

2.2 mmap内存映射

2.2.1 函数的使用

2.2.2 测试案例


一 共享内存的作用

共享内存主要是用来实现进程间通讯的一种机制,只不过,需要进程间互斥的访问这段内存,因此需要使用信号量等锁机制来对共享内存进行访问。

二 共享内存的实现

共享内存的实现主要由两种方式:一种是shm系统调用;一种是mmap实现共享(因为mmap也可以实现私有映射)的页面映射。

2.1 shm系统调用

shm系统调用主要是在sys/shm.h头文件中。

2.1.1 共享内存的创建

int shmget(key_t key, size_t size, int shmflg);

函数功能:用于创建或获取用键值为key的一段大小为size的共享内存。

函数参数:

1.key:IPC内核资源(信号量,消息队列,共享内存)的键值,用于在全局唯一的表示该资源。

2.size:申请的共享内存大小。

3.shmflg:主要讲解几个参数:

a.IPC_CREAT和IPC_EXCL与信号量的含义相同;

b.SHM_HUGETLB:用于申请大页面作为共享内存。

c.SHM_NORESERVE:不为该共享内存创建swap交换分区。会导致如果物理内存不够用了,要对该共享内存读写的话会出错并且返回SIGSGV信号量。

创建成功的话,会在内核创建一个shmid_ds类型的结构体变量与该共享内存关联:

struct shmid_ds{IPC_PERM shm_perm;//用于表示该共享内存的键值和权限size_t   shm_segsz;//用于表示该共享内存的大小_time_t  shm_atime;//用于表示最后一次调用shmat()函数的时间_time_t  shm_dtime;//用于表示最后一次调用shmdt()函数的时间_time_t  shm_ctime;//用于表示最后以此调用shmclt()函数的时间_pid_t   shm_cpid;//用于表示创建共享内存的进程PID_pid_t   shm_lpid;//用于表示最后以此调用shmat()函数和shmdt()函数的进程PIDshmatt_t shm_nattach;//用于表示关联到该共享内存的进程个数
};

2.1.2 共享内存的关联与分离

void* shmat(int shm_id, const void* shm_addr, int shmflg);//将共享内存shm_id绑定到进程地址shm_addr
int shmdt(const void* shm_addr);//将进程地址shm_addr与共享内存分离

函数功能:

1.shmat():主要是将调用进程的内存地址shm_addr与shm_id标识的共享内存关联:

2.shmdt():主要是讲调用进程的内存地址shm_addr与共享内存分离。

函数参数:

1.shm_id:是shmget()函数返回的用于标识该共享内存的id号;

2.shm_addr:是进程的指针,主要是用于关联共享内存的起始地址:当不设置该参数时,系统自动分配地址(推荐选择);当设置该参数时,会讲共享内存关联到该内存地址。

3.shmflg:主要参数有如下几个:

a.SHM_RND:当不设置设置该参数时,直接将共享内存关联到shm_addr标识的内存地址(前提是设置了要映射的内存地址shm_addr);当设置该参数时,会将内存地址向下圆整到SHMLBA的整数倍地址处(SHMLBA是内存页面大小的整数倍);

b.SHM_RDONLY:该参数主要是设置该进程对共享内存的访问权限为只读;

c.SHM_REMAP:如果该内存地址shm_addr映射了其他共享内存,重新映射该共享内存。

2.1.3 共享内存的设置

int shmctl(int shm_id, int command, shmid_ds* buf);

函数功能:主要是给该共享内存进行相关设置。

具体使用(常用功能):

1.IPC_STAT:主要是将shm_id标识的共享内存的内核shmid_ds结构体存入函数的参数buf所指向的shmid_ds结构体中;

2.IPC_SET:主要是将buf所指向的shmid_ds结构体赋值给shm_id所标识的共享内存的内核shmid_ds结构体中;

3.IPC_RMID:给该共享内存打上删除的标记,当最后一个与该共享内存关联的进程调用shmdt()函数时,自动删除该共享内存以及与之关联的shmid_ds结构体变量。

2.1.4 测试案例

测试代码:

#include<sys/sem.h>
#include<sys/shm.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>union semun{int val;struct semid_ds* buf;unsigned short int* array;struct seminfo* _buf;
};void pv(int sem_id, int sem_op)
{struct sembuf sem_ops;sem_ops.sem_num = 0;sem_ops.sem_op = sem_op;sem_ops.sem_flg = SEM_UNDO;semop(sem_id, &sem_ops, 1);
}int main()
{char* message1 = "a";char* message2 = "b";printf("开始:\n");//创建信号量int sem_id = semget(IPC_PRIVATE, 1, 0666);//创建信号量集(其中只有一个信号量)union semun sem_un;sem_un.val = 1;//信号量初始值设为1,这里只测试二元信号量semctl(sem_id, 0, SETVAL, sem_un);//创建共享内存int shm_id = shmget(IPC_PRIVATE, 10, 0666);pid_t pid = fork();if(pid < 0)return -1;else if(pid == 0){char* shmaddr = shmat(shm_id, NULL, 0);pv(sem_id, -1);//进入临界区for(int i = 0; i < 5; i++){strcat(shmaddr, message1);sleep(1);printf("%s\n", shmaddr);}pv(sem_id, 1);//退出临界区shmdt(shmaddr);exit(0);//终止子进程}else{char* shmaddr = shmat(shm_id, NULL, 0);pv(sem_id, -1);//进入临界区for(int i = 0; i < 5; i++){strcat(shmaddr, message2);sleep(1);printf("%s\n", shmaddr);}pv(sem_id, 1);//退出临界区shmdt(shmaddr);}waitpid(pid, NULL, 0);//回收子进程semctl(sem_id, 0, IPC_RMID, sem_un);//删除信号量struct shmid_ds *buf;shmctl(shm_id, IPC_RMID, buf);//最后一个进程分离,删除共享内存及其相应的shmid_ds变量return 0;
}

加锁对共享内存进行读写:

不加锁对共享内存进行读写:

2.2 mmap内存映射

mmap函数主要是用于申请一段内存空间:1.作为共享内存;2.作为私有内存;3.文件映射;4.匿名映射。(1,2和3,4排列组合可以达到不同的效果)。

2.2.1 函数的使用

void* mmap(void*start,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void*start,size_t length);

函数功能:

1.mmap():内存映射。

2.munmap():解除起始地址为start,长度为length的内存映射。

函数参数:

1.start:主要是映射在内存的起始位置,如果为空则系统自动分配(推荐设置方法);

2.length:映射的长度(文件长度与内存长度对应);

3.prot:用来设置该段内存的访问权限:

a.PROT_READ:可读;

b.PROT_WRITE:可写;

c.PROT_EXEC:可执行;

d.PROT_NONE:不能被访问。

4.flag:常用参数:

a.MAP_SHARED:作为共享内存进行映射,并且对于该内存的修改会回写到对应的文件中(如果时文件映射的话);

b.MAP_PRIVATE:作为私有内存进行映射,被调用进程所私有,并且不会回写到对应的文件中;

c.MAP_ANONYMOUS:作为匿名内存进行映射,该段内存不是来自于文件的,所以mmap()函数的后两个参数可以忽略;

d.MAP_HUGETLB:按照大内存页面进行分配。

2.2.2 测试案例

这里我采用的是匿名映射的方式,为了展示的方便,不去映射具体的文件,具体代码如下:

#include<sys/sem.h>
#include<sys/mman.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>union semun{int val;struct semid_ds* buf;unsigned short int* array;struct seminfo* _buf;
};void pv(int sem_id, int sem_op)
{struct sembuf sem_ops;sem_ops.sem_num = 0;sem_ops.sem_op = sem_op;sem_ops.sem_flg = SEM_UNDO;semop(sem_id, &sem_ops, 1);
}int main()
{char* message1 = "a";char* message2 = "b";printf("开始:\n");//创建信号量int sem_id = semget(IPC_PRIVATE, 1, 0666);//创建信号量集(其中只有一个信号量)union semun sem_un;sem_un.val = 1;//信号量初始值设为1,这里只测试二元信号量semctl(sem_id, 0, SETVAL, sem_un);//创建共享内存char* mmap_addr = mmap(NULL, 10, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_ANONYMOUS, -1, 0);//MAP_ANONYMOUS:匿名映射pid_t pid = fork();if(pid < 0)return -1;else if(pid == 0){pv(sem_id, -1);//进入临界区for(int i = 0; i < 5; i++){strcat(mmap_addr, message1);sleep(1);printf("%s\n", mmap_addr);}pv(sem_id, 1);//退出临界区exit(0);//终止子进程}else{pv(sem_id, -1);//进入临界区for(int i = 0; i < 5; i++){strcat(mmap_addr, message2);sleep(1);printf("%s\n", mmap_addr);}pv(sem_id, 1);//退出临界区}waitpid(pid, NULL, 0);//回收子进程semctl(sem_id, 0, IPC_RMID, sem_un);//删除信号量munmap(mmap_addr, 10);return 0;
}

加锁对共享内存进行读写:

不加锁对共享内存进行读写:

进程的IPC操作 之 共享内存相关推荐

  1. Linux 进程间通讯(IPC)方式 ------- 共享内存

    Linux 进程间通讯(IPC)方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) ...

  2. linux进程间的通信(C): 共享内存

    一.共享内存介绍 共享内存是三个IPC(Inter-Process Communication)机制中的一个. 它允许两个不相关的进程访问同一个逻辑内存. 共享内存是在两个正在进行的进程之间传递数据的 ...

  3. linux 共享内存_linux进程间通信----IPC篇(一)----共享内存初识篇

    先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题 一 what 所 ...

  4. Linux进程间的通信----->共享内存

    共享内存:         顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式.不同进程之间共享的内存通常安排为同一段物 ...

  5. Linux IPC实践(8) --共享内存/内存映射

    概述 共享内存区是最快的IPC形式.一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据(如图). 共享内存 VS ...

  6. Linux C 进程间的IPC通信 之 共享内存(一)

    1.IPC(inter - process communication)通信 共享内存.消息队列.信号灯 2.库 <sys/shm.h> 2-1  创建共享内存 int shmget( k ...

  7. Linux C 进程间的IPC通信 之 共享内存(二)

    1.父子进程(有亲缘关系)的IPC通信 int shmid;       shmid = shmget(IPC_PRIVATE, 128, IPC_CREAT | 0777); //创建共享内存,参数 ...

  8. UC编程9-管道pipe操作和共享内存段shm操作

    //myuc.h #include<stdio.h>//io流 #include<stdlib.h>//标准库 #include<unistd.h>//uc标准头文 ...

  9. MFC:通过代码简单理解进程间的通讯机制——共享内存

    下面用共享映射文件的方式实现进程间通信,代码可以运行. 一.浅理解 每个进程有自己独立的空间,一个进程无法访问其他进程的数据.就好像两个是互不干涉的个体,想让它们进行通信(交换数据),就必须有一段它们 ...

最新文章

  1. 连麦互动直播方案全实践3:网易云信连麦互动的实现方案
  2. basler相机 ip linux,Basler 相机启动运行程序, Basler IP相机软件 - 其他软件 | Basler
  3. DNS子域委派配置案例[转载]
  4. Algorithms_二叉树的前序遍历、中序遍历、后续遍历(深度优先)
  5. 开发DBA(APPLICATION DBA)的重要性
  6. java calendar字符串显示_java关于字符串和日期的代码展示
  7. build openposewith opencv-2.4.13,cuda9(9.0 - 9.2)
  8. 成功解决Could not fetch URL https://pypi.tuna.tsinghua.edu.cn/simple/xx/: There was a problem confirming
  9. 数字藏品到底有什么魔力?目前有哪些靠谱的团队在开发
  10. TZC 3012-Fibnacci Numbers(矩阵快速幂+降幂公式)
  11. 「SQL面试题库」 No_10 超过经理收入的员工
  12. 【HTML5】------- JavaScript 实现网页版HTML5发送语音功能
  13. 支持向量机(SVM)、支持向量回归(SVR)
  14. 神盾加密php文件夹,[宜配屋]听图阁
  15. 拜托,你的这些努力一定要让HR看见!
  16. vb.net mysql 查询,mysql-vb.net查询以显示数据表的特定行[基本]
  17. Lattice Diamond关于原语的使用
  18. 阿迪達斯NFT狂卷兩千多萬美元,但過程並非一帆風順
  19. 陈志武:教育不转型,国家只能卖苦力
  20. FFMEPEG+SDL2 多路音频采集播放

热门文章

  1. Windows Server 2008 R2 系统管理篇(理论+实战)-深博-专题视频课程
  2. 小学生除法检验,10道题,每小题10分,被除数和除数随机产生。被除数是20~200之间的数,除数是2~9之间的一位数。用户输入答案,计算机判卷,做对了加10分,做错了不允许重做,最后输出成绩;
  3. C语言域名解析的简单实现
  4. 计蒜客 难题题库 204 草药的价值
  5. 欠料、品质问题频发,PMC该怎么做?
  6. 如何在Ubuntu 18.04上设置Mattermost
  7. 差分 题解加图详解(通俗易懂)
  8. 【模电】寻找静态工作点——三极管放大电路的仿真和估算(1)
  9. G1D8-GNN@lab0APT论文美亚2021个人赛
  10. 从WLAN的安全威胁 西电捷通解析电信诈骗技术症结