• 进程间通信——System V IPC与共享内存


      什么是System V IPC?

      System V是一种Unix操作系统,此系统引入了三种高级进程间的通信机制:消息队列,共享内存,信号量。System V IPC是以下三种IPC的合称:

      System V 消息队列     System V 信号量      System V 共享内存

      消息队列、信号量、共享内存也称为IPC对象。通过ipcs可以查看当前的IPC对象,通过ipcrm可以删除IPC对象。

    1   ipcs -q: 只显示消息队列。
    2   ipcs -s: 只显示信号量。
    3   ipcs -m: 只显示共享内存。
    4   ipcs –help: 其他的参数

      IPC对象存在于内核中而不是文件系统中,由用户控制释放,不像管道的释放由内核控制

           IPC对象通过其标识符来引用和访问,所有IPC对象在内核空间有唯一性标志ID,在用户空间的唯一性标识符称为key

      IPC对象函数创建

      Unix系统中,一切皆文件,很多IPC机制中的操作都是针对文件描述符的,但system V是针对IPC对象的ID操作的,而ID(标识符)是由key(键)生成的。

       在IPC的通信模式下,不管是使用消息队列还是共享内存,甚至是信号量,每个IPC的对象(object)都有唯一的名字,称为“键”(key)。通过“键”,进程能够识别所用的对象。“键”与IPC对象的关系   就如同文件名称之于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够共用一个文件。而在IPC的通讯模式下,通过“键”的使用也使得一个IPC对象能为多个进程所共用。

      ftok() ---> key ------>  ID

      接下来看一下ftok函数:

      ftok()函数

    1 #include <sys/types.h>
    2 #include <sys/ipc.h>
    3 
    4 key_t ftok(const char *pathname, int proj_id);
    5 //pathname为文件路径名,必须存在且可访问,常为当前路径名“.”
    6 //proj_id 子ID,用于生成key的数字,范围1-255
    7 

      System V IPC详见:https://blog.csdn.net/qq_38211852/article/details/80475818

    共享内存

      共享内存允许不同的进程访问同一个逻辑内存,对内存直接读写,而不需要任何数据的拷贝,对于消息队列、管道等通信方式,需要在内核空间进行四次数据拷贝,而高兴内存只需要拷贝两次,一次输入文件到共享内存区,另一次从共享内存区到输出文件。。

      共享内存在内核空间创建,可以被映射到用户空间访问,使用灵活。(映射即建立关联:如在内核中一块内存地址为1,通过映射到用户空间后为a,之后在用户空间访问a即访问内核空间1一样。)

      共享内存并未提供同步机制,所以需要用其他机制实现不同进程对共享内存的访问。

        共享内存使用步骤

    1 创建/打开共享内存
    2 映射共享内存,即把指定的共享内存映射到进程地址空间用于访问
    3 读写共享内存
    4 撤销共享内存映射
    5 删除共享内存对象

      1、共享内存创建—shmget

     1 #include <sys/ipc.h>
     2 #include <sys/shm.h>
     3 
     4 int shmget(key_t key, size_t size, int shmflg);
     5 
     6 //成功返回共享内存的ID,失败返回EOF
     7 //参数:
     8 //key---可由fotk生成,为共享内存段命令,函数返回与key相关标识符
     9 //size---以字节为单位指定需要的共享内存容量
    10 //shmflg---权限标志,如:IPC_CREAT|0666

    详解:

    //key_t key
    -----------------------------------------------
        key标识共享内存的键值: 0/IPC_PRIVATE。 当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中设置了IPC_PRIVATE这个标志,则同样将创建一块新的共享内存。
    
    //int size(单位字节Byte)
    -----------------------------------------------
        size是要建立共享内存的长度。所有的内存分配操作都是以页为单位的。所以如果一段进程只申请一块只有一个字节的内存,内存也会分配整整一页(在i386机器中一页的缺省大小PACE_SIZE=4096字节)这样,新创建的共享内存的大小实际上是从size这个参数调整而来的页面大小。即如果size为1至4096,则实际申请到的共享内存大小为4K(一页);4097到8192,则实际申请到的共享内存大小为8K(两页),依此类推。
    
    //int shmflg
    -----------------------------------------------
        shmflg主要和一些标志有关。其中有效的包括IPC_CREAT和IPC_EXCL,它们的功能与open()的O_CREAT和O_EXCL相当。
        IPC_CREAT   如果共享内存不存在,则创建一个共享内存,否则打开操作。
        IPC_EXCL     只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。
    
        如果单独使用IPC_CREAT,shmget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符。
    
        如果将IPC_CREAT和IPC_EXCL标志一起使用,shmget()将返回一个新建的共享内存的标识符;如果该共享内存已存在,或者返回-1。
    
         IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。
    
    推荐使用这个:
               可以使用0666|IPC_CREAT,来作为shmflg的值。
    
    //返回值
    -----------------------------------------------
    成功返回共享内存的标识符;不成功返回-1,errno储存错误原因。
        EINVAL           参数size小于SHMMIN或大于SHMMAX。
        EEXIST           预建立key所致的共享内存,但已经存在。
        EIDRM            参数key所致的共享内存已经删除。
        ENOSPC        超过了系统允许建立的共享内存的最大值(SHMALL )。
        ENOENT        参数key所指的共享内存不存在,参数shmflg也未设IPC_CREAT位。
        EACCES        没有权限。
        ENOMEM       核心内存不足。
    
    引用于:http://blog.csdn.net/lanmanck/archive/2010/12/22/6092995.aspx
    shmget参数详解

    示例1:创建一个私有的共享内存,大小为512字节,权限为0666

    1 int shmid;
    2 
    3 if((shmid = shmget(IPC_PRIVATE, 512, 0666)) < 0)
    4 {
    5       perror("shmget");
    6       exit(-1);            
    7 }

      示例2:创建/打开一个和KEY关联的共享内存,大小为1024字节,权限为0666

     1 key_t key;
     2 int shmid;
     3 
     4 if((key = ftok(".", 'm')) == -1)
     5 {
     6      perror("ftok");
     7      exit(-1);
     8 }
     9 if((shmid = shmget(key, 1024, IPC_CREAT|0666)) < 0)
    10 {
    11       perror("shmget");
    12       exit(-1);            
    13 }

      2、共享内存的映射—shmat

     1 #include <sys/ipc.h>
     2 #include <sys/shm.h>
     3 
     4 void *shmat(int shmid, const void *shmaddr, int shmflg);
     5 
     6 //成功返回映射后的地址,失败返回(void *)-1
     7 //参数:
     8 //shmid---要映射的共享内存ID
     9 //shmaddr---映射后的地址,NULL表示由系统自动映射
    10 //shmflg---标志位,0表可读写;SHM_RDONLY表只读

      shmat--share memory attach,应用层无法直接访问内核内存,通过系统调用,将内核中的共享内存映射到用户空间。

      3、写共享内存

      通过指针访问共享内存,指针类型取决于共享内存中存放的数据类型

      示例:在共享内存中存放键盘输入的字符串

     1 char *addr;
     2 int shmid;
     3 ...
     4 if((addr = (char *)shmat(shmid, NULL, 0)) == (char *)-1)
     5 {
     6       perror("shmat");
     7       exit(-1);  
     8 }
     9 fgets(addr, N, stdin);
    10 ...

      4、共享内存撤销映射—shmdt

      将共享内存从当前进程中分离,分离并不是删除,只是使该共享内存对当前进程不再可用。

     1 #include <sys/ipc.h>
     2 #include <sys/shm.h>
     3 
     4 int shmdt(const void *shmaddr);
     5 
     6 //成功时返回0,失败时返回EOF
     7 //参数:
     8 //shmaddr---为shmat函数返回的地址指针
     9 //不使用共享内存时应当撤销映射
    10 //进程结束时会自动撤销

      5、共享内存控制—shmctl

      用于控制共享内存

     1 #include <sys/ipc.h>
     2 #incude  <sys/shm.h>
     3 
     4 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
     5 
     6 //成功返回0,失败返回EOF
     7 //参数
     8 //shmid---要操作的共享内存id
     9 //cmd---要执行的操作  
    10 //       IPC_STAT:用共享内存的当前关联值覆盖shmid_ds的值。
    11 //       IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
    12 //       IPC_REID:删除共享内存段

    注意事项:

      每块共享内存的大小有限制,可以通过下命令查看

      ipcs -l

      cat /proc/sys/kernel/shmmax

      共享内存删除的时间点

      shmctl(shmid, IPC_RMID, NULL);添加删除标记

      当nattach变成0时才真正删除。

    测试程序

    通过shm_write 和shm_read这两个进程实现对共享内存的读写

    shm_wirte.c

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 #include <sys/ipc.h>
     5 #include <sys/shm.h>
     6 
     7 int main()
     8 {
     9     key_t key;
    10     int shmid;
    11     char *addr; //定义映射后的地址
    12     key = ftok(".",23);
    13     if(key == -1)
    14     {
    15         perror("ftok");
    16         return -1;
    17     }
    18     shmid = shmget(key,1024,IPC_CREAT|0666); //创建共享内存
    19     if(shmid==-1)
    20     {
    21         perror("shmget");
    22         return -1;
    23     }
    24     
    25     addr = shmat(shmid,NULL,0); //映射共享内存
    26 
    27     strcpy(addr,"this is share memory"); //读写共享内存
    28 
    29     shmdt(addr); //撤销共享内存映射
    30 
    31 
    32 }

    shm_read.c

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 #include <sys/ipc.h>
     5 #include <sys/shm.h>
     6 
     7 int main()
     8 {
     9     key_t key;
    10     int shmid;
    11     char *addr; //定义映射后的地址
    12     key = ftok(".",23);
    13     if(key == -1)
    14     {
    15         perror("ftok");
    16         return -1;
    17     }
    18     shmid = shmget(key,1024,IPC_CREAT|0666); //创建共享内存
    19     if(shmid==-1)
    20     {
    21         perror("shmget");
    22         return -1;
    23     }
    24     
    25     addr = shmat(shmid,NULL,0); //映射共享内存
    26 
    27 //    strcpy(addr,"this is share memory"); //读写共享内存
    28     printf("get share memory = %s
    ",addr);
    29     shmdt(addr); //撤销共享内存映射
    30 
    31 
    32 }

    结果:

    共享内存详解优秀博客:https://blog.csdn.net/Al_xin/article/details/38602093

  • 相关阅读:
    4.方向-世界坐标系
    3.Unity3d物体操作常用按键
    2.Unity3d常用按键
    1.Unity3d的新建场景和保存场景
    MySQL CodeFirst的配置与注意事项
    HNU暑假训练第一场C.Ninja Map
    牛客暑假多校第二场J-farm
    牛客暑假多校第一场J-Different Integers
    主席树设计与实现
    博弈论的一些理解
  • 原文地址:https://www.cnblogs.com/y4247464/p/12109332.html
Copyright © 2020-2023  润新知