2015-03-24 15:52:27 chinawangfei 阅读数 415
  • Linux系统编程第05期:进程间通信

    Linux系统提供了各种系统调用API用于进程之间的通信: *     无名管道PIPE *     命名管道FIFO *     消息队列 *     共享内存 *     信号量 *     文件锁 *     信号signal.... 其中还包括system V和POSIX 两种接口标准,除此之外,Linux系统自身还扩展了自己的一套API接口用于进程间通信,比如signalfd、timerfd、eventfd等。 本视频教程为《Linux系统编程》第05期,本期课程将会带领大家学习Linux下将近15种进程间通信IPC工具的使用,了解它们的通信机制、编程实例、使用场景、内核中的实现以及各自的优缺点。 本课程会提供PDF版本的PPT课件和代码,学员购买课程后可到课程主页自行下载 嵌入式自学路线指导图: ------------------------------------------------------------------------------------------------------                    《嵌入式工程师自我修养》嵌入式自学系列教程                                           作者:王利涛 ------------------------------------------------------------------------------------------------------ 一线嵌入式工程师精心打造,嵌入式学习路线六步走:  第 1 步:Linux三剑客 * 零基础玩转Linux+Ubuntu * Git零基础实战:Linux开发技能标配 * vim从入门到精通基础篇:零基础学习vim基本命令 * vim从入门到精通定制篇:使用插件打造嵌入式开发IDE * makefile工程实践基础篇:从零开始一步一步写项目的Makefile * makefile工程实践第2季:使用Autotools自动生成Makefile * 软件调试基础理论 * printf打印技巧 * Linux内核日志与打印 * 使用QEMU搭建u-boot+Linux+NFS嵌入式开发环境 第 2 步:C语言嵌入式Linux高级编程 * 第1期:C语言进阶学习路线指南 * 第2期:计算机架构与ARM汇编程序设计 * 第3期:程序的编译、链接和运行原理 * 第4期:堆栈内存管理 * 第6期:数据存储与指针 * 第7期:嵌入式数据结构与Linux内核的OOP思想 * 第8期:C语言的模块化编程 * 第9期:CPU和操作系统入门       搞内核驱动开发、光会C语言是不行的!       你还需要学习的有很多,包括:计算机体系架构、ARM汇编、程序的编译链接运行原理、CPU和操作系统原理、堆栈内存管理、指针、linux内核中的面向对象思想、嵌入式系统架构、C语言的模块化编程..... 第 3 步:Linux系统编程 * 第00期:Linux系统编程入门 * 第01期:揭开文件系统的神秘面纱 * 第02期:文件I/O编程实战 * 第03期:I/O缓存与内存映射 * 第04期:打通进程与终端的任督二脉 * 第05期:进程间通信-------------------we are here!‍      第 4 步:Linux内核编程‍     练乾坤大挪移,会不会九阳神功,是一道坎。搞驱动内核开发,懂不懂内核也是一道坎。 第 5 步:嵌入式驱动开发     芯片原理、datasheet、硬件电路、调试手段、总线协议、内核机制、框架流程.... 第 6 步:项目实战     嵌入式、嵌入式人工智能、物联网、智能家居...

    379 人正在学习 去看看 王利涛

一、在linux中使用共享内存的目的:
 共享内存是进程间通信中最简单的方式之一。
 共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。
 当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。


二、使用共享内存的流程:
 1.首先,进程必须分配它。
 2.然后,需要访问这个共享内存块的每一个进程都必须将这个共享内存绑定到自己的地址空间中。
 3.当完成通信之后,所有进程都将脱离共享内存,并且由一个进程释放该共享内存块。 


三、共享内存的分配方法:
 int segment_id = shmget (shm_key, int size , shmflag ); 
进程通过调用shmget(“Shared Memory GET”的缩写,表示获取共享内存)分配一个共享内存块

  1.该函数的第一个参数是一个用来标识共享内存块键值
 彼此无关的进程可以通过指定同一个键以获取对同一个共享内存块的访问。 不幸的是,其它程序也可能挑选了同样的特定值作为自己分配共享内存的键值,从而产生冲突。 用特殊常量IPC_PRIVATE作为键值可以保证系统建立一个全新的共享内存块。 
 2.该函数的第二个参数指定了所申请的内存块的大小
 因为这些内存块是以页面为单位进行分配的,实际分配的内存块大小将被扩大到页面大小的整数倍
 3.第三个参数是一组标志,通过特定常量按位或操作来shmget。这些特定常量包括:
 IPC_CREAT:
  这个标志表示应创建一个新的共享内存块。通过指定这个标志,我们可以创建一个具有指定键值的新共享内存块。 
 IPC_EXCL:
  这个标志只能与 IPC_CREAT 同时使用。当指定这个标志的时候,如果已有一个具有这个键值的共享内存块存在,则shmget会调用失败。  也就是说,这个标志将使线程获得一个“独有”的共享内存块。如果没有指定这个标志,而系统中存在一个具有相同键值的共享内存块,  shmget会返回这个已经建立的共享内存块,而不是重新创建一个。 
 模式标志:
  这个值由9个位组成,分别表示属主、属组和其它用户对该内存块的访问权限。其中表示执行权限的位将被忽略。 指明访问权限的一个简单办法是利用<sys/stat.h>中指定,并且在手册页第二节stat条目中说明了的常量指定。
  例如:
   S_IRUSR和S_IWUSR分别指定了该内存块属主的读写权限,
   S_IROTH和S_IWOTH则指定了其它用户的读写权限。 


四、共享内存的绑定和脱离:
 pst= shmat(iShm_id, NULL, 0);
 一个进程获取对一块共享内存的访问,这个进程必须先调用 shmat(“SHared Memory Attach”的缩写,表示绑定到共享内存)
 1.第一个参数是 shmget 返回的共享内存标识符 SHMID
 2. 第二个参数是一个指针,指向您希望用于映射该共享内存块的进程内存地址;如果您指定NULL,则Linux会自动选择一个合适的地址用于映射。
 3.第三个参数是一个标志位,包含了以下选项:   
  SHM_RND
   表示第二个参数指定的地址应被向下靠拢到内存页面大小的整数倍。   如果您不指定这个标志,您将不得不在调用shmat的时候手工将共享内存块的大小按页面大小对齐。 
  SHM_RDONLY
   表示这个内存块将仅允许读取操作而禁止写入。 

如果这个函数调用成功,则会返回绑定的共享内存块对应的地址


    通过 fork 函数创建的子进程同时继承这些共享内存块;  如果需要,它们可以主动脱离这些共享内存块。 
    当一个进程不再使用一个共享内存块的时候应调用 shmdt(“Shared Memory Detach”的缩写,表示脱离共享内存块) 函数与该共享内存块脱离,将由 shmat 函数返回的地址传递给这个函数。
   如果释放这个内存块的进程是最后一个使用该内存块的进程,则这个内存块将被删除。 对 exit 或任何exec族函数的调用都会自动使进程脱离共享内存块 但不会删除这个内存块。


五、控制和释放共享内存块:
 shmctl(iShm_id,IPC_RMID,0)
<0
 调用 shmctl("Shared Memory Control"的缩写,表示控制共享内存)函数会返回一个共享内存块的相关信息。同时 shmctl 允许程序修改这些信息。
1. 该函数的第一个参数是一个共享内存块标识。

2.要获取一个共享内存块的相关信息,则为该函数传递 IPC_STAT 作为第二个参数,同时传递一个指向一个 struct shmid_ds 对象的指针作为第三个参数。
3.要删除一个共享内存块,则应将 IPC_RMID 作为第二个参数,而将 NULL 作为第三个参数。当最后一个绑定该共享内存块的进程与其脱离时,该共享内存块将被删除。
   应当在结束使用每个共享内存块的时候都使用 shmctl 进行释放,以防止超过系统所允许的共享内存块的总数限制。调用 exit 和 exec 会使进程脱离共享内存块, 但不会删除这个内存块。 要查看其它有关共享内存块的操作的描述,请参考shmctl函数的手册页。


六、修改共享内存:

共享内存的总体大小是有限制的,这个大小通过SHMMAX参数来定义(以字节为单位),您可以通过执行以下命令来确定 SHMMAX 的值:
cat /proc/sys/kernel/shmmax
 1.设置 SHMMAX
 # >echo "2147483648" > /proc/sys/kernel/shmmax
2. 您还可以使用 sysctl 命令来更改 SHMMAX 的值:
 # sysctl -w kernel.shmmax=2147483648
3.最后,通过将该内核参数插入到 /etc/sysctl.conf 启动文件中,您可以使这种更改永久有效:
 # echo "kernel.shmmax=2147483648" >> /etc/sysctl.conf
====================================================================

/*********************************************************
 *  filename: ipc.c
 *  author:bwf
 *  date: 20150325
*********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/wait.h>

int main(int argc,char *argv[])
{
 key_t shmid;
 char *p_addr,*c_addr;
 pid_t pid;
 
 if(argc != 2)
 {
  printf("Usage:%s something\n",argv[0]);
  exit(0);
 }
 
 if((shmid = shmget(IPC_PRIVATE,1024,S_IRUSR|S_IWUSR)) == -1)
 {
  printf("create share memory error!\n");
  exit(1);
 }
 else
 {
  printf("create share memory successfully!\n");
 }
 
 pid = fork();
 if(pid > 0 )
 {
   printf("parent shmat!\n");
  p_addr = (char *)shmat(shmid,0,0);
  printf("parent write data to shmem!\n");
  memset(p_addr,'\0',1024);
  strncpy(p_addr,argv[1],1024);
  wait(NULL);
   if( shmdt( p_addr) == -1 )
  {
perror("parent detach error \n");
//return -1;
  }
  else
  {
  printf("parent detach successfully!\n");
  }
 }
 else if (pid == 0)
 {
  sleep(1);
   printf("son shmat!\n");
  c_addr = (char *)shmat(shmid,0,0);
  printf("son get %s from shmem!\n",c_addr);
   if( shmdt( c_addr) == -1 )
  {
perror("son detach error \n");
return -1;
  }
  else
 {
  printf("son detach successfully!\n");
 }
  exit(0);
 }
 
 if(shmctl(shmid,IPC_RMID,0)<0)
 {
  printf("remove share memory error!!\n");
  return(-1);
 }
 else
 {  
  printf("remove share memory successfully!\n"); 
return 0;
 }
}

2015-08-05 22:34:46 u011244446 阅读数 243
  • Linux系统编程第05期:进程间通信

    Linux系统提供了各种系统调用API用于进程之间的通信: *     无名管道PIPE *     命名管道FIFO *     消息队列 *     共享内存 *     信号量 *     文件锁 *     信号signal.... 其中还包括system V和POSIX 两种接口标准,除此之外,Linux系统自身还扩展了自己的一套API接口用于进程间通信,比如signalfd、timerfd、eventfd等。 本视频教程为《Linux系统编程》第05期,本期课程将会带领大家学习Linux下将近15种进程间通信IPC工具的使用,了解它们的通信机制、编程实例、使用场景、内核中的实现以及各自的优缺点。 本课程会提供PDF版本的PPT课件和代码,学员购买课程后可到课程主页自行下载 嵌入式自学路线指导图: ------------------------------------------------------------------------------------------------------                    《嵌入式工程师自我修养》嵌入式自学系列教程                                           作者:王利涛 ------------------------------------------------------------------------------------------------------ 一线嵌入式工程师精心打造,嵌入式学习路线六步走:  第 1 步:Linux三剑客 * 零基础玩转Linux+Ubuntu * Git零基础实战:Linux开发技能标配 * vim从入门到精通基础篇:零基础学习vim基本命令 * vim从入门到精通定制篇:使用插件打造嵌入式开发IDE * makefile工程实践基础篇:从零开始一步一步写项目的Makefile * makefile工程实践第2季:使用Autotools自动生成Makefile * 软件调试基础理论 * printf打印技巧 * Linux内核日志与打印 * 使用QEMU搭建u-boot+Linux+NFS嵌入式开发环境 第 2 步:C语言嵌入式Linux高级编程 * 第1期:C语言进阶学习路线指南 * 第2期:计算机架构与ARM汇编程序设计 * 第3期:程序的编译、链接和运行原理 * 第4期:堆栈内存管理 * 第6期:数据存储与指针 * 第7期:嵌入式数据结构与Linux内核的OOP思想 * 第8期:C语言的模块化编程 * 第9期:CPU和操作系统入门       搞内核驱动开发、光会C语言是不行的!       你还需要学习的有很多,包括:计算机体系架构、ARM汇编、程序的编译链接运行原理、CPU和操作系统原理、堆栈内存管理、指针、linux内核中的面向对象思想、嵌入式系统架构、C语言的模块化编程..... 第 3 步:Linux系统编程 * 第00期:Linux系统编程入门 * 第01期:揭开文件系统的神秘面纱 * 第02期:文件I/O编程实战 * 第03期:I/O缓存与内存映射 * 第04期:打通进程与终端的任督二脉 * 第05期:进程间通信-------------------we are here!‍      第 4 步:Linux内核编程‍     练乾坤大挪移,会不会九阳神功,是一道坎。搞驱动内核开发,懂不懂内核也是一道坎。 第 5 步:嵌入式驱动开发     芯片原理、datasheet、硬件电路、调试手段、总线协议、内核机制、框架流程.... 第 6 步:项目实战     嵌入式、嵌入式人工智能、物联网、智能家居...

    379 人正在学习 去看看 王利涛
下面将讲解进程间通信的另一种方式,使用共享内存。

一、什么是共享内存
顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如前面说到的信号量。有关信号量的更多内容,可以查阅我的另一篇文章:Linux进程间通信——使用信号量

二、共享内存的使得
与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单。它们声明在头文件 sys/shm.h中。

1、shmget函数
该函数用来创建共享内存,它的原型为:
  1. int shmget(key_t key, size_t size, int shmflg);  
第一个参数,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.

不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某个资源,程序对所有共享内存的访问都是间接的,程序先通过调用shmget函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget函数的返回值),只有shmget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。

第二个参数,size以字节为单位指定需要共享的内存容量

第三个参数,shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。

2、shmat函数
第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。它的原型如下:
  1. void *shmat(int shm_id, const void *shm_addr, int shmflg);  
第一个参数,shm_id是由shmget函数返回的共享内存标识。
第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
第三个参数,shm_flg是一组标志位,通常为0。

调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.

3、shmdt函数
该函数用于将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。它的原型如下:
  1. int shmdt(const void *shmaddr);  
参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1.

4、shmctl函数
与信号量的semctl函数一样,用来控制共享内存,它的原型如下:
  1. int shmctl(int shm_id, int command, struct shmid_ds *buf);  
第一个参数,shm_id是shmget函数返回的共享内存标识符。

第二个参数,command是要采取的操作,它可以取下面的三个值 :
    IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
    IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
    IPC_RMID:删除共享内存段

第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。
shmid_ds结构至少包括以下成员:
  1. struct shmid_ds  
  2. {  
  3.     uid_t shm_perm.uid;  
  4.     uid_t shm_perm.gid;  
  5.     mode_t shm_perm.mode;  
  6. };  

三、使用共享内存进行进程间通信
说了这么多,又到了实战的时候了。下面就以两个不相关的进程来说明进程间如何通过共享内存来进行通信。其中一个文件shmread.c创建共享内存,并读取其中的信息,另一个文件shmwrite.c向共享内存中写入数据。为了方便操作和数据结构的统一,为这两个文件定义了相同的数据结构,定义在文件shmdata.c中。结构shared_use_st中的written作为一个可读或可写的标志,非0:表示可读,0表示可写,text则是内存中的文件。

shmdata.c的源代码如下:
  1. #ifndef _SHMDATA_H_HEADER  
  2. #define _SHMDATA_H_HEADER  
  3.   
  4. #define TEXT_SZ 2048  
  5.   
  6. struct shared_use_st  
  7. {  
  8.     int written;//作为一个标志,非0:表示可读,0表示可写  
  9.     char text[TEXT_SZ];//记录写入和读取的文本  
  10. };  
  11.   
  12. #endif  
源文件shmread.c的源代码如下:
  1. #include <unistd.h>  
  2. #include <stdlib.h>  
  3. #include <stdio.h>  
  4. #include <sys/shm.h>  
  5. #include "shmdata.h"  
  6.   
  7. int main()  
  8. {  
  9.     int running = 1;//程序是否继续运行的标志  
  10.     void *shm = NULL;//分配的共享内存的原始首地址  
  11.     struct shared_use_st *shared;//指向shm  
  12.     int shmid;//共享内存标识符  
  13.     //创建共享内存  
  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.     //将共享内存连接到当前进程的地址空间  
  21.     shm = shmat(shmid, 0, 0);  
  22.     if(shm == (void*)-1)  
  23.     {  
  24.         fprintf(stderr, "shmat failed\n");  
  25.         exit(EXIT_FAILURE);  
  26.     }  
  27.     printf("\nMemory attached at %X\n", (int)shm);  
  28.     //设置共享内存  
  29.     shared = (struct shared_use_st*)shm;  
  30.     shared->written = 0;  
  31.     while(running)//读取共享内存中的数据  
  32.     {  
  33.         //没有进程向共享内存定数据有数据可读取  
  34.         if(shared->written != 0)  
  35.         {  
  36.             printf("You wrote: %s", shared->text);  
  37.             sleep(rand() % 3);  
  38.             //读取完数据,设置written使共享内存段可写  
  39.             shared->written = 0;  
  40.             //输入了end,退出循环(程序)  
  41.             if(strncmp(shared->text, "end", 3) == 0)  
  42.                 running = 0;  
  43.         }  
  44.         else//有其他进程在写数据,不能读取数据  
  45.             sleep(1);  
  46.     }  
  47.     //把共享内存从当前进程中分离  
  48.     if(shmdt(shm) == -1)  
  49.     {  
  50.         fprintf(stderr, "shmdt failed\n");  
  51.         exit(EXIT_FAILURE);  
  52.     }  
  53.     //删除共享内存  
  54.     if(shmctl(shmid, IPC_RMID, 0) == -1)  
  55.     {  
  56.         fprintf(stderr, "shmctl(IPC_RMID) failed\n");  
  57.         exit(EXIT_FAILURE);  
  58.     }  
  59.     exit(EXIT_SUCCESS);  
  60. }  
源文件shmwrite.c的源代码如下:
  1. #include <unistd.h>  
  2. #include <stdlib.h>  
  3. #include <stdio.h>  
  4. #include <string.h>  
  5. #include <sys/shm.h>  
  6. #include "shmdata.h"  
  7.   
  8. int main()  
  9. {  
  10.     int running = 1;  
  11.     void *shm = NULL;  
  12.     struct shared_use_st *shared = NULL;  
  13.     char buffer[BUFSIZ + 1];//用于保存输入的文本  
  14.     int shmid;  
  15.     //创建共享内存  
  16.     shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);  
  17.     if(shmid == -1)  
  18.     {  
  19.         fprintf(stderr, "shmget failed\n");  
  20.         exit(EXIT_FAILURE);  
  21.     }  
  22.     //将共享内存连接到当前进程的地址空间  
  23.     shm = shmat(shmid, (void*)0, 0);  
  24.     if(shm == (void*)-1)  
  25.     {  
  26.         fprintf(stderr, "shmat failed\n");  
  27.         exit(EXIT_FAILURE);  
  28.     }  
  29.     printf("Memory attached at %X\n", (int)shm);  
  30.     //设置共享内存  
  31.     shared = (struct shared_use_st*)shm;  
  32.     while(running)//向共享内存中写数据  
  33.     {  
  34.         //数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本  
  35.         while(shared->written == 1)  
  36.         {  
  37.             sleep(1);  
  38.             printf("Waiting...\n");  
  39.         }  
  40.         //向共享内存中写入数据  
  41.         printf("Enter some text: ");  
  42.         fgets(buffer, BUFSIZ, stdin);  
  43.         strncpy(shared->text, buffer, TEXT_SZ);  
  44.         //写完数据,设置written使共享内存段可读  
  45.         shared->written = 1;  
  46.         //输入了end,退出循环(程序)  
  47.         if(strncmp(buffer, "end", 3) == 0)  
  48.             running = 0;  
  49.     }  
  50.     //把共享内存从当前进程中分离  
  51.     if(shmdt(shm) == -1)  
  52.     {  
  53.         fprintf(stderr, "shmdt failed\n");  
  54.         exit(EXIT_FAILURE);  
  55.     }  
  56.     sleep(2);  
  57.     exit(EXIT_SUCCESS);  
  58. }  
再来看看运行的结果:



分析:
1、程序shmread创建共享内存,然后将它连接到自己的地址空间。在共享内存的开始处使用了一个结构struct_use_st。该结构中有个标志written,当共享内存中有其他进程向它写入数据时,共享内存中的written被设置为0,程序等待。当它不为0时,表示没有进程对共享内存写入数据,程序就从共享内存中读取数据并输出,然后重置设置共享内存中的written为0,即让其可被shmwrite进程写入数据。

2、程序shmwrite取得共享内存并连接到自己的地址空间中。检查共享内存中的written,是否为0,若不是,表示共享内存中的数据还没有被完,则等待其他进程读取完成,并提示用户等待。若共享内存的written为0,表示没有其他进程对共享内存进行读取,则提示用户输入文本,并再次设置共享内存中的written为1,表示写完成,其他进程可对共享内存进行读操作。

四、关于前面的例子的安全性讨论
这个程序是不安全的,当有多个程序同时向共享内存中读写数据时,问题就会出现。可能你会认为,可以改变一下written的使用方式,例如,只有当written为0时进程才可以向共享内存写入数据,而当一个进程只有在written不为0时才能对其进行读取,同时把written进行加1操作,读取完后进行减1操作。这就有点像文件锁中的读写锁的功能。咋看之下,它似乎能行得通。但是这都不是原子操作,所以这种做法是行不能的。试想当written为0时,如果有两个进程同时访问共享内存,它们就会发现written为0,于是两个进程都对其进行写操作,显然不行。当written为1时,有两个进程同时对共享内存进行读操作时也是如些,当这两个进程都读取完是,written就变成了-1.

要想让程序安全地执行,就要有一种进程同步的进制,保证在进入临界区的操作是原子操作。例如,可以使用前面所讲的信号量来进行进程的同步。因为信号量的操作都是原子性的。

五、使用共享内存的优缺点
1、优点:我们可以看到使用共享内存进行进程间的通信真的是非常方便,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。

2、缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。

版权声明:本文为博主原创文章,未经博主允许不得转载。

2014-07-28 17:02:32 xpmwgcwm 阅读数 38
  • Linux系统编程第05期:进程间通信

    Linux系统提供了各种系统调用API用于进程之间的通信: *     无名管道PIPE *     命名管道FIFO *     消息队列 *     共享内存 *     信号量 *     文件锁 *     信号signal.... 其中还包括system V和POSIX 两种接口标准,除此之外,Linux系统自身还扩展了自己的一套API接口用于进程间通信,比如signalfd、timerfd、eventfd等。 本视频教程为《Linux系统编程》第05期,本期课程将会带领大家学习Linux下将近15种进程间通信IPC工具的使用,了解它们的通信机制、编程实例、使用场景、内核中的实现以及各自的优缺点。 本课程会提供PDF版本的PPT课件和代码,学员购买课程后可到课程主页自行下载 嵌入式自学路线指导图: ------------------------------------------------------------------------------------------------------                    《嵌入式工程师自我修养》嵌入式自学系列教程                                           作者:王利涛 ------------------------------------------------------------------------------------------------------ 一线嵌入式工程师精心打造,嵌入式学习路线六步走:  第 1 步:Linux三剑客 * 零基础玩转Linux+Ubuntu * Git零基础实战:Linux开发技能标配 * vim从入门到精通基础篇:零基础学习vim基本命令 * vim从入门到精通定制篇:使用插件打造嵌入式开发IDE * makefile工程实践基础篇:从零开始一步一步写项目的Makefile * makefile工程实践第2季:使用Autotools自动生成Makefile * 软件调试基础理论 * printf打印技巧 * Linux内核日志与打印 * 使用QEMU搭建u-boot+Linux+NFS嵌入式开发环境 第 2 步:C语言嵌入式Linux高级编程 * 第1期:C语言进阶学习路线指南 * 第2期:计算机架构与ARM汇编程序设计 * 第3期:程序的编译、链接和运行原理 * 第4期:堆栈内存管理 * 第6期:数据存储与指针 * 第7期:嵌入式数据结构与Linux内核的OOP思想 * 第8期:C语言的模块化编程 * 第9期:CPU和操作系统入门       搞内核驱动开发、光会C语言是不行的!       你还需要学习的有很多,包括:计算机体系架构、ARM汇编、程序的编译链接运行原理、CPU和操作系统原理、堆栈内存管理、指针、linux内核中的面向对象思想、嵌入式系统架构、C语言的模块化编程..... 第 3 步:Linux系统编程 * 第00期:Linux系统编程入门 * 第01期:揭开文件系统的神秘面纱 * 第02期:文件I/O编程实战 * 第03期:I/O缓存与内存映射 * 第04期:打通进程与终端的任督二脉 * 第05期:进程间通信-------------------we are here!‍      第 4 步:Linux内核编程‍     练乾坤大挪移,会不会九阳神功,是一道坎。搞驱动内核开发,懂不懂内核也是一道坎。 第 5 步:嵌入式驱动开发     芯片原理、datasheet、硬件电路、调试手段、总线协议、内核机制、框架流程.... 第 6 步:项目实战     嵌入式、嵌入式人工智能、物联网、智能家居...

    379 人正在学习 去看看 王利涛
1. 共享内存与消息队列的区别

消息队列在实现消息的收发时,首先由发送进程从进程空间将数据复制到内核分配的数据缓冲区中,接受进程再从内核的缓冲区复制到进程的虚拟地址空间

共享内存是将内核分配的共享存储区域映射到进程的地址空间实现的,没有数据的复制过程,共享内存的访问速度要比消息队列快

2. 共享内存模型

开始---> 创建共享内存(shmget)--->映射共享内存(shmat)--->共享内存读写--->解除映射(shmdt)---结束

共享内存有访问计数器机制,每有一个进程进行一次共享内存映射,共享内存计数器就会加1,每一次解除映射就会减1,只有计数器为0时,才能真正地删除一块共享内存

可以通过ipcs -m 查看, nattch项为访问计数

3. 共享内存挂载在/dev/shm设备上,文件系统名称为tmpfs, 完全驻留在系统内存中,访问速度非常快

4. 使用过程

创建共享内存的Key
key_t IKey = ftok("/etc/profile",1)

创建共享内存
int nShmId = shmget(IKey,256,IPC_CREATE|0666)

type struct{
int n;
char str[256];
}ShmStru

映射共享内存
ShmStru *pShm = shmat(nShmId,NULL,0)

修改共享内存
pShm->n = 3;
strcpy (pShm->str,"12345");

控制共享内存访问权限
shmctl(nShmId,IPC_STAT,..)

解除共享内存映射
shmdt(nShmId,pShm);

计数为0时删除共享内存
shmct(nShmId,IPC_RMID,NULL)



需要注意的:

1. 内核对共享内存大小有限制, 查看 sysctl -a | grep shm, 修改 /etc/sysctl.conf

2. 共享内存创建后,除非将其删除或者重启系统,否则一直存在系统中

只有计数器为0时,才能删除

Linux 共享内存

阅读数 177

linux下共享内存的实现原理

博文 来自: hdxlzh

Linux共享内存整理 shmget shmat

博文 来自: qq_29422251

Linux使用共享内存

阅读数 131

没有更多推荐了,返回首页