Linux进程间通讯之共享内存

#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)
{
    key_t shmid;
    char   *p_addr, *c_addr;
    pid_t pid;
    if(argc != 2) {
        fprintf(stderr, "Usage:%s/n/a", argv[0]);
        exit(1);
    }
    if( (shmid = shmget(IPC_PRIVATE, 1024, PERM)) == -1 )   { //(1)申请一个共享内存
        fprintf(stderr, "Create Share Memory Error:%s/n/a", strerror(errno));
        exit(1);
    }
    pid = fork();
    if(pid > 0) {
        p_addr = (char *)shmat(shmid, 0, 0);//在主进程中获得次共享内存的地址
        memset(p_addr, '/0', 1024);
        strncpy(p_addr, argv[1], 1024);
        wait();
        exit(0);
    }
    else if (pid == 0){
        sleep(1);
        c_addr = (char *) shmat(shmid, 0, 0);//在子进程中获取此共享内存的地址
        printf("Client get %s/n", c_addr);
        exit(0);
    }
}
注意:共享内存一旦创建就会一直存在系统中,直到手动删除或者重启系统。

转自:http://blog.youkuaiyun.com/liranke/article/details/5595740

共享内存:
使用共享内存和使用malloc()来分配内存区域很相似。使用共享内存的方法是:
    1 对一个进程/线程使用shmget()分配内存区域。(
进程使用shmget申请一个共享内存)
    2 使用shmat()放置一个或多个进程/线程在共享内存中,也可以用shmctl()来获取信息或者控制共享区域。(在本线程中获取共享内存的地址
    3 使用shmdt()从共享区域中分离。
    4 使用shmctl()解除分配空间

    共享内存是Linux中最快速的IPC方法。它也是一个双向过程,共享区域内的任何进程都可以读写内存。这个机制的不利方面是其同步和协议都不受程序员控制,必须确保将句柄传递给了子进程和线程。


一. 函数: shmget介绍

功能:取得共享内存段
        语法:#include <sys/types.h>;
             #include <sys/ipc.h>;
             #include <sys/shm.h>;
             int shmget(key,size,shmflg)
             key_t key;
             int size,shmflg;
        说明:本系统调用返回key相关的共享内存标识符.
             共享内存标识符和相关数据结构及至少size字节的共享内存段能
             正常创建,要求以下事实成立:
             . 参数key等于IPC_PRIVATE.
             . 参数key没有相关的共享内存标识符,同时(shmflg&amp;IPC_CREAT)
               值为真.
             共享内存创建时,新生成的共享内存标识相关的数据结构被初始
             化如下:
             . shm_perm.cuid和shm_perm.uid设置为调用进程的有效UID.
             . shm_perm.cgid和shm_perm.gid设置为调用进程的有效GID.
             . shm_perm.mode访问权限比特位设置为shmflg访问权限比特位.
             . shm_lpid,shm_nattch,shm_atime,shm_dtime设置为0.
             . shm_ctime设置为当前系统时间.
             . shm_segsz设置为0.
        返回值:若调用成功则返回一个非0值,称为共享内存标识符,否则返回
             值为-1.

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


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标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。对于用户的读取和写入许可指定SHM_R和SHM_W,(SHM_R>3)和(SHM_W>3)是一组读取和写入许可,而(SHM_R>6)和(SHM_W>6)是全局读取和写入许可。


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

二. 相关数据结构:
struct shmid_ds
-----------------------------------------------
    shmid_ds数据结构表示每个新建的共享内存。当shmget()创建了一块新的共享内存后,返回一个可以用于引用该共享内存的shmid_ds数据结构的标识符。

include/linux/shm.h

    struct shmid_ds { 
        struct ipc_perm    shm_perm;      /* operation perms */ 
        int                shm_segsz;     /* size of segment (bytes) */ 
        __kernel_time_t    shm_atime;     /* last attach time */ 
        __kernel_time_t    shm_dtime;     /* last detach time */ 
        __kernel_time_t    shm_ctime;     /* last change time */ 
        __kernel_ipc_pid_t shm_cpid;      /* pid of creator */ 
        __kernel_ipc_pid_t shm_lpid;      /* pid of last operator */ 
        unsigned short     shm_nattch;    /* no. of current attaches */ 
        unsigned short     shm_unused;    /* compatibility */ 
        void               *shm_unused2; /* ditto - used by DIPC */ 
        void               *shm_unused3; /* unused */ 
    };


struct ipc_perm
-----------------------------------------------
    对于每个IPC对象,系统共用一个struct ipc_perm的数据结构来存放权限信息,以确定一个ipc操作是否可以访问该IPC对象。

    struct ipc_perm { 
        __kernel_key_t   key; 
        __kernel_uid_t   uid; 
        __kernel_gid_t   gid; 
        __kernel_uid_t   cuid; 
        __kernel_gid_t   cgid; 
        __kernel_mode_t mode; 
        unsigned short   seq; 
};

$ ./a.out hello
Client get hello

$ ./a.out hello



http://blog.youkuaiyun.com/nellson/article/details/5398436

共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。因此,采用共享内存的通信方式效率是非常高的。

 

【应用场景】

 

1. 进程间通讯-生产者消费者模式

 

    生产者进程和消费者进程通讯常使用共享内存,比如一个网络服务器,接入进程收到数据包后,直接写到共享内存中,并唤醒处理进程,处理进程从共享内存中读数据包,进行处理。当然,这里要解决互斥的问题。

 

2. 父子进程间通讯

 

    由于fork产生的子进程和父进程不共享内存区,所以父子进程间的通讯也可以使用共享内存,以POSIX共享内存为例,父进程启动后使用MAP_SHARED建立内存映射,并返回指针ptr。fork结束后,子进程也会有指针ptr的拷贝,并指向同一个文件映射。这样父、子进程便共享了ptr指向的内存区。

 

3. 进程间共享-只读模式

 

    业务经常碰到一种场景,进程需要加载一份配置文件,可能这个文件有100K大,那如果这台机器上多个进程都要加载这份配置文件时,比如有200个进程,那内存开销合计为20M,但如果文件更多或者进程数更多时,这种对内存的消耗就是一种严重的浪费。比较好的解决办法是,由一个进程负责把配置文件加载到共享内存中,然后所有需要这份配置的进程只要使用这个共享内存即可。

 

【共享内存分类】

 

1. POSIX共享内存对象

 

const char shmfile[] = "/tmp";
const int size = 100;

 

shm_open创建一个名称为tmp,大小为100字节的共享内存区对象后,在/dev/shm/下可以看到对应的文件,cat可以看到内容。

root:/home/#ls -al /dev/shm/tmp 
-rw------- 1 root root 100 10-15 13:37 /dev/shm/tmp

 

访问速度:非常快,因为 /dev/shm 是tmpfs的文件系统, 可以看成是直接对内存操作的,速度当然是非常快的。

 

持续性:随内核,即进程重启共享内存中数据不会丢失,内核自举或显示调用shm_unlink或rm掉文件删除后丢失。

 

2.  POSIX内存映射文件

const char shmfile[] = "./tmp.shm";
const int size = 100;

 

open在指定目录下创建指定名称后文件,cat可以看到内容。

root:/home/#ls -al ./tmp.shm

-rw-------  1 root    root    100 10-15 13:42 tmp.shm

 

访问速度:慢于内存区对象,因为内核为同步或异步更新到文件系统中,而内存区对象是直接操作内存的。

持续性:随文件,即进程重启或内核自举不后丢失,除失显示rm掉文件后丢失。

 

3. SYSTEM V共享内存

 

共享内存创建后,执行ipcs命令,会打印出相应的信息,比如下面所示,key为申请时分配的,可以执行ipcrm -M 0x12345678 删除,nattch字段为1表示有一个进程挂载了该内存。

 

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status     
0x12345678 32769      root      644        10         1

 

访问速度:非常快,可以理解为全内存操作。

持续性: 随内核,即进程重启共享内存中数据不会丢失,内核自举或显示调用shmdt或使用ipcrm删除后丢失。

 

与POSIX V共享内存区对象不同的是,SYSTEM V的共享内存区对象的大小是在调用shmget创建时固定下来的,而POSIX共享内存区大小可以在任何时刻通过ftruncate修改。

 

 

【代码示例】

 

下面给出三种共享内存使用方法的示例代码,都采用父子进程间通讯,并未考虑互斥,仅做示例供大家参考。

 

1.POSIX共享内存对象

[cpp]  view plain copy
  1. /* 
  2.  * Posix shared memory is easy to use in Linux 2.6, in this program, we  
  3.  * shared a memory between parent process and child process, stored several 
  4.  * objects of struct namelist in it. We store number of items in ptr[0]. 
  5.  */  
  6.   
  7. #include <unistd.h>  
  8. #include <stdio.h>  
  9. #include <stdlib.h>  
  10. #include <string.h>  
  11. #include <sys/ipc.h>  
  12. #include <sys/mman.h>  
  13. #include <sys/types.h>  
  14. #include <sys/wait.h>  
  15. #include <sys/stat.h>  
  16. #include <fcntl.h>  
  17. #include <errno.h>  
  18.   
  19. #define FILE_MODE (S_IRUSR | S_IWUSR)  
  20.   
  21. const char shmfile[] = "/tmp";  
  22. const int size = 100;  
  23.   
  24. struct namelist   
  25. {  
  26.  int  id;   
  27.  char name[20];  
  28. };  
  29.   
  30. int   
  31. main(void)  
  32. {  
  33.  int fd, pid, status;   
  34.  int *ptr;  
  35.  struct stat stat;  
  36.     
  37.  // create a Posix shared memory  
  38.  int flags = O_RDWR | O_CREAT;  
  39.  fd = shm_open(shmfile, flags, FILE_MODE);  
  40.     if (fd < 0) {  
  41.         printf("shm_open failed, errormsg=%s errno=%d", strerror(errno), errno);  
  42.         return 0;  
  43.     }  
  44.  ftruncate(fd, size);  
  45.  ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   
  46.   
  47.  pid = fork();  
  48.  if (pid == 0) { // child process  
  49.   printf("Child %d: start/n", getpid());  
  50.     
  51.   fd = shm_open(shmfile, flags, FILE_MODE);  
  52.   fstat(fd, &stat);    
  53.   ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   
  54.   close(fd);  
  55.   struct namelist tmp;  
  56.   
  57.   // store total num in ptr[0];  
  58.   *ptr = 3;  
  59.     
  60.   ptr++;  
  61.   namelist *cur = (namelist *)ptr;  
  62.   
  63.   // store items  
  64.   tmp.id = 1;  
  65.   strcpy(tmp.name, "Nellson");  
  66.   *cur++ = tmp;  
  67.   tmp.id = 2;  
  68.   strcpy(tmp.name, "Daisy");  
  69.   *cur++ = tmp;  
  70.   tmp.id = 3;  
  71.   strcpy(tmp.name, "Robbie");  
  72.   *cur++ = tmp;  
  73.   
  74.   exit(0);  
  75.  } else// parent process  
  76.   sleep(1);  
  77.   struct namelist tmp;  
  78.   
  79.   int total = *ptr;  
  80.   printf("/nThere is %d item in the shm/n", total);   
  81.     
  82.   ptr++;  
  83.   namelist *cur = (namelist *)ptr;  
  84.   
  85.   for (int i = 0; i< total; i++) {  
  86.    tmp = *cur;  
  87.    printf("%d: %s/n", tmp.id, tmp.name);  
  88.    cur++;  
  89.   }  
  90.   
  91.   printf("/n");  
  92.   waitpid(pid, &status, 0);  
  93.  }  
  94.   
  95.  // remvoe a Posix shared memory from system  
  96.  printf("Parent %d get child status:%d/n", getpid(), status);  
  97.  return 0;  
  98. }  
  

 

 

编译执行

root:/home/ftpuser/ipc#g++ -o shm_posix -lrt shm_posix.cc      
root:/home/ftpuser/ipc#./shm_posix 
Child 2280: start

There is 3 item in the shm
1: Nellson
2: Daisy
3: Robbie

Parent 2279 get child status:0

 

2.POSIX文件映射

 

[cpp]  view plain copy
  1. /* 
  2.  * Posix shared memory is easy to use in Linux 2.6, in this program, we  
  3.  * shared a memory between parent process and child process, stored several 
  4.  * objects of struct namelist in it. We store number of items in ptr[0]. 
  5.  */  
  6.   
  7. #include <unistd.h>  
  8. #include <stdio.h>  
  9. #include <stdlib.h>  
  10. #include <string.h>  
  11. #include <sys/ipc.h>  
  12. #include <sys/mman.h>  
  13. #include <sys/types.h>  
  14. #include <sys/wait.h>  
  15. #include <sys/stat.h>  
  16. #include <fcntl.h>  
  17.   
  18. #define FILE_MODE (S_IRUSR | S_IWUSR)  
  19.   
  20. const char shmfile[] = "./tmp.shm";  
  21. const int size = 100;  
  22.   
  23. struct namelist   
  24. {  
  25.  int  id;   
  26.  char name[20];  
  27. };  
  28.   
  29. int main(void)  
  30. {  
  31.  int fd, pid, status;   
  32.  int *ptr;  
  33.  struct stat stat;  
  34.     
  35.  // create a Posix shared memory  
  36.  int flags = O_RDWR | O_CREAT;  
  37.  fd = open(shmfile, flags, FILE_MODE);  
  38.  ftruncate(fd, size);  
  39.  ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   
  40.   
  41.  pid = fork();  
  42.  if (pid == 0) { // child process  
  43.   printf("Child %d: start/n", getpid());  
  44.     
  45.   fd = open(shmfile, flags, FILE_MODE);  
  46.   fstat(fd, &stat);    
  47.   ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   
  48.   close(fd);  
  49.   struct namelist tmp;  
  50.   
  51.   // store total num in ptr[0];  
  52.   *ptr = 3;  
  53.     
  54.   ptr++;  
  55.   namelist *cur = (namelist *)ptr;  
  56.   
  57.   // store items  
  58.   tmp.id = 1;  
  59.   strcpy(tmp.name, "Nellson");  
  60.   *cur++ = tmp;  
  61.   tmp.id = 2;  
  62.   strcpy(tmp.name, "Daisy");  
  63.   *cur++ = tmp;  
  64.   tmp.id = 3;  
  65.   strcpy(tmp.name, "Robbie");  
  66.   *cur++ = tmp;  
  67.   
  68.   exit(0);  
  69.  } else// parent process  
  70.   sleep(1);  
  71.   struct namelist tmp;  
  72.   
  73.   int total = *ptr;  
  74.   printf("/nThere is %d item in the shm/n", total);   
  75.     
  76.   ptr++;  
  77.   namelist *cur = (namelist *)ptr;  
  78.   
  79.   for (int i = 0; i< total; i++) {  
  80.    tmp = *cur;  
  81.    printf("%d: %s/n", tmp.id, tmp.name);  
  82.    cur++;  
  83.   }  
  84.     
  85.   printf("/n");  
  86.   waitpid(pid, &status, 0);  
  87.  }  
  88.  printf("Parent %d get child status:%d/n", getpid(), status);  
  89.  return 0;  
  90. }  

 

编译执行

 

root:/home/ftpuser/ipc#g++ -o map_posix map_posix.cc 
root:/home/ftpuser/ipc#./map_posix 
Child 2300: start

There is 3 item in the shm
1: Nellson
2: Daisy
3: Robbie

Parent 2299 get child status:0

 

3.SYSTEM V 共享内存对象

 

[cpp]  view plain copy
  1. /* 
  2.  * System V shared memory in easy to use in Linux 2.6, in this program, we  
  3.  * shared a memory between parent process and child process, stored several 
  4.  * objects of struct namelist in it. We store number of items in ptr[0]. 
  5.  */  
  6.   
  7. #include <unistd.h>  
  8. #include <stdio.h>  
  9. #include <stdlib.h>  
  10. #include <string.h>  
  11. #include <sys/ipc.h>  
  12. #include <sys/shm.h>  
  13. #include <sys/types.h>  
  14. #include <sys/wait.h>  
  15.   
  16. #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)  
  17. const char shmfile[] = "./tmp.shm";  
  18. const int shmsize = 10;  
  19.   
  20. struct namelist   
  21. {  
  22.  int  id;   
  23.  char name[20];  
  24. };  
  25.   
  26. int   
  27. main(void)  
  28. {  
  29.  int shmid, pid, status;   
  30.  int *ptr;  
  31.  struct shmid_ds buff;  
  32.   
  33.  // create a systym V shared memory  
  34.  //shmid = shmget(ftok(shmfile, 0), shmsize, SVSHM_MODE | IPC_CREAT);  
  35.  shmid = shmget((key_t)0x12345678, shmsize, SVSHM_MODE | IPC_CREAT);  
  36.   
  37.  pid = fork();  
  38.  if (pid == 0) { // child process  
  39.   printf("Child %d: start/n", getpid());  
  40.   //shmid = shmget(ftok(shmfile, 0), shmsize, SVSHM_MODE | IPC_CREAT);  
  41.   shmid = shmget((key_t)0x12345678, shmsize, SVSHM_MODE | IPC_CREAT);  
  42.   ptr = (int *) shmat(shmid, NULL, 0);  
  43.   shmctl(shmid, IPC_STAT, &buff);  
  44.   
  45.   struct namelist tmp;  
  46.   
  47.   // store total num in ptr[0];  
  48.   *ptr = 3;  
  49.     
  50.   ptr++;  
  51.   namelist *cur = (namelist *)ptr;  
  52.   
  53.   // store items  
  54.   tmp.id = 1;  
  55.   strcpy(tmp.name, "Nellson");  
  56.   *cur++ = tmp;  
  57.   tmp.id = 2;  
  58.   strcpy(tmp.name, "Daisy");  
  59.   *cur++ = tmp;  
  60.   tmp.id = 3;  
  61.   strcpy(tmp.name, "Robbie");  
  62.   *cur++ = tmp;  
  63.     
  64.   exit(0);  
  65.  } else// parent process  
  66.   sleep(1);  
  67.   shmctl(shmid, IPC_STAT, &buff);  
  68.   ptr = (int *) shmat(shmid, NULL, 0);   
  69.   struct namelist tmp;  
  70.   
  71.   int total = *ptr;  
  72.   printf("/nThere is %d item in the shm/n", total);   
  73.     
  74.   ptr++;  
  75.   namelist *cur = (namelist *)ptr;  
  76.   
  77.   for (int i = 0; i< total; i++) {  
  78.    tmp = *cur;  
  79.    printf("%d: %s/n", tmp.id, tmp.name);  
  80.    cur++;  
  81.   }  
  82.   
  83.   printf("/n");  
  84.   waitpid(pid, &status, 0);  
  85.  }  
  86.   
  87.  // remvoe a systym V shared memory from system  
  88.  shmctl(shmid, IPC_RMID, NULL);  
  89.  printf("Parent %d get child status:%d/n", getpid(), status);  
  90.  return 0;  
  91. }  
  92.   
  93.    

 

编译执行

 

root:/home/ftpuser/ipc#g++ -o shm_v shm_v.cc  
root:/home/ftpuser/ipc#./shm_v 
Child 2323: start

There is 3 item in the shm
1: Nellson
2: Daisy
3: Robbie

Parent 2322 get child status:0

 

【性能测试】

下面对三种方式进行性能测试,比较下差异。

测试机信息:

AMD Athlon(tm) Neo X2 Dual Core Processor 6850e

cpu:1.7G

os: Linux 2.6.18

 

测试方式:

打开大小为SIZE的共享内存,映射到一个int型的数组中,循环写数组、读数组。

重复10W次,计算时间开销。

 

内存大小

Shmopen+mmap(ms)

Open+mmap

Shmget

4k

1504

1470

1507

16k

6616

6201

5994

64k

25905

24391

24315

256k

87487

76981

69417

1M

253209

263431

241886

 

重复1K次,计算时间开销。

 

内存大小

Shmopen+mmap(ms)

Open+mmap(ms)

Shmget(ms)

1M

5458

5447

5404

4M

21492

21447

21307

16M

90880

93685

87594

32M

178000

214900

193000

 

分析:

Sytem V方式读写速度快于POSIX方式,而POSIX 共享内存和文件映射方式相差不大, 共享内存性能略优。

 

附上测试源码:

[cpp]  view plain copy
  1. /* 
  2.  * 共享内存读写速度测试 
  3.  */  
  4. #include <unistd.h>  
  5. #include <stdio.h>  
  6. #include <stdlib.h>  
  7. #include <string.h>  
  8. #include <sys/ipc.h>  
  9. #include <sys/mman.h>  
  10. #include <sys/types.h>  
  11. #include <sys/wait.h>  
  12. #include <sys/stat.h>  
  13. #include <fcntl.h>  
  14. #include <unistd.h>  
  15. #include <stdio.h>  
  16. #include <stdlib.h>  
  17. #include <string.h>  
  18. #include <sys/ipc.h>  
  19. #include <sys/mman.h>  
  20. #include <sys/types.h>  
  21. #include <sys/wait.h>  
  22. #include <sys/stat.h>  
  23. #include <fcntl.h>  
  24. #include <errno.h>  
  25. #include <unistd.h>  
  26. #include <stdio.h>  
  27. #include <stdlib.h>  
  28. #include <string.h>  
  29. #include <sys/ipc.h>  
  30. #include <sys/shm.h>  
  31. #include <sys/types.h>  
  32. #include <sys/wait.h>  
  33. #include "module_call.h"  
  34.   
  35. #define FILE_MODE (S_IRUSR | S_IWUSR)  
  36. #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)  
  37.   
  38. enum emType  
  39. {  
  40.     SHMOPEN = 0x01,  
  41.     OPEN = 0x02,  
  42.     SHMGET = 0x04,  
  43. };  
  44.   
  45. void * GetShmMem(emType type, int size)  
  46. {  
  47.     void * ptr = NULL;  
  48.     switch (type)  
  49.     {  
  50.         case SHMOPEN:  
  51.             {  
  52.                 const char shmfile[] = "/tmp";  
  53.                 int flags = O_RDWR | O_CREAT;  
  54.                 int fd = shm_open(shmfile, flags, FILE_MODE);  
  55.                 if (fd < 0)  
  56.                 {  
  57.                     printf("shm_open failed, errormsg=%s errno=%d/n", strerror(errno), errno);    
  58.                     return NULL;  
  59.                 }  
  60.                   
  61.                 ftruncate(fd, size);  
  62.   
  63.                 ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);     
  64.                 if (MAP_FAILED == ptr)  
  65.                 {  
  66.                     printf("mmap failed, errormsg=%s errno=%d/n", strerror(errno), errno);    
  67.                     return NULL;  
  68.                 }  
  69.   
  70.                 break;  
  71.             }  
  72.         case OPEN:  
  73.             {  
  74.                 const char shmfile[] = "./tmp.shm";  
  75.                 int flags = O_RDWR | O_CREAT;  
  76.                 int fd = open(shmfile, flags, FILE_MODE);  
  77.                 if (fd < 0)  
  78.                 {  
  79.                     printf("ope failed, errormsg=%s errno=%d/n", strerror(errno), errno);  
  80.                     return NULL;  
  81.                 }  
  82.   
  83.                 ftruncate(fd, size);  
  84.                   
  85.                 ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);                 
  86.                 if (MAP_FAILED == ptr)  
  87.                 {  
  88.                     printf("mmap failed, errormsg=%s errno=%d/n", strerror(errno), errno);    
  89.                     return NULL;  
  90.                 }  
  91.   
  92.                 break;  
  93.             }  
  94.   
  95.         case SHMGET:  
  96.             {  
  97.                 int shmid;    
  98.                 struct shmid_ds buff;  
  99.                 const char shmfile[] = "./tmp.shm_v";  
  100.                 shmid = shmget(ftok(shmfile, 0), size, SVSHM_MODE | IPC_CREAT);  
  101.                 if (shmid < 0)  
  102.                 {  
  103.                     printf("shmget failed, errormsg=%s errno=%d/n", strerror(errno), errno);  
  104.                     return NULL;  
  105.                 }  
  106.                   
  107.                 ptr = (int *) shmat(shmid, NULL, 0);  
  108.                 if ((void *) -1 == ptr)  
  109.                 {  
  110.                     printf("shmat failed, errormsg=%s errno=%d/n", strerror(errno), errno);  
  111.                     return NULL;  
  112.                 }  
  113.                   
  114.                 shmctl(shmid, IPC_STAT, &buff);  
  115.                 break;  
  116.             }  
  117.     }  
  118.   
  119.     return ptr;  
  120. }  
  121.   
  122. int realmain(int size, int loop, emType type)  
  123. {  
  124.     int * array_int = NULL;  
  125.   
  126.     /* get shmmem*/  
  127.     array_int = (int *)GetShmMem(type, size);  
  128.     if (NULL == array_int)  
  129.     {  
  130.         printf("GetShmMem failed/n");  
  131.         return -1;  
  132.     }  
  133.   
  134.     /* loop */  
  135.     int array_num = size/sizeof(int);  
  136.     modulecall::call_start();  
  137.     while (0 != loop)  
  138.     {  
  139.         /* write */  
  140.         for (int i = 0; i < array_num; i++)  
  141.         {  
  142.             array_int[i] = i;  
  143.         }  
  144.   
  145.         /* read */  
  146.         for (int i = 0; i < array_num; i++)  
  147.         {  
  148.             if (array_int[i] != i)  
  149.             {  
  150.                 printf("ShmMem is invalid i=%d v=%d/n", i, array_int[i]);  
  151.                 return -1;  
  152.             }  
  153.         }  
  154.       
  155.         loop--;  
  156.     }  
  157.     modulecall::call_end();  
  158.     printf("timecost=%lld/n", modulecall::call_timecost());  
  159.   
  160.     return 0;  
  161. }  
  162.   
  163. int main(int argc, char ** argv)  
  164. {  
  165.     if (argc < 4)  
  166.     {  
  167.         printf("usage: %s size loop shmtype(1-shmposix 2-mapposix 4-shmv 7-all)/n", argv[0]);  
  168.         return -1;  
  169.     }  
  170.   
  171.     const int size = atoi(argv[1]);  
  172.     int loop = atoi(argv[2]);  
  173.     const int type = atoi(argv[3]);  
  174.   
  175.     if ((type&SHMOPEN) == SHMOPEN)  
  176.     {  
  177.         printf("shmopen ");  
  178.         realmain(size, loop, SHMOPEN);  
  179.     }  
  180.   
  181.     if ((type&OPEN) == OPEN)  
  182.     {  
  183.         printf("open    ");  
  184.         realmain(size, loop, OPEN);  
  185.     }  
  186.   
  187.     if ((type&SHMGET) == SHMGET)  
  188.     {  
  189.         printf("shmget  ");  
  190.         realmain(size, loop, SHMGET);  
  191.     }     
  192.   
  193.     return 0;  
  194. }  
  

 基于文件映射的共享内存实战:

这个共享的内存其实就是在硬盘上的一个文件,但是对于进程而言,就可以看做是一块内存,用memcpy就能操作这块共享内存。

比如可以把一个结构体的数组写入这块内存,然后,进程结束,但是这个结构体就放入了硬盘的文件中,对于新的进程不需要再一行一行的读取文件到内存,只要把这个文件映射成内存的一个指针,然后,就像操作内存中的数组一样操作文件内的数组。


P1.cpp

#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h> 
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> 

const char shmfile[] = "shm";
const int size = 100;

struct AA {
    int id;
    char name[20];
};

int main() {
    int fd = open(shmfile,  O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    ftruncate(fd, size);
    unsigned char *ptr = (unsigned char *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);


    *ptr = 2;
    ++ptr;

    AA *aptr = (AA *) ptr;
    aptr->id = 1;
    strcpy(aptr->name, "nike");

    aptr++;

    aptr->id = 2;
    strcpy(aptr->name, "josh");

    getchar();

    return 0;
}

p2.cpp


#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h> 
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> 

const char shmfile[] = "shm";
const int size = 100;

struct AA {
    int id;
    char name[20];
};

int main() {
    int fd = open(shmfile,  O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    ftruncate(fd, size);
    unsigned char *ptr = (unsigned char *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);

    int num = *ptr;

    ++ptr;

    AA *aptr = (AA *) ptr;
    
    for (int i = 0; i < num; ++i) {
        printf("%d: %s\n", aptr->id, aptr->name);
        ++aptr;
    }

    getchar();

    return 0;
}


#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>

using namespace std;

int Open(const string& fn, int count, struct stat& st) {
    int fd = open(fn.c_str(), O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);//1,open fd
    ftruncate(fd, count+1);//2,allocate size
    fstat(fd,&st);//3, statistic
    return fd;
}

uint8_t *GetMem(int fd, int count) {
    uint8_t *head = (uint8_t*)mmap(NULL, count+1, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//4,create mapping from disk to memory
}

void Close(uint8_t *head,int fd, struct stat& st) {//6, close fd, write to disk
    munmap(head, st.st_size);
    close(fd);
}

void Write(int count, uint8_t *head) {//5, write as memory
    int offset = 0;
    int* memp = (int*)head;
    memp[offset] = count;
    ++offset;
    for (int i = 0; i < count; ++i)
        memp[offset+i] = (i+1)*100;

}

void Read(uint8_t* head) {
    int *memp = (int*)head;
    int count = memp[0];
    for (int i = 0; i < count; ++i)
        cout << memp[i+1] <<" ";
    cout <<endl;
}

int main(int argc, char *argv[]) {
    if (argc != 4) {
        cout << "filename count w|r\n";
        return -1;
    }

    int count = atoi(argv[2]);
    struct stat st;
    int fd;
    uint8_t *head;
            
    if (argv[3][0] == 'w') {
        fd = open(argv[1], O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);//1,open fd
        ftruncate(fd, count+1);//2,allocate size
        fstat(fd,&st);//3, statistic
        head = (uint8_t*)mmap(NULL, count+1, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//4,create mapping from disk to memory
        
        Write(count, head);
    }
    else {
        fd = open(argv[1],O_RDONLY, S_IRUSR);//1, open fd
        fstat(fd, &st);//2, get fd size
        cout << "fstat st.st_size = "<<st.st_size <<endl;
        head =(uint8_t*)mmap(NULL,st.st_size,PROT_READ,MAP_SHARED,fd,0);//3, create mapping from disk to memory
        
        Read(head);
    }
    Close(head,fd, st);

    return 1;
}



首先运行程序往共享内存中写入数据

再运行数据将磁盘上的文件映射到内存,只要得到其指针就能可以直接用。

需要注意的是,建立内存映射并没有实际拷贝数据,这时,MMU在地址映射表中是无法找到与ptr相对应的物理地址的,也就是MMU失败,将产生一个缺页中断,缺页中断的中断响应函数会在swap中寻找相对应的页面,如果找不到(也就是该文件从来没有被读入内存的情况),则会通过mmap()建立的映射关系,从硬盘上将文件读取到物理内存中

如果磁盘上的文件很大,需要再映射之后再对内存中的数据进行下预读,这样就可以将磁盘中的数据全部加载到内存中。


可以把vector用共享内存的方法进行写入和读取吗?


#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <vector>

using namespace std;

void Close(uint8_t *head,int fd, struct stat& st) {//6, close fd, write to disk
    munmap(head, st.st_size);
    close(fd);
}

void Write(int count, uint8_t *head) {//5, write as memory
    vector<int> vec(count,0);
    for (int i = 0; i < count; ++i)
        vec[i] = (i+1)*10;
    memcpy(head,&vec,sizeof(vector<int>));//先把vector的信息写入mmap
    memcpy(head+sizeof(vector<int>),vec.data(),sizeof(int)* count);//再把数据/数组信息写入接下来的内存

}

void Read(uint8_t* head) {
    vector<int> vec;
    memcpy(&vec,head,sizeof(vector<int>));//只把vecotor的信息拷贝,data指向的地址不能拷贝
    
}

int main(int argc, char *argv[]) {
    if (argc != 4) {
        cout << "filename count w|r\n";
        return -1;
    }

    int count = atoi(argv[2]);
    struct stat st;
    int fd;
    uint8_t *head;
            
    if (argv[3][0] == 'w') {
        size_t size = count*sizeof(int)+sizeof(vector<int>);
        fd = open(argv[1], O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);//1,open fd
        ftruncate(fd, size);//2,allocate size
        fstat(fd,&st);//3, statistic
        head = (uint8_t*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//4,create mapping from disk to memory
        
        Write(count, head);
    }
    else {
        fd = open(argv[1],O_RDONLY, S_IRUSR);//1, open fd
        fstat(fd, &st);//2, get fd size
        cout << "fstat st.st_size = "<<st.st_size <<endl;
        head =(uint8_t*)mmap(NULL,st.st_size,PROT_READ,MAP_SHARED,fd,0);//3, create mapping from disk to memory
        
        Read(head);
    }
    Close(head,fd, st);

    return 1;
}


暂时还是没发现有什么好的办法可以直接读取vector


总结,对于数组类型的数据可以直接用共享内存进行读取,但是对于复杂的数据结构,譬如vector暂时没有高效的方法一步就读取到内存


如果多个进程同时将一份磁盘文件映射到各自的进程空间呢,那么只有一份内存,这样就减少了内存的消耗



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值