STM32/STM8技术论坛
直播中

donatello1996

8年用户 687经验值
擅长:处理器/DSP 控制/MCU RF/无线
私信 关注
[经验]

【100ASK_IMX6ULL(带屏) 开发板试用体验】基于shm共享内存的双进程全双工通信

`       上周刚学完unix socket,这周尝试一下Linux环境另一款非常好用/非常常用的进程间通信方式——共享内存,这个共享内存同样是需要入门Linux代码的小白熟练掌握的,跟unix socket一样非常常用。共享内存,顾名思义就是不同的进程访问同一块内存,这块内存的特点,一是使用前需要手动申请给用户层;二是用户程序(进程)结束之后如果不手动释放的话会一直被占用且一直可被访问(读/写),外部终端查看被使用共享内存可用
  1. ipcs -a

指令;三是这块内存若要不想出现访问冲突,需要手动加锁,不然的话被两个或多个进程同时改写,这个在实际项目中是不允许出现的,但是我还没学会怎么给共享内存加锁,现在这帖贴出的Demo先确保同一块共享内存不会被多个进程同时改写,用的思想还是跟上一帖socket的思想一样,两个进程的读和写用两块不同的共享内存实现,比如说进程A和进程B,共享内存A和共享内存B,共享内存A只能被进程A写, 被进程B读,共享内存B只能被进程B写,被进程A读;四是内存访问速度比socket快,用在效率要求高的场合项目;五是需要借助key文件进行shm地址的定位,以便确保进程A和进程B访问的共享内存地址(或shmid)是正确的,这个key文件可以是touch新建出来的文件,反正都会被进程改写;六是不同进程之间地位平等,不像socket那样分主进程和从进程,主进程gg从进程跟着gg,从这点看的话,共享内存不需要担心某个进程崩溃的问题,这是相对socket通信的优点。
33.jpg

      上代码,进程1:
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #include
  9. #include

  10. key_t key1 , key2;
  11. int shmid1 , shmid2;
  12. char *p1 = NULL , *p2 = NULL;
  13. pthread_t id1;

  14. void *Thread_Send(void *arg)
  15. {
  16.     while(1)
  17.     {
  18.                 fgets(p2,10,stdin);
  19.         }
  20. }

  21. int main(int argc, const char *argv[])
  22. {

  23.     key1 = ftok("./app1",'b'); //获取唯一的 key 值,
  24.     if(key1 < 0)
  25.     {
  26.         perror("fail ftok ");
  27.         return -1;
  28.     }

  29.     shmid1 = shmget(key1, 128, IPC_CREAT|IPC_EXCL|0777);
  30.         //创建/打开共享内存,返回id根据id映射
  31.        
  32.     if(shmid1 < 0)
  33.     {
  34.         if(errno == EEXIST)
  35.                 //文件存在时,直接打开文件获取shmid
  36.         {
  37.             printf("file eexist");
  38.             shmid1 = shmget(key1,128,0777);
  39.         }
  40.         else
  41.         {
  42.             perror("shmget fail ");
  43.             return -2;
  44.         }
  45.     }
  46.     p1 = (char *)shmat(shmid1,NULL,0);
  47.         //映射,返回地址,根据地址操作
  48.     if( p1 == (char *)(-1) )
  49.     {
  50.         perror("shmat fail ");
  51.         return -3;
  52.     }

  53.         key2 = ftok("./app2",'b'); //获取唯一的 key 值,
  54.     if(key2 < 0)
  55.     {
  56.         perror("fail ftok ");
  57.         return -1;
  58.     }

  59.     shmid2 = shmget(key2, 128, IPC_CREAT|IPC_EXCL|0777);
  60.         //创建/打开共享内存,返回id根据id映射
  61.        
  62.     if(shmid2 < 0)
  63.     {
  64.         if(errno == EEXIST)
  65.                 //文件存在时,直接打开文件获取shmid
  66.         {
  67.             printf("file eexist");
  68.             shmid2 = shmget(key2,128,0777);
  69.         }
  70.         else
  71.         {
  72.             perror("shmget fail ");
  73.             return -2;
  74.         }
  75.     }
  76.     p2 = (char *)shmat(shmid2 , NULL , 0);
  77.         //映射,返回地址,根据地址操作
  78.     if(p2 == (char *)(-1) )
  79.     {
  80.         perror("shmat fail ");
  81.         return -3;
  82.     }
  83.         pthread_create(&id1,NULL,Thread_Send,NULL);
  84.     while(1)
  85.     {
  86.         sleep(1);
  87.         printf("P:%s
  88. ",p1);
  89.     }
  90.     shmdt(p1);
  91.         //解除映射
  92.        
  93.     shmctl(shmid1,IPC_RMID,NULL);
  94.         //删除
  95.     return 0;
  96. }


      上代码,进程2:
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #include
  9. #include

  10. key_t key1 , key2;
  11. int shmid1 , shmid2;
  12. char *p1 = NULL , *p2 = NULL;
  13. pthread_t id1;

  14. void *Thread_Send(void *arg)
  15. {
  16.     while(1)
  17.     {
  18.                 fgets(p1,10,stdin);
  19.         }
  20. }

  21. int main(int argc, const char *argv[])
  22. {

  23.     key1 = ftok("./app1",'b');
  24.     if(key1 < 0)
  25.     {
  26.         perror("fail ftok ");
  27.         exit(1);
  28.     }
  29.     shmid1 = shmget(key1,128,IPC_CREAT|IPC_EXCL|0777);//创建/打开共享内存,返回id根据id映射
  30.     if(shmid1 < 0)
  31.     {
  32.         if(errno == EEXIST)
  33.         {
  34.             printf("file eexist");
  35.             shmid1 = shmget(key1,128,0777);
  36.         }
  37.         else
  38.         {
  39.             perror("shmget fail ");
  40.             exit(1);
  41.         }
  42.     }
  43.     p1 = (char *)shmat(shmid1,NULL,0);//映射,返回地址,根据地址操作
  44.     if( p1 == (char *)(-1) )
  45.     {
  46.         perror("shmat fail ");
  47.         exit(1);
  48.     }

  49.         key2 = ftok("./app2",'b'); //创建key值,
  50.     if(key2 < 0)
  51.     {
  52.         perror("fail ftok ");
  53.         exit(1);
  54.     }
  55.     shmid2 = shmget(key2,128,IPC_CREAT|IPC_EXCL|0777);//创建/打开共享内存,返回id根据id映射
  56.     if(shmid2 < 0)
  57.     {
  58.         if(errno == EEXIST)//文件存在时,直接打开文件获取shmid
  59.         {
  60.             printf("file eexist");
  61.             shmid2 = shmget(key2,128,0777); //共享内存存在时,直接打开
  62.         }
  63.         else
  64.         {
  65.             perror("shmget fail ");
  66.             exit(1);
  67.         }
  68.     }
  69.     p2 = (char *)shmat(shmid2,NULL,0);//映射,返回地址,根据地址操作
  70.     if( p2 == (char *)(-1) )
  71.     {
  72.         perror("shmat fail ");
  73.         exit(1);
  74.     }
  75.         pthread_create(&id1,NULL,Thread_Send,NULL);
  76.     while(1)
  77.     {
  78.         sleep(1);
  79.         printf("P:%s
  80. ",p2);
  81.     }
  82.     shmdt(p1);
  83.         //解除映射

  84.     //函数原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  85.     shmctl(shmid1,IPC_RMID,NULL); //删除
  86.     return 0;
  87. }




无论是先运行进程1还是进程2,都需要先在文件夹内创建key文件app1和app2,以便定位共享内存shmid:
  1. touch app1
  2. touch app2
34.jpg


运行效果:
36.jpg

从终端看不出fgets输入和打印输出的区别,不过两个进程读写通信是没有任何问题的。
` 35.jpg

更多回帖

发帖
×
20
完善资料,
赚取积分