目录
1、IPC对象
在linux下,IPC对象指的是 消息队列、共享内存、信号量
如果用户需要使用IPC对象来进行进程之间的通信,首先必须为IPC对象申请对应的资源
IPC对象是进程通信方式的子集
信息队列:Message Queuing 共享内存:Shared memory 信号量:Semaphores
1)想要操作文件(设备文件/dev): 需要获得的资源: >文件的路径名 >文件的文件描述符
2)想要操作消息队列: 需要获得的资源: >需要获得key值------文件的路径名 >ID号 -----文件的文件描述符
2、查看IPC对象的指令
1)查看IPC对象: ipcs -a
key值: 类似于 文件的路径名
ID号: 类似于 文件描述符
2)删除IPC对象
想删除消息队列: ipcrm -q 消息队列的key值 / ipcrm -q 消息队列的ID值 (ipc remove -queue)
想删除共享内存: ipcrm -m 共享内存的key值 / ipcrm -m 共享内存的ID值 (ipc remove -memory)
想删除信号量: ipcrm -s 信号量的key值 / ipcrm -s 信号量的ID值 (ipc remove sem)
3、IPC对象 的 key值申请
申请key值 ftok --> man 3 ftok
#include <sys/types.h>
#include <sys/ipc.h>
key的定义:key_t key;
key_t ftok(const char *pathname, int proj_id);
函数作用: 获得一个key值
参数:pathname: 一个合法的路径。 常用 "."
proj_id: 非0整数。 常用 10
返回值:成功 key值
失败 -1
The resulting value is the same for all pathnames that name the same file, when the same value of proj_id is used.
当文件路径pathname 和 proj_id 是一样的时候,两个ftok函数的返回值---key是一样的。
The value returned should be different when the (simultaneously existing) files or the project IDs differ.
只要文件路径pathname 或者 proj_id 有一个不一样,返回的key值 就是不一样的。
结论:如果想要使用IPC对象实现两个进程之间的通信,那么两个进程的IPC对象的key值必须是一样的。
4、消息队列
消息队列是属于IPC对象,所以使用之前一定要先申请key值
2.管道通信跟消息队列的区别
1)管道通信: 不能读取指定的数据,只要管道中有数据,就一定要读取出来,操作的时候使用 open / write read -->文件描述符(系统IO来操作 write read)
2)消息队列: 消息队列是一种带有数据标识的特殊管道,消息队列可以读取指定的数据,如果里面有多个数据,但是不符合我的类型,我可以不读取, 操作时候使用消息队列中独有的函数接口:msgsnd / msgrcv
消息队列机制:
进程1往消息队列中写入数据时,“类型”+ “数据正文” //类型 ---数据的编号
进程2从消息队列中读取数据时,只需要提供数据的编号就可以读取到指定的数据了
消息队列作用的范围: linux下任意两个进程
5、使用消息队列通信的步骤
1.申请消息队列的key值
2.根据消息队列的key值获取ID号 ,如果该key值对应的消息不存在,会创建 -->man 2 msgget
3.发送 /写入数据 ----> man 2 msgsnd
4.从消息队列中读取数据(接收数据) --->man 2 msgrcv
5.删除消息队列 msgctl = message contol
6、消息队列相关函数说明
发送端和接收端都需要定义一个数据结构体,并且使用memset对结构体对象清0
struct msgbuf
{
long mtype; // 消息类型/数据的编号 而且是>0
char mtext[1024]; // 数据的正文
};
根据消息队列的key值获取ID号 ,如果该key值对应的消息不存在,会创建 ---> man 2 msgget
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgid=msgget(key_t key,IPC_CREAT|0666);
int msgget(key_t key, int msgflg);
函数作用:得到一个消息队列的ID号
参数:key:消息队列的key值
msgflg: IPC_CREAT|0666 如果不存在则创建。并且给权限
返回值:成功返回 消息队列的ID号
失败返回 -1
从消息队列中写入数据 ---> man 2 msgsnd
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
函数作用:往消息队列中写入数据
参数:msqid:消息队列的ID号
msgp:你要写入的数据,注意传递的是写入的数据结构体的地址
msgsz:数据正文的大小,也就是 char mtext[1024] 的大小,(注意不是整个结构体的大小)
msgflg:一般属性,默认为0
返回值:成功返回 0
失败返回 -1
从消息队列中读取数据(接收数据) --> man 2 msgrcv
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数:msqid:消息队列的ID号
msgp:读取的数据存储到这里,注意 传递的是 读取的数据结构体的地址
msgsz:数据正文的大小,也就是 char mtext[1024] 的大小,(注意不是整个结构体的大小)
msgtyp:读取的数据的类型或者说数据的编号
msgflg:一般属性,默认为0
返回值:成功返回 读取的字节数
失败 返回 -1
删除消息队列 msgctl (message contol) --> man 2 msgctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf); //cmd --->command
参数:msqid:你要删除哪条消息队列,将消息队列的ID号传递过来
cmd: 操作的命令
IPC_STAT -->获取消息队列的状态 --> 最后一个参数要填
struct msqid_ds 变量名(&变量名)
IPC_RMID-->删除消息队列 ---> 删除不需要第三个参数,但是要填NULL
buf: 获取的那些数据 存储到这个结构体里面
返回值:成功返回 0
失败返回 -1
比如删除消息队列:
msgctl(msgid,IPC_RMID,NULL);(msg的通常用法)
父进程利用sleep,多数是子进程使用msgctl删除消息队列
7、消息队列测试代码如下:
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
#include<sys/wait.h>
#include<unistd.h>
struct msgbuf
{
long mtype; //根据结构体的类型变量来决定读取哪种类型的数据
char mtext[1024];
};
//消息队列_rd.c
int main()
{
//1、申请key值
key_t key = ftok(".",10);
//2、获取id
int msgid = msgget(key,IPC_CREAT|0666);
if(msgid == -1)
{
printf("msgget msgid fail\n");
return -1;
}
printf("消息队列 key:%#x msgid:%d\n",key,msgid);
//3、初始化消息队列信息结构体
struct msgbuf msgdata;
memset(&msgdata,0,sizeof(struct msgbuf));
msgdata.mtype = 20;
pid_t id = fork();
if(id < 0)
{
printf("fork fail\n");
return -1;
}
else if(id > 0)
{
while(1)
{
//printf("请输入消息队列正文:");
scanf("%s",msgdata.mtext);
//发送消息
msgsnd(msgid,&msgdata,strlen(msgdata.mtext),0);
if(strcmp(msgdata.mtext,"byebye") == 0)
{
break;
}
}
wait(NULL);
msgctl(msgid,IPC_RMID,NULL);
}
else if(id == 0)
{
while(1)
{
memset(&msgdata,0,sizeof(struct msgbuf));
//接收消息
msgrcv(msgid,&msgdata,sizeof(msgdata),10,0);
printf("子进程:消息队列的正文:%s\n",msgdata.mtext);
if(strcmp(msgdata.mtext,"byebye") == 0)
{
break;
}
}
}
return 0;
}
//消息队列_wr.c
int main()
{
//1、申请key值
key_t key = ftok(".",10);
//2、获取id
int msgid = msgget(key,IPC_CREAT|0666);
if(msgid == -1)
{
printf("msgget msgid fail\n");
return -1;
}
printf("消息队列 key:%#x msgid:%d\n",key,msgid);
//3、初始化消息队列信息结构体
struct msgbuf msgdata;
memset(&msgdata,0,sizeof(struct msgbuf));
msgdata.mtype = 10;
pid_t id = fork();
if(id < 0)
{
printf("fork fail\n");
return -1;
}
else if(id > 0)
{
while(1)
{
//printf("请输入消息队列正文:");
scanf("%s",msgdata.mtext);
//发送消息
msgsnd(msgid,&msgdata,strlen(msgdata.mtext),0);
if(strcmp(msgdata.mtext,"byebye") == 0)
{
break;
}
}
wait(NULL);
msgctl(msgid,IPC_RMID,NULL);
}
else if(id == 0)
{
while(1)
{
memset(&msgdata,0,sizeof(struct msgbuf));
//接收消息
msgrcv(msgid,&msgdata,sizeof(msgdata),20,0);
printf("子进程:消息队列的正文:%s\n",msgdata.mtext);
if(strcmp(msgdata.mtext,"byebye") == 0)
{
break;
}
}
}
return 0;
}