一、共享内存介绍
共享内存是三个IPC(Inter-Process Communication)机制中的一个。
它允许两个不相关的进程访问同一个逻辑内存。
共享内存是在两个正在进行的进程之间传递数据的一种非常有效的方式。
大多数的共享内存的实现,
都把由不同进程之间共享的内存安排为同一段物理内存。
 
共享内存是由IPC为进程创建一个特殊的地址范围,
它将出现在该进程的地址空间中。
其他进程可以将同一段共享内存连接它们自己的地址空间中。
所有进程都可以访问共享内存中的地址,
就好像它们是由malloc分配的一样。
 
如果某个进程向共享内存写入了数据,
所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。
 
二、共享内存的同步
共享内存为在多个进程之间共享和传递数据提供了一种有效的方式。
但是它并未提供同步机制,
所以我们通常需要用其他的机制来同步对共享内存的访问。
我们通常是用共享内存来提供对大块内存区域的有效访问,
同时通过传递小消息来同步对该内存的访问。
 
在第一个进程结束对共享内存的写操作之前,
并无自动的机制可以阻止第二个进程开始对它进行读取。
对共享内存访问的同步控制必须由程序员来负责。
 
下图显示了共享内存是如何共存的:
 
 
图中的箭头显示了每个进程的逻辑地址空间到可用物理内存的映射关系。
三、共享内存使用的函数
  1. #include <sys/shm.h>
  2.  
  3. int shmget(key_t key, size_t size, int shmflg);
  4. void *shmat(int shm_id, const void *shm_addr, int shmflg);
  5. int shmdt(const void *shm_addr);
  6. 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.
 
四、示例
典型的消费者-生产者程序,
第一个程序(消费者)将创建一个共享内存段,
然后把写到它里面的数据都显示出来。
第二个程序(生产者)将连接一个已有的共享内存段,
并允许我们向其中输入数据。
 
  1. shm_com.h
  1. #define TEXT_SZ 2048
  2.  
  3. struct shared_use_st {
  4. int written_by_you;
  5. char some_text[TEXT_SZ];
  6. };
 
当有数据写入这个结构中时,
我们用结构中的written_by_you标志来通知消费者。
需要传输的文本长度2K是随意定的。
 
shm1.c 消费者程序
  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5.  
  6. #include <sys/shm.h>
  7.  
  8. #include "shm_com.h"
  9. int main()
  10. {
  11. int running = ;
  12. void *shared_memory = (void *);
  13. struct shared_use_st *shared_stuff;
  14. int shmid;
  15.  
  16. srand((unsigned int)getpid());
  17. shmid = shmget((key_t), sizeof(struct shared_use_st), | IPC_CREAT);
  18.  
  19. if (shmid == -) {
  20. fprintf(stderr, "shmget failed\n");
  21. exit(EXIT_FAILURE);
  22. }

现在,让程序可以访问这个共享内存:

  1. shared_memory = shmat(shmid, (void *), );
  2.  
  3. if (shared_memory == (void *)-) {
  4. fprintf(stderr, "shmat failed\n");
  5. exit(EXIT_FAILURE);
  6. }
  7.  
  8. 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 = ;
  3.  
  4. while(running)
  5. {
  6. if (shared_stuff->written_by_you)
  7. {
  8. printf("You wrote: %s", shared_stuff->some_text);
  9.  
  10. sleep( rand() % ); /* make the other process wait for us ! */
  11. shared_stuff->written_by_you = ;
  12.  
  13. if (strncmp(shared_stuff->some_text, end”, ) == ) {
  14. running = ;
  15. }
  16. }
  17. }

最后,共享内存被分离,然后被删除:

  1. if (shmdt(shared_memory) == -)
  2. {
  3. fprintf(stderr, "shmdt failed\n");
  4. exit(EXIT_FAILURE);
  5. }
  6.  
  7. if (shmctl(shmid, IPC_RMID, ) == -)
  8. {
  9. fprintf(stderr, "shmctl(IPC_RMID) failed\n");
  10. exit(EXIT_FAILURE);
  11. }
  12.  
  13. exit(EXIT_SUCCESS);
  14. }
shm2.c 生产者程序
通过它向消费者程序输入数据。
  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5.  
  6. #include <sys/shm.h>
  7.  
  8. #include "shm_com.h"
  9.  
  10. int main()
  11. {
  12. int running = ;
  13. void *shared_memory = (void *);
  14. struct shared_use_st *shared_stuff;
  15. char buffer[BUFSIZ];
  16. int shmid;
  17.  
  18. shmid = shmget((key_t), sizeof(struct shared_use_st), | IPC_CREAT);
  19. if (shmid == -)
  20. {
  21. fprintf(stderr, "shmget failed\n");
  22. exit(EXIT_FAILURE);
  23. }
  24.  
  25. shared_memory = shmat(shmid, (void *), );
  26. if (shared_memory == (void *)-)
  27. {
  28. fprintf(stderr, "shmat failed\n");
  29. exit(EXIT_FAILURE);
  30. }
  31.  
  32. printf("Memory attached at %X\n", (int)shared_memory);
  33.  
  34. shared_stuff = (struct shared_use_st *)shared_memory;
  35. while(running)
  36. {
  37. while(shared_stuff->written_by_you == )
  38. {
  39. sleep();
  40. printf("waiting for client...\n");
  41. }
  42. printf("Enter some text: ");
  43. fgets(buffer, BUFSIZ, stdin);
  44.  
  45. strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
  46. shared_stuff->written_by_you = ;
  47.  
  48. if (strncmp(buffer, "end", ) == ) {
  49. running = ;
  50. }
  51. }
  52.  
  53. if (shmdt(shared_memory) == -) {
  54. fprintf(stderr, "shmdt failed\n");
  55. exit(EXIT_FAILURE);
  56. }
  57.  
  58. exit(EXIT_SUCCESS);
  59. }
运行程序,
将看到如下所示的样本输出:
  1. $ ./shm1 &
  2. []
  3. Memory attached at
  4. $ ./shm2
  5. Memory attached at
  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进程间的通信之 共享内存的更多相关文章

  1. Linux进程间的通信

    一.管道 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: A. 管道是半双工的,数据只能向一个方向流动: B. 需要双工通信时,需要建立起两个管道: C. 只能用于父子进程或者兄弟 ...

  2. PHP与Linux进程间的通信

    进程间通信预计是公司考察应届毕业生的必考点(嵌入式行业).当然非常多公司考的是算法. 不查阅资料,我脑子里能想到的 [1] 管道, (有名.无名) [2] 父子进程 [3] System V (消息队 ...

  3. Linux进程IPC浅析[进程间通信SystemV共享内存]

    Linux进程IPC浅析[进程间通信SystemV共享内存] 共享内存概念,概述 共享内存的相关函数 共享内存概念,概述: 共享内存区域是被多个进程共享的一部分物理内存 多个进程都可把该共享内存映射到 ...

  4. [转]WINDOW进程间数据通讯以及共享内存

    1.引言 在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯.WIN32 API提供了许多函数使我们能够方便高效地进行进程间的通讯,通过这些函数我们可以控制不同进程间的数据交换,就如同 ...

  5. linux进程间的通信方式

    linux进程间的通信 进程间的通信就是不同的进程之间传播或交换信息,进程的用户空间是互相独立,进程之间可以利用系统空间交换信息. 管道 允许将一个进程的标准输出和另一个进程的标准输入连接在一起,主要 ...

  6. linux 进程通信之 共享内存

    共享内存是被多个进程共享的一部分物理内存.共享内存是进程间共享数据的一种最快的方法.一个进程向共享内存区域写入了数据,共享这个内存区域的全部进程就能够立马看到当中的内容. 关于共享内存使用的API k ...

  7. Windows中利用共享内存来实现不同进程间的通信

    Windows中利用共享内存来实现不同进程间的通信 一.msdn详细介绍 https://docs.microsoft.com/zh-cn/windows/win32/memory/sharing-f ...

  8. Nginx之进程间的通信机制(共享内存、原子操作)

    1. 概述 Linux 提供了多种进程间传递消息的方式,如共享内存.套接字.管道.消息队列.信号等,而 Nginx 框架使用了 3 种传递消息的传递方式:共享内存.套接字.信号. 在进程间访问共享资源 ...

  9. Linux 进程通信(共享内存区)

    共享内存是由内核出于在多个进程间交换信息的目的而留出的一块内存区(段). 如果段的权限设置恰当,每个要访问该段内存的进程都可以把它映像到自己的私有地址空间中. 如果一个进程更新了段中的数据,其他进程也 ...

随机推荐

  1. CF 671D Roads in Yusland

    弄完之后点进去一看,竟然是div1的D题……最近真是天天被题虐哭 推荐这一篇博客 https://www.cnblogs.com/Sakits/p/8085598.html 感觉讲清楚了,也是基本照着 ...

  2. Luogu 2322 [HNOI2006]最短母串问题

    唔,太菜了,弄了好几个小时. 状压dp,设$f_{s, i}$表示选了集合$s$,以$i$结尾的最短长度,设$g_{i, j}$表示串$i$的后缀和串$j$的前缀的最长匹配长度. $f_{s, i} ...

  3. 几种jar转exe方法的比较

    原摘自:https://blog.csdn.net/uikoo9/article/details/7458666 几种jar转exe方法的比较 2012年04月13日 17:33:07 阅读数:153 ...

  4. 如何把VS2015中本地的一个项目建立远程的Git Repository

    在项目开发中,我在本地自己电脑上用VS2015建立了一个项目,比如项目名字叫做Luke.Test 那么,接下来,我如何把这个项目签入到远程的Git Repository里去呢. 方法如下 先进入远程R ...

  5. url-pattern 的设置与匹配

  6. [C# 线程处理系列]专题四:线程同步

    目录: 一.线程同步概述 二.线程同步的使用 三 .总结 一.线程同步概述 前面的文章都是讲创建多线程来实现让我们能够更好的响应应用程序,然而当我们创建了多个线程时,就存在多个线程同时访问一个共享的资 ...

  7. 算法训练 最大的算式(DP)

    问题描述 题目很简单,给出N个数字,不改变它们的相对位置,在中间加入K个乘号和N-K-1个加号,(括号随便加)使最终结果尽量大.因为乘号和加号一共就是N-1个了,所以恰好每两个相邻数字之间都有一个符号 ...

  8. pandas-如何得到某一个值所在的行

    df[df['列名'].isin([相应的值])]

  9. forEach,for in,for of循环的用法

    一.一般的遍历数组的方法: var array = [1,2,3,4,5,6,7]; for (var i = 0; i < array.length; i) { console.log(i,a ...

  10. 51nod1478(yy)

    题目链接: http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1478&judgeId=365133 题意: 中文题诶 ...