目录
1.消息队列的定义:
消息队列是内核为我们创建的一个队列,通过这个队列的标识符key,每一个进程都可以打开这个队列,每个进程都可以通过向这个队列中插入一个结点(需要我们自己定义)或者获取一个结点来完成不同间的通信。这个队列的节点有一个类型。
消息队列发送的是一个带有数据类型的数据块(两边可以发送数据,可以接收数据)
2.消息队列如何传输数据?
用户组织带有一个数据类型的数据块,添加到队列中,其他进程从队列中获取数据块。也就是说,消息队列传输的是一个个带有类型的数据块,消息队列是全双工通信,可读可写(可以发送数据,也可以接收数据)
消息队列的生命周期随内核。
(1)创建消息队列(消息队列是创建在内核中的,如果没有关闭或没有释放消息队列,消息队列会一直存在于内核中,直到系统重启或以命令释放) mgget:创建 IPC_CREAT | IPC_EXCL
(2)发送数据/接收数据 msgsend/msgrcv
(3)释放消息队列 msgctl/ IPC_RMID
(4)操作系统中 IPC 相关命令:
ipcs:查看 IPC消息
-q:查看消息队列
-m:查看共享内存
-s:查看信号量
ipcrm (-s/-m/-q) msgid:删除指定的消息队列
msgrcv:msgtype
msgtype: 取队列中第一个结点,不分类型
msgtype>0: 区指定类型数据块的第一个结点
msgtype<0: 取小于msgtype绝对值类型的第一个结点
消息队列也有管道的不足,就是每个消息的最大长度是由于上限的,每个消息队列的字节长度也有上限
3.**共享内存(最快的IPC,进程间最快的通信方式)
3.1共享内存为什么比其他的通信方式快?
共享内存是直接申请物理内存映射到虚拟地址空间中(在往后对于这块内存的操作中不需要将数据拷贝到内核态,而是直接操作),因此在进行数据传输的时候相较于其他通信方式,少了内核态与用户态之间的拷贝,因此共享内存是最快的通信方式。
3.2共享内存的创建步骤:
(1)创建共享内存: shmget
(2)将共享内存映射到虚拟地址空间 shmat
(3)内存的数据操作(直接数据拷贝)
(4)不玩了
·解除映射关系:shmat
·删除共享内存(如果有进程依然与共享内存保持映射连接关系,那么共享内存颈部会被立即删除,而是等最后一个映射断开后,在这期间,将拒绝其他进程映射)shmtcl
4.代码示例:
4.1基于管道实现的聊天程序:
客户端:
//这是一个以system V消息队列实现的聊天程序客户端
// 1.创建消息队列
// 2.从消息队列中获取一个数据,打印出来
// 3.从标准输入 获取一个数据,组织成消息队列结点发送
// 4.不玩了。删除消息队列
// 消息队列的接口:
//int msgget(key_t key, int msgflg);
// key:内核中消息队列的标识
// msgflg:
// IPC_CREAT 不存在则创建,存在则打开
// IPC_EXECL 与IPC_CREAT同用时,若存在则报错
// mode 权限
// 返回值 :代码操作的句柄;失败:-1
// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
// int msgflg);
// msqid: msgget返回的句柄
// msgp :用于接收数据
// msgsz:指定接收数据的大小
// msgtyp:指定接收数据的类型
// msgflg:
// msgsend
// msgctl
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<sys/msg.h>
#include<sys/ipc.h>
//消息队列的key值
#define IPC_KEY 0x12345678
//这两个宏,用于我们赋值我们传送的数据块的类型
#define TYPE_S 1
#define TYPE_C 2
struct msgbuf{
long mtype;
char mtext[1024];
};
int main()
{
int msgid = -1;
//ftok
// key_t ftok(const char *pathname, int proj_id);
// ftok通过文件的iNode结点号和一个proj_id计算得出一个key值
// 缺点:
//
//
//1.创建消息队列
msgid = msgget(IPC_KEY, IPC_CREAT | 0664);
if(msgid < 0){
perror("msgget error");
return -1;
}
while(1){
//接收数据、
//struct msgbuf 这个结构体需要我们自己定义
struct msgbuf buf;
//发送数据
memset(&buf,0x00,sizeof(struct msgbuf));
buf.mtype = TYPE_S;
scanf("%s",buf.mtext);
msgsnd(msgid, &buf, 1024, 0);
//接收数据
memset(&buf, 0x00,sizeof(struct msgbuf));
msgrcv(msgid,&buf,1024,TYPE_C,0);
printf("cltient say:[%s]\n",buf.mtext);
}
//IPC_RMID 删除IPC
msgctl(msgid, IPC_RMID,NULL);
}
服务端:
//这是一个以system V消息队列实现的聊天程序客户端
// 1.创建消息队列
// 2.从消息队列中获取一个数据,打印出来
// 3.从标准输入 获取一个数据,组织成消息队列结点发送
// 4.不玩了。删除消息队列
// 消息队列的接口:
//int msgget(key_t key, int msgflg);
// key:内核中消息队列的标识
// msgflg:
// IPC_CREAT 不存在则创建,存在则打开
// IPC_EXECL 与IPC_CREAT同用时,若存在则报错
// mode 权限
// 返回值 :代码操作的句柄;失败:-1
// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
// int msgflg);
// msqid: msgget返回的句柄
// msgp :用于接收数据
// msgsz:指定接收数据的大小
// msgtyp:指定接收数据的类型
// msgflg:
// msgsend
// msgctl
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<sys/msg.h>
#include<sys/ipc.h>
//消息队列的key值
#define IPC_KEY 0x12345678
//这两个宏,用于我们赋值我们传送的数据块的类型
#define TYPE_S 1
#define TYPE_C 2
struct msgbuf{
long mtype;
char mtext[1024];
};
int main()
{
int msgid = -1;
//ftok
// key_t ftok(const char *pathname, int proj_id);
// ftok通过文件的iNode结点号和一个proj_id计算得出一个key值
// 缺点:
//
//
//1.创建消息队列
msgid = msgget(IPC_KEY, IPC_CREAT | 0664);
if(msgid < 0){
perror("msgget error");
return -1;
}
while(1){
//接收数据、
//struct msgbuf 这个结构体需要我们自己定义
struct msgbuf buf;
//msgid:操作句柄
//buf: 接收数据的结构体,需要自己定义
//1024:用于指定要接收的最大数据长度,不包含mtype
//TYPE_C :用于指定接收的数据类型
//msgflag: 0-默认
//msgrcv: 默认阻塞的接收数据
// MSG_NOERRER: 当数据长度超过指定长度,则截断数据
msgrcv(msgid,&buf,1024,TYPE_C,0);
printf("cltient say:[%s]\n",buf.mtext);
//发送数据
memset(&buf,0x00,sizeof(struct msgbuf));
buf.mtype = TYPE_S;
scanf("%s",buf.mtext);
msgsnd(msgid, &buf, 1024, 0);
}
//IPC_RMID 删除IPC
msgctl(msgid, IPC_RMID,NULL);
}
4.2基于共享内存实现的聊天程序
客户端:
//这是基于共享内存的一个进程间聊天程序
//共享内存的操作步骤
// 1.创建共享内存
// 2.映射共享内存到虚拟地址空间
// 3.数据拷贝
// 4.不玩了
// a) 解除映射关系
// b) 删除共享内存(如果有进程依然与共享内存保持映射连接关系,
// 那么共享内存将不会被立即删除,而是等最后一个映射断开后删除,
// 在这期间,将拒绝其他进程映射)
//
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define IPC_KEY 0x12345678
int main()
{
//1.创建共享内存
//int shmget(key_t key, size_t size, int shmflg);
// key :操作系统是上IPC 标识
// size: 要操作的共享内存的大小
// shmflag:
// IPC_CREAT |IPC_EXECL|0664
// 返回值: 操作句柄 失败:-1
int shmid=-1;
shmid = shmget(IPC_KEY,32,IPC_CREAT|0664);
if(shmid <0){
perror("shmid error");
return -1;
}
//将共享内存映射到虚拟地址空间
//void *shmat(int shmid, const void *shmaddr, int shmflg);
//shmat :操作句柄
//shmaddr: 映射起始地址
//shmflag: SHM_RDONLY--只读 否则读写
//返回值: 映射的是虚拟地址空间首地址
void *shm_start =shmat(shmid ,NULL,0);
if(shm_start ==(void*) -1){
perror("shmat error");
return -1;
}
while(1){
//直接通过这个首地址操作共享内存即可
printf("%s\n",(char*)shm_start);
sleep(1);
}
//不玩了 a.解除映射 b.删除
//解除映射
//int shamat(const void* shmaddr);
//shmaddr 共享内存的映射首地址
//返回值: 成功:0 失败:-1
shmdt(shm_start);
//删除共享内存
//int shmctl(int shmid, int cmd, struct shmid_ds *buf);
// shmid :句柄
// cmd: IPC_RMID 删除
// buf : 用于接收共享内存描述信息,不关心可以置空
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
服务端:
//这是基于共享内存的一个进程间的连天程序
//共享内存的操作步骤
// 1.创建共享内存
// 2.映射共享内存到虚拟地址空间
// 3.数据拷贝
// 4.不玩了
// a) 解除映射关系
// b) 删除共享内存(如果有进程依然与共享内存保持映射连接关系,
// 那么共享内存将不会被立即删除,而是等最后一个映射断开后删除,
// 在这期间,将拒绝其他进程映射)
//
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define IPC_KEY 0x12345678
int main()
{
//1.创建共享内存
//int shmget(key_t key, size_t size, int shmflg);
// key :操作系统是上IPC 标识
// size: 要操作的共享内存的大小
// shmflag:
// IPC_CREAT |IPC_EXECL|0664
// 返回值: 操作句柄 失败:-1
int shmid=-1;
shmid = shmget(IPC_KEY,32,IPC_CREAT|0664);
if(shmid <0){
perror("shmid error");
return -1;
}
//将共享内存映射到虚拟地址空间
//void *shmat(int shmid, const void *shmaddr, int shmflg);
//shmat :操作句柄
//shmaddr: 映射起始地址
//shmflag: SHM_RDONLY--只读 否则读写
//返回值: 映射的是虚拟地址空间首地址
void *shm_start =shmat(shmid ,NULL,0);
if(shm_start ==(void*) -1){
perror("shmat error");
return -1;
}
while(1){
//直接通过这个首地址操作共享内存即可
printf("please input:");
//清空一下共享内存中的数据
memset(shm_start,0x00,32);
fflush(stdout);
scanf("%s",(char*)shm_start);
sleep(1);
}
//不玩了 a.解除映射 b.删除
//解除映射
//int shamat(const void* shmaddr);
//shmaddr 共享内存的映射首地址
//返回值: 成功:0 失败:-1
shmdt(shm_start);
//删除共享内存
//int shmctl(int shmid, int cmd, struct shmid_ds *buf);
// shmid :句柄
// cmd: IPC_RMID 删除
// buf : 用于接收共享内存描述信息,不关心可以置空
shmctl(shmid,IPC_RMID,NULL);
return 0;
}