进程间通信 IPC :管道、信号量、共享内存、消息队列、套接字
进程间通信,两个进程间传递信息
除了套接字,前面几个主要是在同一台主机上两个进程间通信
共享内存为多个进程之间共享和传递数据提供了一种有效的方式。共享内存是先在物理内存上申请一块空间,多个进程可以将其映射到自己的虚拟地址空间中。所有进程都可以访问共享内存中的地址,就好像它们是由 malloc 分配的一样。如果某个进程向共享内存写入了 数据,所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。由于它并未提供同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问。
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <sys/types.h>
int shmget(key_t key, size_t size, int shmflg); //创建共享内存
shmget() 用于创建或者获取共享内存
shmget() 成功返回共享内存的 ID, 失败返回-1
key: 不同的进程使用相同的 key 值可以获取到同一个共享内存
size: 创建共享内存时,指定要申请的共享内存空间大小,以字节为单位
shmflg: 包含9个比特的权限标志
IPC_CREAT IPC_EXCL
IPC_CREAT:存在则打开,不存在则创建
IPC_EXCL和IPC_CREAT同时使用存在则会报错,不存在创建;
(权限直接加在后面进行赋值)
void* shmat(int shmid, const void *shmaddr, int shmflg); //映射,将共享内存映射到进程的地址空间
shmat() 将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上
shmat() 成功返回返回共享内存的首地址,失败返回 NULL
shmaddr:一般给NULL,由系统自动选择映射的虚拟地址空间
shmflg: 一组标志位,SHM_RND(这个标志与shm_addr联合使用,用来控制共享内存链接共享内存链接的地址) SHM_RDONLY(它使得连接的内存只读),不指定时给0就可以
int shmdt(const void *shmaddr); //断开映射
shmdt() 断开当前进程的 shmaddr 指向的共享内存映射
shmdt() 成功返回 0, 失败返回-1
shmaddr 返回的地址指针
int shmctl(int shmid, int cmd, struct shmid_ds *buf); //控制共享内存,eg:删除
shmctl() 控制共享内存
shmctl() 成功返回 0,失败返回-1
cmd: 要采取的动作
IPC_STAT 把shmid__ds结构中的数据设置为共亨内存的当前关联值
IPC_SET 如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID 删除共享内存段buf: 是一个指针,它指向包含共享内存模式和访问权限的结构。
进程 a向共享内存中写入数据,进程 b 从共享内存中读取数据并显示 shma.c 的代码:
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<string.h>
- #include<sys/shm.h>
-
- int main()
- {
- //创建共享内存
- int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);//key值为1234,共享内存大小为128,IPC_CREAT 存在则打开,不存在创建,权限0600
- if(shmid==-1)
- {
- printf("shmget err\n");//创建失败
- exit(1);
- }
-
- //映射,将共享内存映射到进程的地址空间
- //映射成功后就可以通过指针s访问此空间
- char*s=(char*)shmat(shmid,NULL,0);//shmat本身返回值void*类型,强转为char*,shmid 共享内存id,给NULL。系统自动选择映射的虚拟地址空间标志位不指定为0
- if(s==(char *)-1)
- {
- printf("shmat err\n");//映射失败
- exit(1);
- }
-
- //向共享内存写入数据
- strcpy(s,"hello");
-
- shmdt(s);//断开映射
-
- exit(0);
-
- }
-
-
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<string.h>
- #include<sys/shm.h>
-
- int main()
- {
- //创建共享内存,存在就获取共享内存
- int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);//key值为1234,共享内存大小为128,IPC_CREAT 存在则打开,不存在创建,权限0600
- if(shmid==-1)
- {
- printf("shmget err\n");//创建失败
- exit(1);
- }
-
- //映射,将共享内存映射到进程的地址空间
- //映射成功后就可以通过指针s访问此空间
- char*s=(char*)shmat(shmid,NULL,0);//shmat本身返回值void*类型,强转为char*,shmid 共享内存id,给NULL。系统自动选择映射的虚拟地址空间 标
- 志位不指定为0
- if(s==(char *)-1)
- {
- printf("shmat err\n");//映射失败
- exit(1);
- }
-
- //用test文件读取共享内存空间中的值
- printf("read:%s\n",s);
-
-
- shmdt(s);//断开映射
-
- exit(0);
-
- }
共享内存创建成功后,会一直存在,即使程序运行结束,也还会存在,除非删除此共享内存或者重启
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<string.h>
- #include<sys/shm.h>
-
- int main()
- {
- //创建共享内存
- int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);//key值为1234,共享内存大小为128,IPC_CREAT 存在则打开,不存在创建,权限0600
- if(shmid==-1)
- {
- printf("shmget err\n");//创建失败
- exit(1);
- }
-
- //映射,将共享内存映射到进程的地址空间
- //映射成功后就可以通过指针s访问此空间
- char*s=(char*)shmat(shmid,NULL,0);//shmat本身返回值void*类型,强转为char*,shmid 共享内存id,给NULL。系统自动选择映射的虚拟地址空间标志位不指定为0
- if(s==(char *)-1)
- {
- printf("shmat err\n");//映射失败
- exit(1);
- }
-
- while(1)
- {
- printf("input\n");
- char buff[128]={0};//将输入的的数据放在buff中
- fgets(buff,128,stdin);
- strcpy(s,buff);//先将数据写入共享内存是为了也可以让读取数据时以end为结束标志
- if(strncmp(buff,"end",3)==0)
- {
- break;//以end为输入结束的标志
- }
-
- }
- shmdt(s);//断开映射
-
- exit(0);
-
- }
-
-
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<string.h>
- #include<sys/shm.h>
-
- int main()
- {
- //创建共享内存,存在就获取共享内存
- int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);//key值为1234,共享内存大小为128,IPC_CREAT 存在则打开,不存在创建,权限0600
- if(shmid==-1)
- {
- printf("shmget err\n");//创建失败
- exit(1);
- }
-
- //映射,将共享内存映射到进程的地址空间
- //映射成功后就可以通过指针s访问此空间
- char*s=(char*)shmat(shmid,NULL,0);//shmat本身返回值void*类型,强转为char*,shmid 共享内存id,给NULL。系统自动选择映射的虚拟地址空间 标
- 志位不指定为0
- if(s==(char *)-1)
- {
- printf("shmat err\n");//映射失败
- exit(1);
- }
-
- while(1)
- {
- if(strncmp(s,"end",3)==0)
- {
- break;//先判断是不是结束标志,不是再输出
- }
- sleep(1);
- printf("resf:%s\n",s);//以此读取数据
- }
-
-
-
-
- shmdt(s);//断开映射
-
- exit(0);
-
- }
-
-
运行结果如下:
我们可以看到,b是可以读到共享内存的,a输入数据b可以读取,但是只要我们不输入新的数据,b就一直读取上一个数据,
我们想要的结果是写一次,读一次,不写就不读,我们可以通过信号量解决这个问题
此时我们需要两个信号量,s1,s2
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<string.h>
- #include<sys/sem.h>
-
- #define SEM1 0 //第一个信号量
- #define SEM2 1 //第二个信号量
- #define NUM 2 //信号量总个数
-
-
- union semun
- {
- int val;//初始值
- };//自己定义
-
- void sem_init();//信号量初始化
- void sem_p(int index);//P操作,创建两个信号量,用下标区分
- void sem_v(int index);//V操作
- void sem_destroy();//销毁
- #include"sem.h"
-
- static int semid = -1;//信号量id,只在本文件有效
-
- void sem_init()//信号量初始化
- {
- semid = semget((key_t)1234,NUM,IPC_CREAT|IPC_EXCL|0600);//key值为1234,SEM个信号量,全新创建一个信号量,如果已存在就报错,存取权限0600
- if(semid == -1)//全新创建失败,说明已存在或者创建失败
- {
- semid=semget((key_t)1234,NUM,0600);
- //semid = semget((key_t)1234,SEM,0600);//当信号已存在,获取信号量
- if(semid == -1)
- {
- printf("semget err\n");//前面获取失败说明是创建的时候就失败了
-
- }
- }
- else//因为要创建NUM个信号量,循环初始化
- {
- union semun a;
- int arr[NUM]={1,0};//信号量初始值数组
- for(int i=0;i<NUM;i++)
- {
- a.val = arr[i];//信号量的初始值
- if(semctl(semid,i,SETVAL,a)==-1)//i,信号量个数。SETVAL,a,赋初值
- {
- printf("semctl init err\n");//初始化失败打印此语句
- }
- }
-
- }
-
- }
-
- void sem_p(int index)//P操作
- {
-
- if(index<0 || index>=NUM)
- {
- return;
- }
- struct sembuf buf;//对信号量的改变
- buf.sem_num=index;//信号量的下标
- buf.sem_op=-1;//对信号量执行的是p操作
- buf.sem_flg = SEM_UNDO;//标志位,如果程序异常可以帮助进行v操作
-
- if(semop(semid,&buf,1)==-1)
- {
- printf("semop p err\n");
- }
-
- }
-
- void sem_v(int index)//V操作
- {
- if(index<0 || index>=NUM)
- {
- return;
- }
-
- struct sembuf buf;//对信号量的改变
- buf.sem_num=index;//信号量的下标
- buf.sem_op=1;//对信号量执行的是v操作
- buf.sem_flg = SEM_UNDO;
-
- if(semop(semid,&buf,1)==-1)
- {
- printf("semop v err\n");
- }
-
- }
-
- void sem_destroy()//销毁
- {
- if(semctl(semid,0,IPC_RMID)==-1)//semid 要删除的信号,0 占位,根据semid删除创建的所有信号。IPC_RMID 删除信号量
- {
- printf("semctl destroy err\n");
- }
-
- }
-
-
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<string.h>
- #include<sys/shm.h>
- #include"sem.h"
-
- int main()
- {
- //创建共享内存
- int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);//key值为1234,共享内存大小为128,IPC_CREAT 存在则打开,不存在创建,权限0600
- if(shmid==-1)
- {
- printf("shmget err\n");//创建失败
- exit(1);
- }
-
- //映射,将共享内存映射到进程的地址空间
- //映射成功后就可以通过指针s访问此空间
- char*s=(char*)shmat(shmid,NULL,0);//shmat本身返回值void*类型,强转为char*,shmid 共享内存id,给NULL。系统自动选择映射的虚拟地址空间 标>
- 志位不指定为0
- if(s==(char *)-1)
- {
- printf("shmat err\n");//映射失败
- exit(1);
- }
-
- sem_init();//信号量初始化
- while(1)
- {
- printf("input\n");
- char buff[128]={0};//将输入的的数据放在buff中
- fgets(buff,128,stdin);
- sem_p(SEM1);//对a文件先P操作
- strcpy(s,buff);//先将数据写入共享内存是为了也可以让读取数据时以end为结束标志
- sem_v(SEM2);//对b文件进行v操作,不能将v操作放在下面的判读之后,因为如果最后输出end结束后,b文件不能进行v操作获得权限就无法打印最后
- 一个
- if(strncmp(buff,"end",3)==0)
- {
- break;//以end为输入结束的标志
- }
-
- }
-
- shmdt(s);//断开映射
-
- exit(0);
-
- }
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<string.h>
- #include<sys/shm.h>
- #include"sem.h"
-
- int main()
- {
- //创建共享内存,存在就获取共享内存
- int shmid = shmget((key_t)1234,128,IPC_CREAT|0600);//key值为1234,共享内存大小为128,IPC_CREAT 存在则打开,不存在创建,权限0600
- if(shmid==-1)
- {
- printf("shmget err\n");//创建失败
- exit(1);
- }
-
- //映射,将共享内存映射到进程的地址空间
- //映射成功后就可以通过指针s访问此空间
- char*s=(char*)shmat(shmid,NULL,0);//shmat本身返回值void*类型,强转为char*,shmid 共享内存id,给NULL。系统自动选择映射的虚拟地址空间 标>
- 志位不指定为0
- if(s==(char *)-1)
- {
- printf("shmat err\n");//映射失败
- exit(1);
- }
-
- sem_init();//初始化信号量
- while(1)
- {
- sem_p(SEM2);
- if(strncmp(s,"end",3)==0)
- {
- break;//先判断是不是结束标志,不是再输出
- }
- printf("resf:%s\n",s);//以此读取数据
- sem_v(SEM1);
- }
-
-
- shmdt(s);//断开映射
-
- sem_destroy();//销毁信号量
- exit(0);
-
- }
运行程序
运行结果