进程间通信---共享内存

来源:互联网 发布:淘宝扫码领红包怎么扫 编辑:程序博客网 时间:2024/06/03 17:47

    进程间的通信方式我们都熟悉,管道(命名管道)、信号(signal)、共享内存、消息队列、套接字(socket),关于信号量个人认为应该归为进程间的同步机制里。
    下面我们就说说共享内存的通信方式。 它是IPC中最快的。一旦内存区映射到共享它的进程的地址空间,这些进程间数据的传递就不在涉及内核。但是存取数据的时候需要保持同步. 关于共享内存权威的参考资料为《unix网络编程卷2》. 在前面我们讲解了mmap的内核机制,在posix v的共享内存的方式就是这个原理。system v也类似,只不过对接口进行了封装.
    我们先从posix v的方式开始说起,它常用的函数接口:
    open/shm_open  +  mmap  、munmap、msync、shm_unlink等.
    既然是open那么就会对应文件,当然并不是所有的文件都可以映射.这里支持的文件类型:
   1 .普通的磁盘文件 
   2. 设备文件(除去一些特殊的文件)
   3.内存文件 (例如tmpfs)
   4. 匿名映射 (主要用在具有亲缘关系的进程间)
     先说一下常用的匿名映射,直接open  /dev/zero 保证映射区域都初始化为0 的匿名映射。 还有可以直接用mmap来映射,而不用打开任何文件:

点击(此处)折叠或打开

  1. mmap( NULL,size_of_map,PROT_READ|PROT_WRITE,MAP_SHARED | MAP_ANON, -1,);
这里提示一下mmap的函数原型:

点击(此处)折叠或打开

  1. void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
匿名映射主要区别是flags 里多了一个 MAP_ANON标志,和fd传递的为-1. 关于更多mmap的资料请自行查询.
    下面说一下一般mmap方式的共享内存需要注意的:
 映射的内存区域大小和文件大小,内存映射的大小都是页面的整数倍(PAGESIZE 4096默认) 比如文件大小为5000,映射了5000 ,那么可以访问2*PAGESIZE的内存区域,但是大于5000的内存写不会同步到文件里。这个内存可以访问的大小跟文件大小也是有关系的。映射的时候会自动检查底层的支持即文件的大小. 但是我们知道mmap的方式的优点就是可以动态的改变文件的大小,函数接口为ftruncate  和fstat. 具体用法参考unix网络编程卷2.

下面就简单总结下它的 特点:
1. 可以随时改变其大小  
操作函数接口:ftruncate  和fstat
2. 必须先创建打开一个文件为基础 ,这个文件可以是磁盘文件 或者内存文件比如tmpfs下的文件;如果共享内存的配置需要写入到磁盘 ,可以选择打开磁盘文件。如果在嵌入式里用一般会把flash一个文件作为映射 ,既然需要频繁的操作文件 和磁盘读写 所以建议还是用shm。  毕竟flash频繁读写影响寿命和容易出现坏块。

 而system v的方式不需要明确的open文件,但是函数操作接口比较多.
 函数接口: shmget、shmat、shmdt、shmctl等。
 shm方式有些限制:
  shmax  一个共享内存区的最大字节数     (具体系统不太一样)
 shmmnb  一个共享内存区的最小字节数   1
 shmmni 系统范围最大共享内存区标识数    128
 shmseg  每个进程附接的最大共享内存区数    32 
这些信息可以通过proc文件系统来查看。  cat   /proc/sys/kernel/shmmax 等.  具体查看映射的内存区 可以通过ipcs命令来查看和操作. 

我们也总结下它的特点:
    1.不需要创建和打开文件
    2. 如果是多进程间通信,需要统一 一个key标示 。动态生成的不一定一致。
    3.读写的速度高于mmap的方式
    4. 共享内存空间不能太大,毕竟直接占用内存空间.
不论哪种方式,都会涉及多进程间的通信、数据交互什么的,那么就必须保持互斥和同步,这里最常用的方式是采用信号量的方式.
信号量的接口:sem_init、sem_wait、sem_post.  由于用了信号量编译的时候会需要链接线程库 -lpthread.
下面就具体代码示例看看它们的具体用法:
posix v方式:
1. 初始化,之后进入循环不停的写,周期为1s。 

点击(此处)折叠或打开

  1. #include <sys/mman.h> 
  2. #include <sys/types.h> 
  3. #include <fcntl.h> 
  4. #include <unistd.h> 
  5. #include <semaphore.h>


  6. #define CLUSTER_SHARED_FILE "/tmp/cluster_share"

  7. typedef struct{ 
  8.     char name[4]; 
  9.     int age; 
  10. }people; 

  11. struct stu {

  12.     sem_t mutex;
  13.     people s[10];

  14. };

  15. struct stu t;

  16. main(int argc, char** argv) // map a normal file as shared mem: 
  17. { 
  18.     int fd,i; 
  19.     struct stu *p_map; 
  20.     char temp; 
  21.       
  22.     fd=open(CLUSTER_SHARED_FILE,O_CREAT|O_RDWR|O_TRUNC,00777); 
  23.     lseek(fd,sizeof(t)-1,SEEK_SET); 
  24.     write(fd,"",1); 
  25.       
  26.     p_map = (struct stu *) mmap( NULL,sizeof(t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,); 
  27.     close(fd); 
  28.     temp = 'a';
  29.     sem_init(&p_map->mutex,1,1);
  30.     int j=0;
  31.     while(1)
  32.     {
  33.      j++;
  34.      printf("j 1\n");
  35.      sem_wait(&p_map->mutex);
  36.      printf("j 2\n");
  37.     for(i=1; i<10; i++) 
  38.     { 
  39.       // temp += 1; 
  40.      
  41.         memcpy(p_map->s[i].name, &temp,); 
  42.         p_map->s[i].age = p_map->s[i].age +20+i+j; 
  43.     } 
  44.     printf("j 3\n");
  45.     sem_post(&p_map->mutex);
  46.     printf("j 4\n");
  47.      sleep(1);
  48.     }
  49.     printf(" initialize over \n "); 
  50.     sleep(50); 
  51.     munmap( p_map, sizeof(t) ); 
  52.     printf( "umap ok \n" ); 
  53. }
2. 读取操作:

点击(此处)折叠或打开

  1. #include <sys/mman.h> 
  2. #include <sys/types.h> 
  3. #include <fcntl.h> 
  4. #include <unistd.h> 

  5. #include <sys/sem.h> 

  6. #include <semaphore.h>

  7. #define CLUSTER_SHARED_FILE "/tmp/cluster_share"

  8. typedef struct{ 
  9.     char name[4]; 
  10.     int age; 
  11. }people; 

  12. struct stu {

  13.     sem_t mutex;
  14.     people s[10];
  15. };

  16. struct stu t;

  17. main(int argc, char** argv) // map a normal file as shared mem: 
  18. { 
  19.     int fd,i; 
  20.     struct stu *p_map; 
  21.     char temp; 
  22.       
  23.     fd=open(CLUSTER_SHARED_FILE,O_CREAT|O_RDWR,00777); 
  24.    // lseek(fd,sizeof(people)*5-1,SEEK_SET); 
  25.   // write(fd,"",1); 
  26.       
  27.     p_map = (struct stu *) mmap( NULL,sizeof(t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,); 
  28.      close( fd ); 

  29.      while(1)
  30.     {
  31.         sleep(1);
  32.         printf("2.....s\n");
  33.     sem_wait(&p_map->mutex);
  34.     printf("2.....\n");
  35.     for(i=0; i<10; i++)         
  36.     { 
  37.         printf("name:%s,age:%d\n",p_map->s[i].name,p_map->s[i].age); 
  38.     
  39.     } 
  40.         printf("2.....0\n");
  41.         sem_post(&p_map->mutex);
  42.         printf("2.......1");
  43.     }
  44.     munmap( p_map, sizeof(t)); 
  45.     printf( "umap ok \n" ); 
  46. }
至于并非的写操作,程序稍微修改一下就可以测试. 多并发没有问题.
system V的方式,虽然接口不太一样,但是也很类似。
1. 初始化

点击(此处)折叠或打开

  1. #include <unistd.h> 
  2. #include <stdlib.h> 
  3. #include <stdio.h> 
  4. #include <string.h> 
  5. #include <sys/shm.h> 
  6. #include <semaphore.h>

  7. //#include "shmdata.h" 




  8. #define TEXT_SZ 2048 
  9.   
  10. struct shared_use_st 
  11. { 
  12.     sem_t mutex;
  13.     int written;//作为一个标志,非0:表示可读,0表示可写 
  14.     char text[TEXT_SZ];//记录写入和读取的文本 
  15.     int cnt;
  16. }; 


  17. int main() 
  18. { 
  19.     int running = 1; 
  20.     void *shm = NULL; 
  21.     struct shared_use_st *shared = NULL; 
  22.     char buffer[4096]="hello world"; 
  23.     int shmid;
  24.     int i=0;
  25.     //创建共享内存 
  26. #if 0
  27.     //删除共享内存 
  28.     if(shmctl(shmid, IPC_RMID, 0) == -1) 
  29.     { 
  30.         fprintf(stderr, "shmctl(IPC_RMID) failed\n"); 
  31.         exit(EXIT_FAILURE); 
  32.     }
  33. #endif 
  34.     shmid = shmget((key_t)12345, sizeof(struct shared_use_st), 0666|IPC_CREAT); 
  35.     if(shmid == -1) 
  36.     { 
  37.         fprintf(stderr, "shmget failed\n"); 
  38.         exit(EXIT_FAILURE); 
  39.     } 
  40.     //将共享内存连接到当前进程的地址空间 
  41.     shm = shmat(shmid, (void*)0, 0); 
  42.     if(shm == (void*)-1) 
  43.     { 
  44.         fprintf(stderr, "shmat failed\n"); 
  45.         exit(EXIT_FAILURE); 
  46.     } 
  47.     printf("Memory attached at %X\n", (int)shm); 
  48.     //设置共享内存 
  49.     shared = (struct shared_use_st*)shm;
  50.     sem_init(&shared->mutex,1,1);
  51.     
  52.     while(running)//向共享内存中写数据 
  53.     { 
  54.         strncpy(shared->text, buffer, TEXT_SZ); 

  55.         sem_wait(&shared->mutex);
  56.         //shared->cnt = 0;
  57.         shared->cnt = shared->cnt +i;    
  58.         sem_post(&shared->mutex);
  59.         i++;
  60.         shared->written = 1;

  61.         sleep(1);
  62.     } 
  63.     //把共享内存从当前进程中分离 
  64.     if(shmdt(shm) == -1) 
  65.     { 
  66.         fprintf(stderr, "shmdt failed\n"); 
  67.         exit(EXIT_FAILURE); 
  68.     } 
  69.     sleep(2); 
  70.     exit(EXIT_SUCCESS); 
  71. }
2. 读取操作

点击(此处)折叠或打开

  1. #include <unistd.h> 
  2. #include <stdlib.h> 
  3. #include <stdio.h> 
  4. #include <sys/shm.h> 
  5. //#include "shmdata.h" 
  6. #include <semaphore.h>




  7. #define TEXT_SZ 2048 
  8.   
  9. struct shared_use_st 
  10. { 
  11.     sem_t mutex;
  12.     int written;//作为一个标志,非0:表示可读,0表示可写 
  13.     char text[TEXT_SZ];//记录写入和读取的文本
  14.     int cnt;
  15. }; 

  16. int main() 
  17. { 
  18.     int running = 1;//程序是否继续运行的标志 
  19.     void *shm = NULL;//分配的共享内存的原始首地址 
  20.     struct shared_use_st *shared;//指向shm 
  21.     int shmid;//共享内存标识符 
  22.     //创建共享内存 
  23.     shmid = shmget((key_t)12345, sizeof(struct shared_use_st), 0666); 
  24.     if(shmid == -1) 
  25.     { 
  26.         fprintf(stderr, "shmget failed\n"); 
  27.         exit(EXIT_FAILURE); 
  28.     } 
  29.     //将共享内存连接到当前进程的地址空间 
  30.     shm = shmat(shmid, 0, 0); 
  31.     if(shm == (void*)-1) 
  32.     { 
  33.         fprintf(stderr, "shmat failed\n"); 
  34.         exit(EXIT_FAILURE); 
  35.     } 
  36.     printf("\nMemory attached at %X\n", (int)shm); 
  37.     //设置共享内存 
  38.     shared = (struct shared_use_st*)shm; 
  39.    // shared->written = 0; 
  40.     while(running)//读取共享内存中的数据 
  41.     { 

  42.         sem_wait(&shared->mutex);
  43.         printf("text:%s,cnt:%d\n",shared->text,shared->cnt);
  44.         sem_post(&shared->mutex);
  45.         sleep(1);


  46.     } 
  47.     //把共享内存从当前进程中分离 
  48.     if(shmdt(shm) == -1) 
  49.     { 
  50.         fprintf(stderr, "shmdt failed\n"); 
  51.         exit(EXIT_FAILURE); 
  52.     } 
  53. #if 0
  54.     //删除共享内存 
  55.     if(shmctl(shmid, IPC_RMID, 0) == -1) 
  56.     { 
  57.         fprintf(stderr, "shmctl(IPC_RMID) failed\n"); 
  58.         exit(EXIT_FAILURE); 
  59.     }
  60. #endif 
  61.     exit(EXIT_SUCCESS); 
  62. }

整体的用法还算比较简单. 也很高效方便. 
0 0
原创粉丝点击