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

一、共享内存介绍
共享内存是三个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消息),
或生成信号
的方法来提供读写之间的更有效的同步机制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/350955.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用js在桌面上写一个倒计时器_论一个倒计时器的性能优化之路

原文发表于 2018.05.25&#xff0c;搬运自个人博客。引子回顾这半年&#xff0c;扛需求能力越来越强&#xff0c;业务代码也是越写越多。但稍一认真看看这些当时为了满足快速上线所码的东西&#xff0c;问题其实还是不少。这次就从一个简单的计时器说起。现状问题很明显倒计时器…

Vue 导入文件import、路径@和.的区别

***import&#xff1a; html文件中&#xff0c;通过script标签引入js文件。而vue中&#xff0c;通过import xxx from xxx路径的方式导入文件&#xff0c;不光可以导入js文件。 from前的&#xff1a;“xxx”指的是为导入的文件起一个名称&#xff0c;不是指导入的文件的名称&…

python split()

python函数&#xff1a;split() Python中有split()和os.path.split()两个函数&#xff0c;具体作用如下&#xff1a; split()&#xff1a;拆分字符串。通过指定分隔符对字符串进行切片&#xff0c;并返回分割后的字符串列表&#xff08;list&#xff09; os.pa…

windows聚焦图片为什么不更新了_为什么年轻明星都不愿意接周星驰的戏? 林更新道出了事情的真相|周星驰|林更新|喜剧之王|演员...

要说华语电影史上最有成就的喜剧之王&#xff0c;那么大家脑中一定会闪过周星驰的名字&#xff0c;相信在不少60后、70后心目中&#xff0c;都有周星驰的一席之地。《大话西游》、《逃学威龙》、《九品芝麻官》等一系列作品都是非常经典的作品&#xff0c;就算是拿到当下来看都…

多线程多进程解析:Python、os、sys、Queue、multiprocessing、threading

当涉及到操作系统的时候&#xff0c;免不了要使用os模块&#xff0c;有时还要用到sys模块。 设计到并行程序&#xff0c;一般开单独的进程&#xff0c;而不是线程&#xff0c;原因是python解释器的全局解释器锁GIL&#xff08;global interpreter lock&#xff09;&#xff0c;…

练习1

方法一&#xff1a; 统计在一个队列中的数字&#xff0c;有多少个正数&#xff0c;多少个负数&#xff0c;如[1, 3, 5, 7, 0, -1, -9, -4, -5, 8] lists [1, 3, 5, 7, 0, -1, -9, -4, -5, 8]positive 0 negative 0for number in lists:if number > 0:positive 1elif nu…

python sort()、sorted()

python sort、sorted排序 这篇文章主要介绍了python sort、sorted高级排序技巧,本文讲解了基础排序、升序和降序、排序的稳定性和复杂排序、cmp函数排序法等内容. python list内置sort()方法用来排序&#xff0c;也可以用python内置的全局sorted()方法来对可迭代的序列排…

电脑内存占用莫名很高_CPU占用高,电脑莫名卡顿?万能的重启拯救不了就用这3招,妥了!...

大家还记得windows 10 1903推送时发生的大翻车事件吗&#xff1f;那次的更新主要是修复早期版本的Visual Basic 6、VBA和VBScript无反应、远端桌面出现当机黑屏幕等问题&#xff0c;但win10的更新总是“捡了芝麻&#xff0c;丢了西瓜”&#xff0c;解决早期问题而又出现新的问题…

5. 多线程程序如何让 IO 和“计算”相互重叠,降低 latency?

基本思路是&#xff0c;把 IO 操作&#xff08;通常是写操作&#xff09;通过 BlockingQueue 交给别的线程去做&#xff0c;自己不必等待。 例1: logging 在多线程服务器程序中&#xff0c;日志 (logging) 至关重要&#xff0c;本例仅考虑写 log file 的情况&#xff0c;不考…

tomcat web应用_具有可执行Tomcat的独立Web应用程序

tomcat web应用在部署应用程序时&#xff0c;简单性是最大的优势。 您将了解到&#xff0c;尤其是在项目发展且需要在环境中进行某些更改时。 将您的整个应用程序打包到一个独立且自足的JAR中似乎是个好主意&#xff0c;特别是与在目标环境中安装和升级Tomcat相比。 过去&#…

anaconda在ubuntu中添加环境变量

在ubuntu上安装好anaconda后&#xff0c;如果输入conda命令报错&#xff0c;很有可能需要以下修改注册表&#xff08;相当于windows中将路径添加到系统环境变量&#xff09; ~ /anaconda3/bin为.Sh所在home目录路径 在终端输入&#xff1a;sudo gedit ~/.bashrc 打开注册表后…

webpackjsonp 还原_具有催化CO2还原性能的非贵金属配合物的配体设计

Non-noble metal-based molecular complexes for CO2 reduction: From the ligand design perspectiveDong-Cheng Liu, Di-Chang Zhong, Tong-Bu LuEneryChem, 2, 100034 (2020).DOI: https://doi.org/10.1016/j.enchem.2020.100034全文链接https://www.sciencedirect.com/jour…

【数据库系统概论】第3章-关系数据库标准语言SQL(1)

文章目录 3.1 SQL概述3.2 学生-课程数据库3.3 数据定义3.3.1 数据库定义3.3.2 模式的定义3.3.3 基本表的定义3.3.4 索引的建立与删除3.3.5 数据字典 3.1 SQL概述 动词 分类 三级模式 3.2 学生-课程数据库 3.3 数据定义 3.3.1 数据库定义 创建数据库 tips&#xff1a;[ ]表…

适用于Java开发人员的Elasticsearch教程

课程大纲 Elasticsearch是基于Lucene的搜索引擎。 它提供了具有HTTP Web界面和无模式JSON文档的分布式多租户全文搜索引擎。 Elasticsearch是用Java开发的&#xff0c;并根据Apache许可的条款作为开源发布。 Elasticsearch是最受欢迎的企业搜索引擎&#xff0c;紧随其后的也是基…

shell实战之tomcat看门狗

1、脚本简介 tomcat看门狗&#xff0c;在tomcat进程异常退出时会自动拉起tomcat进程并记录tomcat运行的日志。 1 函数说明&#xff1a; 2 log_info&#xff1a;打印日志的函数&#xff0c;入参为需要在日志中打印的msg 3 start_tom&#xff1a;启动tomcat的函数…

tensorflow tf.train.batch()

tf.train.batch([example, label],batch_sizebatch_size, capacitycapacity) [example, label]表示样本和样本标签&#xff0c;这个可以是一个样本和一个样本标签&#xff0c;batch_size是返回的一个batch样本集的样本个数。capacity是队列中的容量。这主要是按顺序组合成一个b…

苹果6s上市时间_iPhone7的A10处理器还能战多长时间?2-3年不成问题!

iPhone 7采用A10 Fusion处理器&#xff0c;简称A10处理器&#xff0c;在2018年依然是处于高端处理器&#xff0c;再加上苹果自己的系统优化和资源调度&#xff0c;流畅度甚至超过其他安卓835机子。16年上市的iPhone7的A10还能再战多长时间&#xff1f;小编今天来分析一下。A10处…

tf.summary.FileWriter

ummary_waiter tf.summary.FileWriter("log",tf.get_default_graph()) log是事件文件所在的目录&#xff0c;这里是工程目录下的log目录。第二个参数是事件文件要记录的图&#xff0c;也就是tensorflow默认的图。

83998 连接服务器出错_服务端 TCP 连接的 TIME_WAIT 问题分析与解决

民工哥技术之路 写在开头&#xff0c;大概 4 年前&#xff0c;听到运维同学提到 TIME_WAIT 状态的 TCP 连接过多的问题&#xff0c;但是当时没有去细琢磨&#xff1b;最近又听人说起&#xff0c;是一个新手进行压测过程中&#xff0c;遇到的问题&#xff0c;因此&#xff0c;花…

SqlServer 时间格式化

select GETDATE() as 当前日期, DateName(year,GetDate()) as 年,DateName(month,GetDate()) as 月,DateName(day,GetDate()) as 日,DateName(dw,GetDate()) as 星期,DateName(week,GetDate()) as 周数,DateName(hour,GetDate()) as 时,DateName(minute,GetDate()) as 分,DateN…