在IPC(InterProcess Communication)的通信模式下,不管是使用消息队列还是共享内存,甚至是信号量,每个IPC的对象(object)都有唯一的名字,称为“键”(key)。通过“键”,进程能够识别所用的对象。“键”与IPC对象的关系就如同文件名称之于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够共用一个文件。而在IPC的通讯模式下,通过“键”的使用也使得一个IPC对象能为多个进程所共用。
Linux系统中的所有表示System V中IPC对象的数据结构都包括一个ipc_perm结构,其中包含有IPC对象的键值,该键用于查找System V中IPC对象的引用标识符。如果不使用“键”,进程将无法存取IPC对象,因为IPC对象并不存在于进程本身使用的内存中。
通常,都希望自己的程序能和其他的程序预先约定一个唯一的键值,但实际上并不是总可能的成行的,因为自己的程序无法为一块共享内存选择一个键值。因此,在此把key设为IPC_PRIVATE,这样,操作系统将忽略键,建立一个新的共享内存,指定一个键值,然后返回这块共享内存IPC标识符ID。而将这个新的共享内存的标识符ID告诉其他进程可以在建立共享内存后通过派生子进程,或写入文件或管道来实现。

要使用共享内存,应该有如下步骤:
1.开辟一块共享内存 shmget()
2.允许本进程使用共某块共享内存 shmat()
3.写入/读出

需要删除这块内存的时候,步骤为

4.禁止本进程使用这块共享内存 shmdt()
5.删除这块共享内存 shmctl()或者命令行下ipcrm

过程会用到以下的这些函数:

(1)创建一个新的共享内存区或打开一存在的共享内存区

  int shmget( key_t key , int shmsiz , int flag );

   说明:创建一个新的共享内存区或打开一存在的共享内存区

执行成功时返回共享内存的ID,失败时返回-1。

  key_t key: 是这块共享内存的标识符。如果是父子关系的进程间通信的话,这个标识符用IPC_PRIVATE来代替。如果两个进程没有任何关系,所以就用ftok()算出来一个标识符(或者自己定义一个)使用了。

  int size:共享内存的大小.
  int flag: 共享内存的模式(mode)以及权限标识。
  模式可取如下值:       
  IPC_CREAT 新建(如果已创建则返回目前共享内存的id)
  IPC_EXCL   与IPC_CREAT结合使用,如果已创建则返回错误
  然后将“模式” 和“权限标识”进行“或”运算,做为第三个参数。
  如:    IPC_CREAT | IPC_EXCL | 0640  
  例子中的0666为权限标识,4/2/1 分别表示读/写/执行3种权限,第一个0是UID,第一个6(4+2)表示拥有者的权限,第二个4表示同组权限,第3个0表示他人的权限。

  如果单独使用IPC_CREAT,shmget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符。

  如果将IPC_CREAT和IPC_EXCL标志一起使用,shmget()将返回一个新建的共享内存的标识符;如果该共享内存已存在,或者返回-1。

  IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。

  对于用户的读取和写入许可指定SHM_R和SHM_W,(SHM_R>3)和(SHM_W>3)是一组读取和写入许可,而(SHM_R>6)和(SHM_W>6)是全局读取和写入许可。
  需要注意的是,使用参数要加上 | 0666 作为校验,在有些Linux系统中,如果不加此校验,则不能顺利获取共享空间的值(如Ubuntu)。此外,有两个常用参数,一般要同时出现,他们是:S_IRUSH | S_IWUSR 。由于这两个参数非常常用,程序员一般做这样的操作
  #define PERM S_IRUSR | S_IWUSR | IPC_CREAT
  这样一来,第三个参数就可以直接用PERM来表示了!

返回值
-----------------------------------------------
成功返回共享内存的标识符;不成功返回-1,errno储存错误原因。
    EINVAL        参数size小于SHMMIN或大于SHMMAX。
    EEXIST        预建立key所致的共享内存,但已经存在。
    EIDRM         参数key所致的共享内存已经删除。
    ENOSPC        超过了系统允许建立的共享内存的最大值(SHMALL )。
    ENOENT        参数key所指的共享内存不存在,参数shmflg也未设IPC_CREAT位。
    EACCES        没有权限。
    ENOMEM        核心内存不足。

  关于这个函数,要多说两句。

  创建共享内存时,shmflg参数至少需要 IPC_CREAT | 权限标识,如果只有IPC_CREAT 则申请的地址都是k=0xffffffff,不能使用;
  获取已创建的共享内存时,shmflg不要用IPC_CREAT(只能用创建共享内存时的权限标识,如0640),否则在某些情况下,比如用ipcrm删除共享内存后,用该函数并用IPC_CREAT参数获取一次共享内存(当然,获取失败),则即使再次创建共享内存也不能成功,此时必须更改key来重建共享内存。

另:所有的内存分配操作都是以页为单位的。所以如果一段进程只申请一块只有一个字节的内存,内存也会分配整整一页(在i386机器中一页的缺省大小PACE_SIZE=4096字节)这样,新创建的共享内存的大小实际上是从size这个参数调整而来的页面大小。即如果size为1至4096,则实际申请到的共享内存大小为4K(一页);4097到8192,则实际申请到的共享内存大小为8K(两页),依此类推。

(2) 返回共享内存区在调用进程内的起始地址

  void *shmat( int shmid , char *shmaddr , int shmflag );

  用来允许本进程访问一块共享内存。

  成功时,这个函数返回共享内存的起始地址。失败时返回-1

  int shmid:共享内存的ID。 
  char *shmaddr:共享内存的起始地址,如果shmaddr为0,内核会把共享内存映像到调用进程的地址空间中选定位置;如果shmaddr不为0,内核会把共享内存映像到shmaddr指定的位置。所以一般把shmaddr设为0。
  int shmflag:是本进程对该内存的操作模式。如果是SHM_RDONLY的话,就是只读模式。其它的是读写模式成功时,这个函数返回共享内存的起始地址。失败时返回-1。

(3)删除本进程对这块内存的使用

  int shmdt( char *shmaddr );
  参数char *shmaddr是那块共享内存的起始地址。
  成功时返回0。失败时返回-1。

  附:shmdt()与shmat()相反,是用来禁止本进程访问一块共享内存的函数。

  当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,但是,这并不是从内核真正地删除这个段,而是把相关shmid_ds结构的 shm_nattch域的值减1,当这个值为0时,内核才从物理上删除这个共享段

(4)控制对这块共享内存的使用

  int     shmctl( int shmid , int cmd , struct shmid_ds *buf );

  成功返回0,失败返回-1。
  int shmid: 共享内存的ID。
  int cmd:是控制命令,可取值如下:
        IPC_STAT        得到共享内存的状态
        IPC_SET         改变共享内存的状态
        IPC_RMID        删除共享内存
  struct shmid_ds *buf: 是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定。

  IPC_RMID 命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生在最后一个进程离开这个共享段时。

  请注意,共享内存不会随着程序结束而自动消除,要么调用shmctl删除,要么自己用手敲命令去删除,否则永远留在系统中。

Q&A:

1, 今天调试的时候发现了以下的这些问题:两个需要通信的进程使用一个同样的字符串调用ftok生成key,并对该key调用shmget,其中有一个不能访问到共享内存区。查阅了UNP2才明白了。

ftok将一个已存在的路径名和一个整数标识符转换成一个key_t值。

key_t ftok(const char *pathname, int proj_id)

ftok会组合三个值来产生key:

1、pathname所在的文件系统的信息。

2、该文件在本文件系统内的索引节点号。

3、id的低序8位。

key_t的生成是以一个已存在的文件作为输入,并不是简单的字符串散列函数,必须真正存在某个文件,才能将其位置传入ftok。

2,

收集整理了些资料,做了个简单的对比

 

  

  mmap系统调用 系统V共享内存
获取共享
内存ID
#include <sys/mman.h>
fd=open(name ,flag,mode);
if(fd<0)
….
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
映射内存 ptr=mmap(NULL,len, PROT_READ|PROT_WRITE,
MAP_SHARED , fd , 0);
void *shmat( int shmid , char *shmaddr , int shmflag );
解除映射 int munmap( void * addr, size_t len ) ; int shmdt( char *shmaddr );
使进程中的映射内存无效化,不可以使用。但是保留空间
其它 同步:
int msync ( void * addr , size_t len, int flags);
控制:
shmctl( shmid , IPC_STAT , &buf );
// 取得共享内存的状态
shmctl( shmid , IPC_RMID , &buf );
// 删除共享内存–删除共享内存,彻底不可用,释放空间

IPC_共享内存的更多相关文章

  1. Linux 共享内存详解一

    共享内存段被多个进程附加的时候,如果不是所有进程都已经调用shmdt,那么删除该共享内存段时,会出现一个临时的不完整的共享内存段(key值是0),无法彻底删除.只有当所有进程都调用shmdt,这个临时 ...

  2. PHP进程通信基础——信号量+共享内存通信

    PHP进程通信基础--信号量+共享内存通信 由于进程之间谁先执行并不确定,这取决于内核的进程调度算法,其中比较复杂.由此有可能多进程在相同的时间内同时访问共享内存,从而造成不可预料的错误.信号量这个名 ...

  3. C++ 共享内存 函数封装

    #pragma once #include <string> #include <wtypes.h> #include <map> using namespace ...

  4. Linux学习笔记(14)-进程通信|共享内存

    在Linux中,共享内存是允许两个不相关的进程访问同一个逻辑内存的进程间通信方法,是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式. 不同进程之间共享的内存通常安排为同一段物理内存.进程可 ...

  5. linux 共享内存 shmat,shmget,shmdt,shmctl

    shmget int shmget(key_t key, size_t size, int flag);//开辟一段共享内存 key_t key :标识符的规则() size_t size :共享内存 ...

  6. Linux进程间通信(六):共享内存 shmget()、shmat()、shmdt()、shmctl()

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

  7. linux后台查看共享内存和消息队列的命令

    ipcs ipcs -q : 显示所有的消息队列 ipcs -qt : 显示消息队列的创建时间,发送和接收最后一条消息的时间 ipcs -qp: 显示往消息队列中放消息和从消息队列中取消息的进程ID ...

  8. c++共享内存(转载)

    对于连个不同的进程之间的通信,共享内存是一种比较好的方式,一个进程把数据发送到共享内存中, 另一个进程可以读取改数据,简单记录一下代码 #define BUF_SIZE 256 TCHAR szNam ...

  9. OpenMP共享内存并行编程详解

    实验平台:win7, VS2010 1. 介绍 平行计算机可以简单分为共享内存和分布式内存,共享内存就是多个核心共享一个内存,目前的PC就是这类(不管是只有一个多核CPU还是可以插多个CPU,它们都有 ...

随机推荐

  1. Linux下查看进程和线程

    在linux中查看线程数的三种方法 1.top -H 手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程.否则,它一行显示一个进程. 2.ps xH 手册 ...

  2. UITableview刷新某一个cell或section

    //一个section刷新 NSIndexSet *indexSet=[[NSIndexSet alloc]initWithIndex:2]; [tableview reloadSections:in ...

  3. React组件生命周期-初始化阶段的函数执行顺序

    <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8& ...

  4. Android TextView 文字居中

    有2种方法可以设置TextView文字居中: 一:在xml文件设置:android:gravity="center" 二:在程序中设置:m_TxtTitle.setGravity( ...

  5. Spring笔记——Spring+JDBC组合开发

      使用Spring+JDBC集成步骤如下:   1. 配置数据源 2. 配置事务.配置事务时,需要在xml配置文件中引入用于声明事务的tx命名空间,事务的配置方式有两种:注解方式和基于XML配置方式 ...

  6. 【重走Android之路】【番外篇】关于==和equals

    [重走Android之路][番外篇]关于==和equals   在实际的编程当中,经常会使用==和equals来判断变量是否相同.但是这两种比较方式也常常让人搞得云里雾里摸不着头脑.下面是我个人做的总 ...

  7. CentOS查看系统信息命令和方法

    收集整理的一些linux查看系统信息的命令和方法: 一.linux查看服务器系统信息的方法: 1.查看主机名/内核版本/CPU构架: # uname -n -r -p -o localhost.loc ...

  8. Scala模式匹配和类型系统

    1.模式匹配比java中的switch case强大很多,除了值,类型,集合等进行匹配,最常见的Case class进行匹配,Master.scala有大量的模式匹配. Case "_&qu ...

  9. Database Corruption ->> Fix Database In Suspect State

    昨天在工作中遇到一个情况,就是Development环境中的某台服务器上的某个数据库进入了Suspect状态.以前看书倒是知道说这个状态,不过实际工作当中从来没有遇到过.那么一些背景情况是这样的. 环 ...

  10. Vi Usage

    标签: linux 编辑工具 md 快捷键以及常用命令(前面带:的是命令) h -> 左移一个字符 j -> 下移一行 k -> 上移一行 l -> 右移一个字符 w或Shif ...