zoukankan      html  css  js  c++  java
  • UNIX环境高级编程——System V 共享内存区

          共享内存区域是被多个进程共享的一部分物理内存。如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信。共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。这块共享虚拟内存的页面,出现在每一个共享该页面的进程的页表中。但是它不需要在所有进程的虚拟内存中都有相同的虚拟地址。 


    共享内存的实现,分为两个步骤:
    a. 创建共享内存,使用 shmget 函数。
    b. 映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用 shmat 函数。

         对于每个共享内存区,内核维护如下的信息结果,它定义在<sys/shm.h>头文件中:

               struct shmid_ds {
                   struct ipc_perm shm_perm;    /* Ownership and permissions */
                   size_t          shm_segsz;   /* Size of segment (bytes) */
                   time_t          shm_atime;   /* Last attach time */
                   time_t          shm_dtime;   /* Last detach time */
                   time_t          shm_ctime;   /* Last change time */
                   pid_t           shm_cpid;    /* PID of creator */
                   pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
                   shmatt_t        shm_nattch;  /* current #attached */
                   shmat_t         shm_cnattch; /* in-core #attached */
               };

         下面是ipc_per 结构,它含有本共享内存区的访问权限。

    struct ipc_perm {
        key_t          __key;    /* Key supplied to shmget(2) */
        uid_t          uid;      /* Effective UID of owner */
        gid_t          gid;      /* Effective GID of owner */
        uid_t          cuid;     /* Effective UID of creator */
        gid_t          cgid;     /* Effective GID of creator */
        unsigned short mode;     /* Permissions + SHM_DEST and SHM_LOCKED flags */
        unsigned short __seq;    /* Sequence number */
    };


    1.shmget函数(创建共享内存)

         shmget函数创建一个新的共享内存区,或者访问一个已存在的共享内存区。

    #include <sys/shm.h>
    int shmget(key_t key,size_t size,int oflag);

        返回值是一个称为共享内存区标识符的整数,其他三个shmXXX函数就用它来指代这个内存区。

        key即可以是ftok的返回值,也可以是IPC_PRIVATE。

       size以字节为单位指定内存区的大小。当时机操作为创建一个新的共享内存区时,必须指定一个不为0的size值。如果实际操作为访问一个已存在的共享内存区,那么size应为0.

       oflag是读写权限的组合。它还可以与IPC_CREAT或IPC_CREAT | IPC_EXCL按位或。

       当实际操作为创建一个新的共享内存区时,该内存区被初始化为size字节的0.

       注意,shmget创建或打开一个共享内存区,但并没有给调用进程提供访问该内存区的手段。这是shmat函数的目的。


    2.shmat函数(共享内存映射)

       由shmget创建或打开一个共享内存区后,通过调用shmat把它附接到调用进程的地址空间。

    #include <sys/shm.h>
    void* shmat(int shmid,const void *shmaddr,int flag);

    其中shmid是由shmget返回的标识符。shmat的返回值是所指定的共享内存区在调用进程内的起始地址。确定这个地址的规则如下:

    (1)如果shmaddr是一个空指针,那么系统替调用者选择地址。这是推荐的方法。

    (2)如果shmaddr是一个非空指针,那么返回地址取决于调用者是否给flag参数指定了SHM_RND值:

        a.如果没有指定SHM_RND,那么相应的共享内存区附接到由shmaddr参数指定的地址;

        b.如果指定了SHM_RND,那么相应的共享内存附接到由shmaddr参数指定的地址向下舍入一个SHMLBA常值。LBA代表“低端边界地址”。
       默认情况下,只要调用进程具有某个共享内存区得读写权限,它附接该内存区后就能够同时读写该内存区。flag参数中也可以指定SHM_RDONLY值,它限定只读访问。flag参数默认是0,表示共享内存可读写。


    3.shmdt函数(共享内存解除映射)

        当一个进程完成某个共享内存区的使用时,它可以调用shmdt断接这个内存区。

    #include <sys/shm.h>
    int shmdt(const void *shmaddr);

        当一个进程终止时,它当前附接着的所有共享内存区都自动断接掉。

        注意本函数调用并不删除所指定的共享内存区。这个删除工作通过以IPC_RMID命令调用shmctl完成。


    4.shmctl函数

       shmctl提供了对一个共享内存区的多种操作。

    #include <sys/shm.h>
    int shmctl(int shmid,int cmd,struct shmid_ds *buff);

        该函数提供了三个命令:

       IPC_RMID   从系统中删除由shmid标识的共享内存区并拆除它。

       IPC_SET     给所指定的共享内存区设置其shmid_ds结构的以下三个成员:shm_perm.uid,shm_perm.gid和shm_perm.mode,他们的值来自buff参数指向的结构中的相应成员。shm_ctime的值也用当前时间替换。

       IPC_STAT    (通过buff参数)向调用者返回所指定共享内存区当前的shmid_ds结构。


    5.shmget程序(shmget.c)
      下面是给出的shmget程序使用指定的路径名和长度创建一个共享内存区。
    #include "unpipc.h"
    int main(int argc,char ** argv)
    {
      int c,id,oflag;
      char *ptr;
      size_t length;
      oflag = SVSHM_MODE | IPC_CREAT;
      while( ( c= getopt(argc,argv,"e") ) != -1 )
       {
         switch(c){
         case 'e':
             oflag |= IPC_EXCL;
             break;
          }
        }
       if(optind != argc -2)
         err_quit("usage: shmget [-e] <pathname> <length>");
    
       length = atoi(argv[optind + 1]);
       id = shmget(ftok(argv[optind],0),length,oflag);
       ptr = shmat(id,NULL,0);
       exit(0);
    }

    20    shmget创建由用户指定其名字和大小的共享内存区。作为命令行参数传递进来的路径名由ftok映射成一个system V IPC键。如果指定了- e选项,那么一旦该内存已存在就会出错。如果我们知道该内存区已存在,那么在命令行上的长度参数必须指定为0.

    21    shmat把该内存区附接到当前进程的地址空间。本程序然后终止,不过既然system V共享内存区至少具有随内核的持续性,那么这不会删除该共享内存区。


    6.shmrnid程序(shmrmid.c)
      如下给出的只是以一个IPC_RMID命令调用shmctl,以便从系统中删除一个共享内存区。

    #include "unpipc.h"
    int main(int argc,char ** argv)
    {
      int id;
      if(argc != 2)
       err_quit("usage: shmrmid<pathname>");
    
      id = shmget(ftok(argv[1],0),0,SVSHM_MODE);
      shmctl(id,IPC_RMID,NULL);
      exit(0);
    }


    7.shmwrite程序(shmwrite.c)

       下面给出了shmwrite.c程序,它往一个共享内存区中写入一个模式:0.1.2........254.255.0.1等等。

    #include "unpipc.h"
    int main(int argc,char **argv)
    {
      int i,id;
      struct shmid_ds buff;
      unsigned char *ptr;
    
      if(argc != 2)
        err_quit("usage: shmwrite <pathname>");
    
      id = shmget(ftok(argv[1],0),0,SVSHM_MODE);
      ptr = shmat(id,NULL,0);
      shmctl(id,IPC_STAT,&buff);
    
      /*set: ptr[0] = 0,ptr[1] = 1,etc.*/
      for(i = 0;i < buff.shm_segsz;i++)
          *ptr++ = i%256;
      exit(0);
    }

    11-13   使用shmget打开所指定的共享内存区后由shmat把它附接到当前进程的地址空间。其大小通过以一个IPC_STAT命令调用shmctl取得。
    16-17   往该共享内存区中写入给定的模式。
     

    8.shmread程序(shmread.c)

       下面给出的代码验证shmwrite写入的模式。

    #include "unpipc.h"
    int main(int argc,char** argv)
    {
      int i,id;
      struct shmid_ds buff;
      unsigned char c,*ptr;
    
      if(argc != 2)
        err_quit("usage: shmread <pathname>");
    
      id = shmget(ftok(argv[1],0),0,SVSHM_MODE);
      ptr = shmat(id,NULL,0);
      shmctl(id,IPC_STAT,&buff);
    
      /*check that ptr[0] = 0,ptr[1] = 1,etc.*/
      for(i = 0;i < buff.shm_segsz;i++)
        if((c = *ptr++) != (i%256))
           err_ret("ptr[%d] = %d",i,c);
      exit(0);
    }
    11-13   打开并附接所指定的共享内存区。其大小通过以一个IPC_STAT命令调用shmctl获取。
    16-18   验证由shmwrite写入的模式。



    示例代码:

    #include <stdlib.h>  
    #include <stdio.h>  
    #include <string.h>  
    #include <errno.h>  
    #include <unistd.h>  
    #include <sys/stat.h>  
    #include <sys/types.h>  
    #include <sys/ipc.h>  
    #include <sys/shm.h>  
       
    #define PERM S_IRUSR | S_IWUSR  
     
    int main(int argc, char **argv)  
    {  
             int shmid;  
             char *p_addr, *c_addr;  
       
             if(argc != 2){  
                     fprintf(stderr,"Usage: %s
    ", argv[0]);  
                     exit(0);  
             }  
       
             //创建1k的共享内存,可读可写  
             if((shmid = shmget(IPC_PRIVATE, 1024, PERM)) == -1){  
                     fprintf(stderr, "Create share memory error: %s
    ",strerror(errno));  
             }  
       
             //创建子进程  
             if(fork()){ //父进程,写操作  
                     p_addr = shmat(shmid, NULL , 0); //映射到父进程中  
                                                  //地址为NULL,说明让系统自动指定地址。  
                     memset(p_addr, '', 1024);  
                     strncpy(p_addr, argv[1], 1024);  //拷贝命令行输入字符到共享内存  
                     wait(NULL);
    				 shmctl(shmid,IPC_RMID,NULL);
                     exit(0);  
             } else{  
                     sleep(1);  //子进程,读操作  
                     c_addr = shmat(shmid, 0 ,0); //共享内存映射到子进程  
                     printf("Client get %s
    ", c_addr); 				 
                     exit(0);  
             }  
    }  

    运行结果:

    huangcheng@ubuntu:~$ ./a.out huangcheng
    Client get huangcheng


  • 相关阅读:
    SCI写作经典替换词,瞬间高大上!(转)
    最佳化常用测试函数 Optimization Test functions
    算法复杂度速查表
    VS 代码行统计
    CPLEX IDE 菜单栏语言设置( 中文 英文 韩文 等多国语言 设置)
    如何从PDF文件中提取矢量图
    Matlab无法打开M文件的错误( Undefined function or method 'uiopen' for input arguments of type 'char)
    visual studio 资源视图 空白 解决方案
    MFC DialogBar 按钮灰色不响应
    嗨翻C语言笔记(二)
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6332664.html
Copyright © 2011-2022 走看看