linux-进程通信(2)消息队列

消息队列

消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。
特点
(1)消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级(链表存放的为结构体)。
(2)消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
(3)消息队列可以实现消息的随机查询(链表的特性),也可以按消息的类型读取。
(4)与无名管道、有名管道一样,从消息队列中读出消息,消息队列中数据会被删除。

在这里插入图片描述
常用api

 int msgget(key_t key, int flag);
// 创建或打开消息队列:成功返回队列ID,失败返回-1

 int msgsnd(int msqid, const void *ptr, size_t size, int flag);
 // 添加消息:成功返回0,失败返回-1

 int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
 // 读取消息:成功返回消息数据的长度,失败返回-1


int msgctl(int msqid, int cmd, struct msqid_ds *buf);
 // 控制消息队列:成功返回0,失败返回-1

 key_t ftok( const char * fname, int id )
//系统IPC键值的格式转换函数

msgget()

创建或打开消息队列:成功返回队列ID,失败返回-1

int msgget(key_t key, int flag);

参数:
key :消息队列的键值

多个进程可以通过它访问同一个消息队列,如果key为IPC_PRIVATE 意思就是创建一个只能被创建进程读写的消息队列;
如果不是IPC_PRIVATE .则我们可以指定一个值0x1234,还可以用ftok()函数来获得一个唯一的键值。

flag : 创建消息队列的创建方式或权限。

IPC_CREAT :(需要指定权限:IPC_CREAT|0600)
如果消息队列对象不存在,则创建消息队列,否则则进行打开操作;
IPC_EXCL:
和IPC_CREAT 一起使用(用”|”连接),如果消息对象不存在则创建,否则产生一个错误并返回。

msgsnd()

添加消息:成功返回0,失败返回-1

int msgsnd(int msqid, const void *ptr, size_t size, int flag);
//例
 msgend(msgId,&sndBuf,strlen(sndBuf.mtext),0);

参数:
msqid:消息队列的ID
ptr:发送给队列的消息。写入的数据,指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:

struct msgbuf {
	long mtype; /* 消息类型,必须 > 0 */ //第一个字段必须是long类型
	char mtext[1]; /* 消息文本 */
};

size:ptr指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,也就是说size是不包括长整型消息类型成员变量的长度。
flag: 为0表示在消息队列满或者空时都等待,直到队列中出现满足要求的消息为止(阻塞方式),设置IPC_NOWAIT 表示非阻塞方式

msgrcv()

读取消息:成功返回消息数据的长度,失败返回-1

int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
//例
msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);

参数
msqid:消息队列的ID

ptr:写入的数据,指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:

struct msgbuf {
	long mtype; /* 消息类型,必须 > 0 */
	char mtext[1]; /* 消息文本 */
};

type:消息类型
msgtyp=0 则返回队列的最早的一个消息。
msgtyp>0,则返回其类型为msgtyp的第一个消息。
msgtyp<0,则返回其类型小于或等于mtype(在msgbuf结构体中的mtype)参数的绝对值的最小的一个消息。

操作系统提供的消息队列,确保同一个类型的消息是先进先出的,整个队列的消息可能不是先进先出的。

size:数据的长度(同msgsnd)

flag:为0表示在消息队列满或者空时都等待,直到队列中出现满足要求的消息为止(阻塞方式),设置IPC_NOWAIT 表示非阻塞方式

示例:
//send

 struct msgbuf {
               long mtype;   /* 消息类型,必须 > 0 */     
               char mtext[128];   /* 消息文本 */  
           };
int main()
{
//      int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

        struct msgbuf sndBuf={888,"this is message from snd"};
        int msgId=msgget(0x1234,IPC_CREAT|0777);
        if(msgId==-1){
                printf("get msg failed");
        }

        msgsnd(msgId,&sndBuf,strlen(sndBuf.mtext),0);
        printf("send over\n");
		//写入一个消息类型为888的数据
        return 0;
}

//get

 struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[128];    /* message data */
           };
int main()
{
//      ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
        struct msgbuf readBuf;
        int msgId=msgget(0x1234,IPC_CREAT|0777);
        if(msgId==-1){
                printf("get msg failed");

        }
        msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);
        //把数据类型为888的数据读出来
        printf("this is get :%s\n",readBuf.mtext);
        printf("read over\n");
        return 0;
}

先运行get
在这里插入图片描述

再运行send
在这里插入图片描述

ftok()

系统IPC键值的格式转换函数,系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
函数原型

key_t ftok( const char * fname, int id );

参数
fname:就是你指定的文件名(已经存在的文件名),一般使用当前目录,如

key_t key;
key = ftok(".", 1);

“ . ” 表示当前文件 ls-a 可以查看

这样就是将fname设为当前目录。
id:是子序号。虽然是int类型,但是只使用8bits(1-255)。
如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。

查询文件索引节点号的方法是: ls -ia
在这里插入图片描述

msgctl()

内核中的队列过多时,可以用该函数来清除队列
函数原型

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数
msqid:消息队列的ID

cmd:函数要对消息队列进行的操作,它可以是:
IPC_STAT
取出系统保存的消息队列的msqid_ds 数据,并将其存入参数buf 指向的msqid_ds 结构
中。
IPC_SET
设定消息队列的msqid_ds 数据中的msg_perm 成员。设定的值由buf 指向的msqid_ds
结构给出
IPC_RMID
将队列从系统内核中删除(用来删除队列)

buf:队列内容(一般写NULL)

msgctl(msgid,IPC_RMID,NULL);//删除队列

关于ftok和msgctl的示例

//send

 struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[128];    /* message data */
           };
int main()
{
        struct msgbuf sndBuf={888,"this is message from send"};
        struct msgbuf readBuf;
        key_t key;
        key=ftok(".",'a');//获取键值
        printf("key=%x\n",key);
        int msgId=msgget(key,IPC_CREAT|0777);//在内核中打开或建立键值为key的,权限为0777的消息队列
        if(msgId==-1){
                printf("get msg failed");
        }
        msgsnd(msgId,&sndBuf,strlen(sndBuf.mtext),0);//往队列id为msgId的队列写入sendbuf(类型为888)数据
        printf("send over\n");
        msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),988,0);//从队列中获取988类型的数据,如果队列中未出现988类型的数据,则程序阻塞在这里
        printf("this is from get:%s\n",readBuf.mtext);
        msgctl(msgId,IPC_RMID,NULL);//将队列从系统内核中删除
        return 0;
}

//get

 struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[128];    /* message data */
           };
int main()
{
        struct msgbuf readBuf;
        key_t key;
        key=ftok(".",'a');//获取键值
        printf("key=%x\n",key);
        int msgId=msgget(key,IPC_CREAT|0777);//在内核中打开或建立键值为key的,权限为0777的消息队列
        if(msgId==-1){
                printf("get msg failed");
        }
        msgrcv(msgId,(void *)&readBuf,sizeof(readBuf.mtext),888,0);//从队列中获取888类型的数据,如果队列中未出现888类型的数据,则程序阻塞在这里
        printf("this is get :%s \n",readBuf.mtext);
        printf("read over\n");
        struct msgbuf sendbuf={988,"thank you"};
        msgsnd(msgId,&sendbuf,strlen(sendbuf.mtext),0);//往队列id为msgid的队列写入sendbuf(类型为988)数据
        msgctl(msgId,IPC_RMID,NULL);//将队列从系统内核中删除
        return 0;
}

在这里插入图片描述
消息队列

IPCS:报告进程间通信状态
ipcs -q查看消息队列,-m查看内存,-s查看信号量 -a查看上述所有信息

IPCRM:删除
ipcrm -M shmkey 移除用shmkey创建的共享内存段 ipcrm -m shmid 移除用shmid标识的共享内存段
ipcrm -Q msgkey 移除用msqkey创建的消息队列 ipcrm -q msqid 移除用msqid标识的消息队列
ipcrm -S semkey 移除用semkey创建的信号 ipcrm -s semid 移除用semid标识的信号

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值