一、共享内存介绍
共享内存是三个IPC(Inter-Process Communication)机制中的一个。
它允许两个不相关的进程访问同一个逻辑内存。
共享内存是在两个正在进行的进程之间传递数据的一种非常有效的方式。
大多数的共享内存的实现,
都把由不同进程之间共享的内存安排为同一段物理内存
共享内存是由IPC为进程创建一个特殊的地址范围,
它将出现在该进程的地址空间中。
其他进程可以将同一段共享内存连接它们自己的地址空间中。
所有进程都可以访问共享内存中的地址,
就好像它们是由malloc分配的一样。
如果某个进程向共享内存写入了数据,
所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。
二、共享内存的同步
共享内存为在多个进程之间共享和传递数据提供了一种有效的方式。
但是它并未提供同步机制
所以我们通常需要用其他的机制来同步对共享内存的访问。
我们通常是用共享内存来提供对大块内存区域的有效访问,
同时通过传递小消息来同步对该内存的访问。
在第一个进程结束对共享内存的写操作之前,
并无自动的机制可以阻止第二个进程开始对它进行读取。
对共享内存访问的同步控制必须由程序员来负责。
下图显示了共享内存是如何共存的:
图中的箭头显示了每个进程的逻辑地址空间到可用物理内存的映射关系。
三、共享内存使用的函数
  1. #include <sys/shm.h>
  2. int shmget(key_t key, size_t size, int shmflg);
  3. void *shmat(int shm_id, const void *shm_addr, int shmflg);
  4. int shmdt(const void *shm_addr);
  5. int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
1. shmget函数
该函数用来创建共享内存:
  1. int shmget(key_t key, size_t size, int shmflg);
参数:
key : 和信号量一样,程序需要提供一个参数key,
      它有效地为共享内存段命名。
      
      有一个特殊的键值IPC_PRIVATE, 
      它用于创建一个只属于创建进程的共享内存,
      通常不会用到。
size: 以字节为单位指定需要共享的内存容量。
shmflag: 包含9个比特的权限标志,
         它们的作用与创建文件时使用的mode标志是一样。
         由IPC_CREAT定义的一个特殊比特必须和权限标志按位或
         才能创建一个新的共享内存段。
NOTE:
权限标志对共享内存非常有用,
因为它允许一个进程创建的共享内存可以被共享内存的创建者所拥有的进程写入,
同时其它用户创建的进程只能读取共享内存。
我们可以利用这个功能来提供一种有效的对数据进行只读访问的方法,
通过将数据放共享内存并设置它的权限,
就可以避免数据被其他用户修改。
返回值:
创建成功,则返回一个非负整数,即共享内存标识;
如果失败,则返回-1.
2. shmat函数
第一次创建共享内存段时,它不能被任何进程访问。
要想启动对该内存的访问,
必须将其连接到一个进程的地址空间。
这个工作由shmat函数完成:
  1. void *shmat(int shm_id, const void *shm_addr, int shmflg);
参数:
shm_id : 由shmget返回的共享内存标识。
shm_add: 指定共享内存连接到当前进程中的地址位置。
         它通常是一个空指针, 
         表示让系统来选择共享内存出现的地址。
shmflg : 是一组标志。
         它的两个可能取值是:
         SHM_RND, 和shm_add联合使用,
                  用来控制共享内存连接的地址。
         SHM_RDONLY, 它使连接的内存只读
         
返回值:
如果调用成功, 返回一个指向共享内存第一个字节的指针;
如果失败,返回-1.
共享内存的读写权限由它的属主(共享内存的创建者),
它的访问权限和当前进程的属主决定。
共享内存的访问权限类似于文件的访问权限。
3. shmdt
将共享内存从当前进程中分离。
  1. int shmdt(const void *shm_addr);
shm_addr: shmat返回的地址指针。
成功时,返回0,
失败时,返回-1.
NOTE:
共享内存分离并未删除它,
只是使得该共享内存对当前进程不再可用。
4. shmctl
共享内存的控制函数
  1. int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
shmid_ds结构至少包含以下成员:
  1. struct shmid_ds {
  2. uid_t shm_perm.uid;
  3. uid_t shm_perm.gid;
  4. mode_t shm_perm.mode;
  5. }
参数:
shm_id : 是shmget返回的共享内存标识符。
command: 是要采取的动作,
         它可以取3个值:
IPC_STAT  把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET   如果进程有足够的权限,
          就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID  删除共享内存段
buf    : 是一个指针,
         包含共享内存模式和访问权限的结构。
返回值:
成功时,返回0,
失败时,返回-1.
四、示例
典型的消费者-生产者程序,
第一个程序(消费者)将创建一个共享内存段,
然后把写到它里面的数据都显示出来。
第二个程序(生产者)将连接一个已有的共享内存段,
并允许我们向其中输入数据。
shm_com.h
  1. #define TEXT_SZ 2048
  2. struct shared_use_st {
  3. int written_by_you;
  4. char some_text[TEXT_SZ];
  5. };
当有数据写入这个结构中时,
我们用结构中的written_by_you标志来通知消费者。
需要传输的文本长度2K是随意定的。
shm1.c 消费者程序
  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <sys/shm.h>
  6. #include "shm_com.h"
  7. int main()
  8. {
  9. int running = 1;
  10. void *shared_memory = (void *)0;
  11. struct shared_use_st *shared_stuff;
  12. int shmid;
  13. srand((unsigned int)getpid());
  14. shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
  15. if (shmid == -1) {
  16. fprintf(stderr, "shmget failed\n");
  17. exit(EXIT_FAILURE);
  18. }
现在,让程序可以访问这个共享内存:
  1. shared_memory = shmat(shmid, (void *)0, 0);
  2. if (shared_memory == (void *)-1) {
  3. fprintf(stderr, "shmat failed\n");
  4. exit(EXIT_FAILURE);
  5. }
  6. printf("Memory attached at %X\n", (int)shared_memory);
程序的下一部分将shared_memory分配给shared_stuff,
然后它输出written_by_you中的文本。
循环将一直执行到在written_by_you中找到end字符串为止。
sleep调用强迫消费者程序在临界区域多待一会,
让生产者程序等待:
  1. shared_stuff = (struct shared_use_st *)shared_memory;
  2. shared_stuff->written_by_you = 0;
  3. while(running)
  4. {
  5. if (shared_stuff->written_by_you)
  6. {
  7. printf("You wrote: %s", shared_stuff->some_text);
  8. sleep( rand() % 4 ); /* make the other process wait for us ! */
  9. shared_stuff->written_by_you = 0;
  10. if (strncmp(shared_stuff->some_text, “end”, 3) == 0) {
  11. running = 0;
  12. }
  13. }
  14. }
最后,共享内存被分离,然后被删除:
  1. if (shmdt(shared_memory) == -1)
  2. {
  3. fprintf(stderr, "shmdt failed\n");
  4. exit(EXIT_FAILURE);
  5. }
  6. if (shmctl(shmid, IPC_RMID, 0) == -1)
  7. {
  8. fprintf(stderr, "shmctl(IPC_RMID) failed\n");
  9. exit(EXIT_FAILURE);
  10. }
  11. exit(EXIT_SUCCESS);
  12. }
shm2.c 生产者程序
通过它向消费者程序输入数据。
  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <sys/shm.h>
  6. #include "shm_com.h"
  7. int main()
  8. {
  9. int running = 1;
  10. void *shared_memory = (void *)0;
  11. struct shared_use_st *shared_stuff;
  12. char buffer[BUFSIZ];
  13. int shmid;
  14. shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
  15. if (shmid == -1)
  16. {
  17. fprintf(stderr, "shmget failed\n");
  18. exit(EXIT_FAILURE);
  19. }
  20. shared_memory = shmat(shmid, (void *)0, 0);
  21. if (shared_memory == (void *)-1)
  22. {
  23. fprintf(stderr, "shmat failed\n");
  24. exit(EXIT_FAILURE);
  25. }
  26. printf("Memory attached at %X\n", (int)shared_memory);
  27. shared_stuff = (struct shared_use_st *)shared_memory;
  28. while(running)
  29. {
  30. while(shared_stuff->written_by_you == 1)
  31. {
  32. sleep(1);
  33. printf("waiting for client...\n");
  34. }
  35. printf("Enter some text: ");
  36. fgets(buffer, BUFSIZ, stdin);
  37. strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
  38. shared_stuff->written_by_you = 1;
  39. if (strncmp(buffer, "end", 3) == 0) {
  40. running = 0;
  41. }
  42. }
  43. if (shmdt(shared_memory) == -1) {
  44. fprintf(stderr, "shmdt failed\n");
  45. exit(EXIT_FAILURE);
  46. }
  47. exit(EXIT_SUCCESS);
  48. }
运行程序,
将看到如下所示的样本输出:
  1. $ ./shm1 &
  2. [1] 294
  3. Memory attached at 40017000
  4. $ ./shm2
  5. Memory attached at 40017000
  6. Enter some text: hello
  7. You wrote: hello
  8. waiting for client...
  9. waiting for client...
  10. Enter some text:
  11. You wrote:
  12. waiting for client...
  13. waiting for client...
  14. waiting for client...
  15. Enter some text: end
  16. You wrote: end
  17. $
程序解析:
消费者程序
创建共享内存段,
然后将它连接到它自己的地址空间中,
并且,
我们在共享内存的开始处使用了一个结构shared_use_st.
该结构中有个标志written_by_you,
当共享内存中有数据写入时,就设置这个标志。
这个标志被设置时,
程序就从共享内存中读取文本,
将它打印出来,
然后清除这个标志,表示已经读完数据。
我们用一个特殊字符串end来退出循环。
接下来,
程序分离共享内存段并删除它。
生产者程序
使用相同的键值1234来取得并连接同一个共享内存段,
然后提示用户输入一些文本。
如果标志written_by_you被设置,
生产者就知道消费都进程还未读完上一次的数据,
因此就继续等待。
当其它进程清除了这个标志后,
生产者写入新的数据并设置这个标志。
它还使用字符串end来终止并分离共享内存段。
这里提供的同步标志written_by_you,
它是一个非常缺乏效率的忙等待(不停地循环)。
但在实际编程中,
应该使用信号量,
或通过传递消息(使用管道或IPC消息),
或生成信号
的方法来提供读写之间的更有效的同步机制。

linux进程间的通信(C): 共享内存相关推荐

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

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

  2. 深刻理解 Linux 进程间七大通信(IPC)

    前言 网络编程是 Linux C/C++的面试重点,今天我就来聊一聊进程间通信的问题,文章末尾列出了参考资料,希望帮助到大家. 篇幅有点长,希望大家耐心阅读. Linux 下的进程通信手段基本上是从 ...

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

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

  4. linux进程间的通信实验,linux实验进程之间的通信.doc

    院 系: 计算机学院 实验课程:linux内核设计与实现 实验项目:进程之间的通信 指导老师:冯刚 开课时间: 2010 - 2011 年度第 2 学期 专 业:计算机科学与技术(师范) 班 级:2班 ...

  5. linux共享内存示例,linux 进程间共享内存示例

    写入端: #include #include #include #include #include using namespace std; struct MappingDataType { int ...

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

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

  7. 进程间的通信——共享内存

    下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式 ...

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

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

  9. android 进程间的通信,Android native进程间通信实例-binder结合共享内存

    在android源码的驱动目录下,一般会有共享内存的相关实现源码,目录是:kerneldriversstagingandroidashmem.c.但是本篇文章不是讲解android共享内存的功能实现原 ...

最新文章

  1. php 预订义变量,预约义变量 PHP
  2. Shiro 核心功能案例讲解 基于SpringBoot 有源码
  3. Docker容器间通讯,直接路由方式实现网络通讯
  4. php 导出csv文件
  5. stm32 薄膜键盘原理_铅锤哥:市面上的笔记本键盘优缺点解析,看完秒懂
  6. 百度智呼吸 html5,手机百度 iBreath智呼吸
  7. app.config中的值获取及设置 以及对log4net配置
  8. 【图像分割】基于matlab GUI多种阈值图像分割(带面板)【含Matlab源码 733期】
  9. python模拟三次输入密码_python 3.0 模拟用户登录功能并实现三次错误锁定
  10. ngx_http_upstream_module模块学习笔记
  11. 挑战性题目DSCT103:客观指标评价问题
  12. Nmap扫描机器开放的端口
  13. ModifyStyle ModifyStyleEx修改自定义控件的问题
  14. P.W.N. CTF - MISC - Canadian FOI
  15. 漫威 DC 英雄综合实力排位,这人秒杀灭霸
  16. 知识图谱下图神经网络、图计算、图数据、图数据库未来发展趋势如何?
  17. 版号解禁,网络游戏却未解冻
  18. 股票/期货分仓系统都能实现什么功能?
  19. 结算系统业务监控实践
  20. 【洛谷P1255 数楼梯】

热门文章

  1. python 用while输出数字金字塔_用Python实现一个Dual Thrust数字货币量化交易策略
  2. 涡轮流量计说明书_实际应用中超声波流量计与电磁流量计哪个更好用
  3. 未发现oracle(tm)客户端和网络组件_SpringColud Eureka的服务注册与发现
  4. linux应用程序安装PPT免费序,linux下应用程序安装的总结
  5. 1043 输出PATest(PAT乙级 C++)
  6. 什么是节点光端机?总线型光端机有哪些优势?
  7. [渝粤教育] 郑州商学院 商学概论 参考 资料
  8. 【渝粤教育】国家开放大学2018年秋季 0434-22T高级英语口语 参考试题
  9. 【渝粤教育】广东开放大学 数据库原理与应用 形成性考核 (1)
  10. 【lora无线数传通信模块】亿佰特E22串口模块用于物联网地震预警传感通信方案