上周刚学完unix socket,这周尝试一下Linux环境另一款非常好用/非常常用的进程间通信方式——共享内存,这个共享内存同样是需要入门Linux代码的小白熟练掌握的,跟unix socket一样非常常用。共享内存,顾名思义就是不同的进程访问同一块内存,这块内存的特点,一是使用前需要手动申请给用户层;二是用户程序(进程)结束之后如果不手动释放的话会一直被占用且一直可被访问(读/写),外部终端查看被使用共享内存可用
[color=rgb(51, 102, 153) !important]复制代码
指令;三是这块内存若要不想出现访问冲突,需要手动加锁,不然的话被两个或多个进程同时改写,这个在实际项目中是不允许出现的,但是我还没学会怎么给共享内存加锁,现在这帖贴出的Demo先确保同一块共享内存不会被多个进程同时改写,用的思想还是跟上一帖socket的思想一样,两个进程的读和写用两块不同的共享内存实现,比如说进程A和进程B,共享内存A和共享内存B,共享内存A只能被进程A写, 被进程B读,共享内存B只能被进程B写,被进程A读;四是内存访问速度比socket快,用在效率要求高的场合项目;五是需要借助key文件进行shm地址的定位,以便确保进程A和进程B访问的共享内存地址(或shmid)是正确的,这个key文件可以是touch新建出来的文件,反正都会被进程改写;六是不同进程之间地位平等,不像socket那样分主进程和从进程,主进程gg从进程跟着gg,从这点看的话,共享内存不需要担心某个进程崩溃的问题,这是相对socket通信的优点。
上代码,进程1:
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <pthread.h>
- key_t key1 , key2;
- int shmid1 , shmid2;
- char *p1 = NULL , *p2 = NULL;
- pthread_t id1;
- void *Thread_Send(void *arg)
- {
- while(1)
- {
- fgets(p2,10,stdin);
- }
- }
- int main(int argc, const char *argv[])
- {
- key1 = ftok("./app1",'b'); //获取唯一的 key 值,
- if(key1 < 0)
- {
- perror("fail ftok ");
- return -1;
- }
- shmid1 = shmget(key1, 128, IPC_CREAT|IPC_EXCL|0777);
- //创建/打开共享内存,返回id根据id映射
- if(shmid1 < 0)
- {
- if(errno == EEXIST)
- //文件存在时,直接打开文件获取shmid
- {
- printf("file eexist");
- shmid1 = shmget(key1,128,0777);
- }
- else
- {
- perror("shmget fail ");
- return -2;
- }
- }
- p1 = (char *)shmat(shmid1,NULL,0);
- //映射,返回地址,根据地址操作
- if( p1 == (char *)(-1) )
- {
- perror("shmat fail ");
- return -3;
- }
- key2 = ftok("./app2",'b'); //获取唯一的 key 值,
- if(key2 < 0)
- {
- perror("fail ftok ");
- return -1;
- }
- shmid2 = shmget(key2, 128, IPC_CREAT|IPC_EXCL|0777);
- //创建/打开共享内存,返回id根据id映射
- if(shmid2 < 0)
- {
- if(errno == EEXIST)
- //文件存在时,直接打开文件获取shmid
- {
- printf("file eexist");
- shmid2 = shmget(key2,128,0777);
- }
- else
- {
- perror("shmget fail ");
- return -2;
- }
- }
- p2 = (char *)shmat(shmid2 , NULL , 0);
- //映射,返回地址,根据地址操作
- if(p2 == (char *)(-1) )
- {
- perror("shmat fail ");
- return -3;
- }
- pthread_create(&id1,NULL,Thread_Send,NULL);
- while(1)
- {
- sleep(1);
- printf("P:%s\n",p1);
- }
- shmdt(p1);
- //解除映射
- shmctl(shmid1,IPC_RMID,NULL);
- //删除
- return 0;
- }
[color=rgb(51, 102, 153) !important]复制代码
上代码,进程2:
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <pthread.h>
- key_t key1 , key2;
- int shmid1 , shmid2;
- char *p1 = NULL , *p2 = NULL;
- pthread_t id1;
- void *Thread_Send(void *arg)
- {
- while(1)
- {
- fgets(p1,10,stdin);
- }
- }
- int main(int argc, const char *argv[])
- {
- key1 = ftok("./app1",'b');
- if(key1 < 0)
- {
- perror("fail ftok ");
- exit(1);
- }
- shmid1 = shmget(key1,128,IPC_CREAT|IPC_EXCL|0777);//创建/打开共享内存,返回id根据id映射
- if(shmid1 < 0)
- {
- if(errno == EEXIST)
- {
- printf("file eexist");
- shmid1 = shmget(key1,128,0777);
- }
- else
- {
- perror("shmget fail ");
- exit(1);
- }
- }
- p1 = (char *)shmat(shmid1,NULL,0);//映射,返回地址,根据地址操作
- if( p1 == (char *)(-1) )
- {
- perror("shmat fail ");
- exit(1);
- }
- key2 = ftok("./app2",'b'); //创建key值,
- if(key2 < 0)
- {
- perror("fail ftok ");
- exit(1);
- }
- shmid2 = shmget(key2,128,IPC_CREAT|IPC_EXCL|0777);//创建/打开共享内存,返回id根据id映射
- if(shmid2 < 0)
- {
- if(errno == EEXIST)//文件存在时,直接打开文件获取shmid
- {
- printf("file eexist");
- shmid2 = shmget(key2,128,0777); //共享内存存在时,直接打开
- }
- else
- {
- perror("shmget fail ");
- exit(1);
- }
- }
- p2 = (char *)shmat(shmid2,NULL,0);//映射,返回地址,根据地址操作
- if( p2 == (char *)(-1) )
- {
- perror("shmat fail ");
- exit(1);
- }
- pthread_create(&id1,NULL,Thread_Send,NULL);
- while(1)
- {
- sleep(1);
- printf("P:%s\n",p2);
- }
- shmdt(p1);
- //解除映射
- //函数原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- shmctl(shmid1,IPC_RMID,NULL); //删除
- return 0;
- }
[color=rgb(51, 102, 153) !important]复制代码
无论是先运行进程1还是进程2,都需要先在文件夹内创建key文件app1和app2,以便定位共享内存shmid:
[color=rgb(51, 102, 153) !important]复制代码
运行效果:
从终端看不出fgets输入和打印输出的区别,不过两个进程读写通信是没有任何问题的。
|
|