进程间的通信————消息队列&共享内存

目录

 

1.消息队列的定义:

2.消息队列如何传输数据?

3.**共享内存(最快的IPC,进程间最快的通信方式)

4.代码示例:


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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值