zoukankan      html  css  js  c++  java
  • 进程间通信---共享内存

    一、IPC(Inter-Process Communication,进程间通信)对象的介绍
     
    System V 的IPC对象有共享内存、消息队列、信号灯。



    注意:在IPC的通信模式下,不管是使用消息队列还是共享内存,甚至是信号灯,每个IPC的对象都有唯一的名字,称为"键"(key)。通过"键",进程能够识别所用的对象。"键"与IPC对象的关系就如同文件名称于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够公用一个文件。而在 IPC的通讯模式下,通过"键"的使用也使得一个IPC对象能为多个进程所共用。
     
    二、共享内存的介绍
     
    <1>共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。
     
    <2>为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率。
     
    <3>由于多个进程共享一段内存,因此也需要依靠某种同步机制。
     
    三、共享内存的特点



    四、共享内存的操作流程
     
    <1>创建/打开共享内存
    <2>映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
    <3>撤销共享内存映射
    <4>删除共享内存对象
     
    五、相关API 
     
    A.获取一块共享内存



    功能:分配一块共享内存
     
    返回值:
     
    调用成功返回一个shmid(类似打开一个或创建一个文件获得的文件描述符一样);
    调用失败返回-1。
     
    参数说明:
     
    <1>key标识共享内存的键值(就像文件的标识是文件名):0  / IPC_PRIVATE。
     
    当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;
     
    如果key的取值为0,而参数shmflg中设置了IPC_CREATE这个标志,则同样创建一块新的共享内存。
     
    通过这种方式分配的共享内存,一般用来亲缘关系的进程间通信。
     
    注意:我们一般是通过ftok这个函数获取键值

    功能 : 获取一个IPC对象的键值
     
    参数说明:
     
    pthname就是你指定文件名的路径(该文件必须是存在而且可以访问的),一般情况我们都写一个目录
     
    proj_id  : 和pthname一起完成创建键值的参数,虽然为int,但是只有8个比特被使用。一般我们写一个字符代替。
     
    例如:
     
    案例:


    运行的结果:



    <2>size是要建立共享内存的长度。所有的内存分配操作都是以页为单位的。所以如果一个进程只申请一块只有一个字节的内存,内存也会分配整整一页(在i386机器中一页的缺省大小PACE_SIZE = 4096字节)。
     
    <3>shmflg有效的标志包括IPC_CREAT 和IPC_EXCL,他们的功能与open()的O_CREAT和O_EXCL相当。
     
    IPC_CREAT      如果共享内存不存在,则创建一个共享内存,否则直接打开已存在的
    IPC_EXCL        只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误



    例子一:假设键值为key,创建一个共享内存大小为4k,访问权限为066,如果已经存在则返回其标识号 
     
    int shmid;
    if( (shmid = shmget(key,4 * 1024,0666 | IPC_CREAT)) < 0)
    {
        perror("Fail to shmget");
        exit(EXIT_FAILURE)
    }
     
    例子二、假设键值为key,创建一个共享内存大小为1k,访问权限为0666,如果已经存在则报错
     
    int shmid;
    if((shmid  = shmget(key,1024,0666 | IPC_CREAT | IPC_EXCL)) < 0)
    {
        perror("Fail to shmget");
        exit(EXIT_FAILURE);
    }
     
    B.共享内存的映射



    函数shmat将标识号为shmid共享内存映射到调用进程的地址空间中。
     
    参数说明:
     
    shmid  :  要映射的共享内存区标识符
     
    shmaddr  :  将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)
     
    shmflg  :  SHM_RDONLY  共享内存只读
                    默认0:共享内存可读写。
     
    返回值 :调用成功放回映射后的地址 ,出错放回(void *)-1;
     
    C.取消共享内存与用户进程之间的映射



    参数shmaddr是shmat映射成功放回的地址。
     
    注意:当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,但是,这并不是从内核真正地删除这个段,而是把相关shmid_ds结构的shm_nattch域的值减1当这个值为0时,内核才从物理上删除这个共享段
     
    D.控制共享内存



    参数说明:
     
    shmid  共享内存标识ID
     
    cmd      IPC_STAT得到共享内存的状态
                  IPC_SET改变共享内存的状态
                  IPC_RMID删除共享内存
     
    buf  是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定;



    注意:
     
    1.IPC_RMID命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生最后一个进程离开这个共享段时
     
    2.当cmd为IPC_RMID时,第三个参数应为NULL。呵呵,大部分我们都是这样做,用这个函数删除共享内存。
     
     
    案例探究:


    点击(此处)折叠或打开

    1. #include <sys/shm.h>
    2. #include <stdio.h>
    3. #include <stdlib.h>
    4. #include <string.h>
    5. #include <semaphore.h>
    6. #include <fcntl.h>
    7. #include <sys/stat.h>
    8. #define BUFF_SIZE 1024
    9. int father_do_work(int shmid)
    10. {
    11.     char *buf;
    12.     void *shmaddr;
    13.     sem_t *prsem;
    14.     sem_t *pwsem;
    15.     //有名信号量
    16.     if((prsem = sem_open("rsem",O_CREAT,0666,0)) == SEM_FAILED)
    17.     {
    18.         perror("Fail to sem open");
    19.         return -1;
    20.     }
    21.         
    22.         //有名信号量
    23.     if((pwsem = sem_open("wsem",O_CREAT,0666,1)) == SEM_FAILED)
    24.     {
    25.         perror("Fail to sem open");
    26.         return -1;
    27.     }
    28.     //映射共享内存
    29.     if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1)
    30.     {
    31.         perror("Fail to shmat");
    32.         exit(EXIT_FAILURE);
    33.     }
    34.     buf = (char *)shmaddr;
    35.     while(1)
    36.     {
    37.         if(sem_wait(pwsem) < 0)
    38.         {
    39.             perror("Fail to sem wait");
    40.             break;
    41.         }
    42.         printf(">");
    43.         fgets(buf,BUFF_SIZE,stdin);
    44.         buf[strlen(buf) - 1] = '';
    45.         
    46.         if(sem_post(prsem) < 0)
    47.         {
    48.             perror("Fail to sem post");
    49.             break;
    50.         }
    51.         
    52.         if(strncmp(buf,"quit",4) == 0)
    53.         {
    54.             if(shmdt(shmaddr) < 0)
    55.             {
    56.                 perror("Fail to shmaddr");
    57.                 exit(EXIT_FAILURE);
    58.             }
    59.             break;
    60.         }
    61.     
    62.         usleep(500);
    63.     }
    64.     
    65.     return 0;
    66. }
    67. int child_do_work(int shmid)
    68. {
    69.     char *buf;
    70.     void *shmaddr;
    71.     sem_t *prsem;
    72.     sem_t *pwsem;
    73.     //
    74.     if((prsem = sem_open("rsem",O_CREAT,0666,0)) == SEM_FAILED)
    75.     {
    76.         perror("Fail to sem open");
    77.         return -1;
    78.     }
    79.     if((pwsem = sem_open("wsem",O_CREAT,0666,1)) == SEM_FAILED)
    80.     {
    81.         perror("Fail to sem open");
    82.         return -1;
    83.     }
    84.     //映射共享内存
    85.     if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1)
    86.     {
    87.         perror("Fail to shmat");
    88.         exit(EXIT_FAILURE);
    89.     }
    90.     buf = (char *)shmaddr;
    91.     while(1)
    92.     {
    93.         if(sem_wait(prsem) < 0)
    94.         {
    95.             perror("Fail to prsem");
    96.             break;
    97.         }
    98.         printf("read buf : %s. ",buf);
    99.         if(sem_post(pwsem) < 0)
    100.         {
    101.             perror("Fail to pwsem");
    102.             break;
    103.         }
    104.         if(strncmp(buf,"quit",4) == 0)
    105.         {
    106.             if(shmdt(shmaddr) < 0)
    107.             {
    108.                 perror("Fail to shmaddr");
    109.                 exit(EXIT_FAILURE);
    110.             }
    111.             break;
    112.         }
    113.     }
    114.     
    115.     return 0;
    116. }
    117. int main()
    118. {
    119.     int shmid;
    120.     int pid;
    121.     void *shmaddr;
    122.     
    123.     //创建共享内存
    124.     if((shmid = shmget(IPC_PRIVATE,BUFF_SIZE,0666 | IPC_CREAT)) < 0)
    125.     {
    126.         perror("Fail to shmget");
    127.         exit(EXIT_FAILURE);
    128.     }
    129.     if((pid = fork()) < 0)
    130.     {
    131.         perror("Fail to fork");
    132.         exit(EXIT_FAILURE);
    133.     
    134.     }else if(pid == 0){
    135.     
    136.         child_do_work(shmid);
    137.     
    138.     }else{
    139.         
    140.         father_do_work(shmid);
    141.         wait(NULL);
    142.         if(shmctl(shmid,IPC_RMID,NULL) < 0)
    143.         {
    144.             perror("Fail to shmctl");
    145.             exit(EXIT_FAILURE);
    146.         }
    147.     }
    148.     exit(EXIT_SUCCESS);
    149. }

    运行结果:





  • 相关阅读:
    html基础笔记
    webpack实用配置总结
    Vue生命周期
    Vue跨域解决方法
    日历
    绝对地址和相对地址
    HTML+CSS要点
    (利用DOM)在新打开的页面点击关闭当前浏览器窗口
    前端面试
    前端工程师的知识体系
  • 原文地址:https://www.cnblogs.com/jiangzhaowei/p/5558785.html
Copyright © 2011-2022 走看看