当前位置: 首页 > news >正文

【linux】进程间通信-消息队列

目录

文章1

前言:

函数:

1.创建新消息队列或取得已存在消息队列

2.向队列读/写消息

3.设置消息队列属性

实例

消息发送端:send.c

消息接收端 receive.c

文章2

1、消息队列简介

2、消息队列相关的函数

3、消息数据格式

4、msgsnd 函数

5、msgrcv 函数

6、实例

msgctl函数


文章1

原文:http://www.cnblogs.com/lpshou/archive/2013/06/20/3145651.html

前言:

   消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息

函数:


1.创建新消息队列或取得已存在消息队列

原型:int msgget(key_t key, int msgflg);

参数:

     key:可以认为是一个端口号,也可以由函数ftok生成。

     msgflg:IPC_CREAT值,若没有该队列,则创建一个并返回新标识符;若已存在,则返回原标识符。

                IPC_EXCL值,若没有该队列,则返回-1;若已存在,则返回0。

2.向队列读/写消息

原型:

msgrcv从队列中取用消息:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgsnd将数据放到消息队列中:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数:

     msqid:消息队列的标识码

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

struct msgstru{

    long mtype; //大于0

    char mtext[512];

};

     msgsz:消息的大小。

     msgtyp:从消息队列内读取的消息形态。如果值为零,则表示消息队列中的所有消息都会被读取。

  msgflg:用来指明核心程序在队列没有数据的情况下所应采取的行动。如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式。

3.设置消息队列属性

原型:int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );

参数:msgctl 系统调用对 msgqid 标识的消息队列执行 cmd 操作,系统定义了 3 种 cmd 操作: IPC_STAT , IPC_SET , IPC_RMID
      IPC_STAT : 该命令用来获取消息队列对应的 msqid_ds 数据结构,并将其保存到 buf 指定的地址空间。
      IPC_SET : 该命令用来设置消息队列的属性,要设置的属性存储在buf中。     

      IPC_RMID : 从内核中删除 msqid 标识的消息队列。

实例

消息发送端:send.c

/*send.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
 
#define MSGKEY 1024
 
struct msgstru
{
   long msgtype;
   char msgtext[2048];
};
 
main()
{
  struct msgstru msgs;
  int msg_type;
  char str[256];
  int ret_value;
  int msqid;
 
  msqid=msgget(MSGKEY,IPC_EXCL);  /*检查消息队列是否存在*/
  if(msqid < 0){
    msqid = msgget(MSGKEY,IPC_CREAT|0666);/*创建消息队列*/
    if(msqid <0){
    printf("failed to create msq | errno=%d [%s]\n",errno,strerror(errno));
    exit(-1);
    }
  }
 
  while (1){
    printf("input message type(end:0):");
    scanf("%d",&msg_type);
    if (msg_type == 0)
       break;
    printf("input message to be sent:");
    scanf ("%s",str);
    msgs.msgtype = msg_type;
    strcpy(msgs.msgtext, str);
    /* 发送消息队列 */
    ret_value = msgsnd(msqid,&msgs,sizeof(struct msgstru),IPC_NOWAIT);
    if ( ret_value < 0 ) {
       printf("msgsnd() write msg failed,errno=%d[%s]\n",errno,strerror(errno));
       exit(-1);
    }
  }
  msgctl(msqid,IPC_RMID,0); //删除消息队列
}

消息接收端 receive.c

/*receive.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
 
#define MSGKEY 1024
 
struct msgstru
{
   long msgtype;
   char msgtext[2048];
};
 
/*子进程,监听消息队列*/
void childproc(){
  struct msgstru msgs;
  int msgid,ret_value;
  char str[512];
 
  while(1){
     msgid = msgget(MSGKEY,IPC_EXCL );/*检查消息队列是否存在 */
     if(msgid < 0){
        printf("msq not existed! errno=%d [%s]\n",errno,strerror(errno));
        sleep(2);
        continue;
     }
     /*接收消息队列*/
     ret_value = msgrcv(msgid,&msgs,sizeof(struct msgstru),0,0);
     printf("text=[%s] pid=[%d]\n",msgs.msgtext,getpid());
  }
  return;
}
 
void main()
{
  int i,cpid;
 
  /* create 5 child process */
  for (i=0;i<5;i++){
     cpid = fork();
     if (cpid < 0)
        printf("fork failed\n");
     else if (cpid ==0) /*child process*/
        childproc();
  }
}

文章2

1、消息队列简介

消息队列本质上是位于内核空间的链表,链表的每个节点都是一条消息。每一条消息都有自己的消息类型,消息类型用整数来表示,而且必须大于 0。每种类型的消息都被对应的链表所维护:

图1 位于内核空间的消息队列.png

其中数字 1 表示类型为 1 的消息,数字2、3、4 类似。彩色块表示消息数据,它们被挂在对应类型的链表上。

值得注意的是,刚刚说过没有消息类型为 0 的消息,实际上,消息类型为 0 的链表记录了所有消息加入队列的顺序,其中红色箭头表示消息加入的顺序。

2、消息队列相关的函数

// 创建和获取 ipc 内核对象
int msgget(key_t key, int flags);
// 将消息发送到消息队列
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
// 从消息队列获取消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
// 查看、设置、删除 ipc 内核对象(用法和 shmctl 一样)
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

size_t 就是无符号型的ssize_t,也就是unsigned long/ unsigned int (在32位下),

3、消息数据格式

无论你是发送还是接收消息,消息的格式都必须按照规范来。简单的说,它一般长成下面这个样子:

struct Msg{
    long type; // 消息类型。这个是必须的,而且值必须 > 0,这个值被系统使用
    // 消息正文,多少字节随你而定
    // ...
};

所以,只要你保证首4字节(32 位 linux 下的 long)是一个整数就行了。
举个例子:

struct Msg {
    long type;
    char name[20];
    int age;
} msg;

struct Msg {
    long type;
    int start;
    int end;
} msg;

从上面可以看出,正文部分是什么数据类型都没关系,因为消息队列传递的是 2 进制数据,不一定非得是文本。

4、msgsnd 函数

msgsnd 函数用于将数据发送到消息队列。如果该函数被信号打断,会设置 errno 为 EINTR。

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数 msqid:ipc 内核对象 id
参数 msgp:消息数据地址
参数 msgsz:消息正文部分的大小(不包含消息类型)
参数 msgflg:可选项
该值为 0:如果消息队列空间不够,msgsnd 会阻塞。
IPC_NOWAIT:直接返回,如果空间不够,会设置 errno 为 EAGIN.

返回值:0 表示成功,-1 失败并设置 errno。

5、msgrcv 函数

msgrcv 函数从消息队列取出消息后,并将其从消息队列里删除。

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

参数 msqid:ipc 内核对象 id
参数 msgp:用来接收消息数据地址
参数 msgsz:消息正文部分的大小(不包含消息类型)
参数 msgtyp:指定获取哪种类型的消息

msgtyp = 0:获取消息队列中的第一条消息
msgtyp > 0:获取类型为 msgtyp 的第一条消息,除非指定了 msgflg 为MSG_EXCEPT,这表示获取除了 msgtyp 类型以外的第一条消息。
msgtyp < 0:获取类型 ≤|msgtyp|≤|msgtyp| 的第一条消息。
参数 msgflg:可选项。
如果为 0 表示没有消息就阻塞。
IPC_NOWAIT:如果指定类型的消息不存在就立即返回,同时设置 errno 为 ENOMSG
MSG_EXCEPT:仅用于 msgtyp > 0 的情况。表示获取类型不为 msgtyp 的消息
MSG_NOERROR:如果消息数据正文内容大于 msgsz,就将消息数据截断为 msgsz

6、实例

程序 msg_send 和 msg_recv 分别用于向消息队列发送数据和接收数据。

6.1 msg_send

msg_send 程序定义了一个结构体 Msg,消息正文部分是结构体 Person。该程序向消息队列发送了 10 条消息。
msg_send.c

#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    char name[20];
    int age;
}Person;

typedef struct {
    long type;
    Person person;
}Msg;

int main(int argc, char *argv) {
    int id = msgget(0x8888, IPC_CREAT | 0664);
    
    Msg msg[10] = {
        {1, {"Luffy", 17}},
        {1, {"Zoro", 19}},
        {2, {"Nami", 18}},
        {2, {"Usopo", 17}},
        {1, {"Sanji", 19}},
        {3, {"Chopper", 15}},
        {4, {"Robin", 28}},
        {4, {"Franky", 34}},
        {5, {"Brook", 88}},
        {6, {"Sunny", 2}}
    };
    
    int i;
    for (i = 0; i < 10; ++i) {
        int res = msgsnd(id, &msg[i], sizeof(Person), 0);
    }
    
    return 0;
}

程序 msg_send 第一次运行完后,内核中的消息队列大概像下面这样:

图2 第一次执行完 msg_send 后的消息队列.png

6.2 msg_recv

msg_recv 程序接收一个参数,表示接收哪种类型的消息。比如./msg_recv 4 表示接收类型为 4 的消息,并打印在屏幕。

#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

typedef struct {
    char name[20];
    int age;
}Person;

typedef struct {
    long type;
    Person person;
}Msg;

void printMsg(Msg *msg) {
    printf("{ type = %ld, name = %s, age = %d }\n",
           msg->type, msg->person.name, msg->person.age);
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("usage: %s <type>\n", argv[0]);
        return -1;
    }
    
    // 要获取的消息类型
    long type = atol(argv[1]);
    
    // 获取 ipc 内核对象 id
    int id = msgget(0x8888, 0);
   
    
    Msg msg;
    int res;
    
    while(1) {
        // 以非阻塞的方式接收类型为 type 的消息
        res = msgrcv(id, &msg, sizeof(Person), type, IPC_NOWAIT);
        if (res < 0) {
            // 如果消息接收完毕就退出,否则报错并退出
            if (errno == ENOMSG) {
                printf("No message!\n");
                break;
            }

        }
        // 打印消息内容
        printMsg(&msg);
    }
    return 0;
}

6.3 编译

[root@localhost ~]# gcc msg_send.c -o msg_send
[root@localhost ~]# gcc msg_recv.c -o msg_recv

6.4 运行

先运行 msg_send,再运行 msg_recv。
接收所有消息

image.png

接收类型为 4 的消息

image.png

msgctl函数

获取和设置消息队列的属性

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

msqid:消息队列标识符
cmd:控制指令
IPC_STAT:获得msgid的消息队列头数据到buf中
IPC_SET:设置消息队列的属性,要设置的属性需先存储在buf中,可设置的属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes
buf:消息队列管理结构体。

返回值:
成功:0
出错:-1,错误原因存于error中
EACCESS:参数cmd为IPC_STAT,确无权限读取该消息队列
EFAULT:参数buf指向无效的内存地址
EIDRM:标识符为msqid的消息队列已被删除
EINVAL:无效的参数cmd或msqid
EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行

实例

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <error.h>


struct msgbuf{
    long mtype ;
    char mtext[] ;
}  ;

int main(int argc, char **argv){
    int msqid ;
    struct msqid_ds info ;
    struct msgbuf buf ;
    struct msgbuf buf1 ;
    int flag ;
    int sendlength, recvlength ;

    msqid = msgget( IPC_PRIVATE, 0666 ) ;

    if ( msqid < 0 ){
        perror("get ipc_id error") ;
        return -1 ;
    }

    buf.mtype = 1 ;
    strcpy(buf.mtext, "happy new year!") ;
    sendlength = sizeof(struct msgbuf) - sizeof(long) ;
    flag = msgsnd( msqid, &buf, sendlength , 0 ) ;

    if ( flag < 0 ){
        perror("send message error") ;
        return -1 ;
    }

    buf.mtype = 3 ;
    strcpy(buf.mtext, "good bye!") ;
    sendlength = sizeof(struct msgbuf) - sizeof(long) ;
    flag = msgsnd( msqid, &buf, sendlength , 0 ) ;

    if ( flag < 0 ){
        perror("send message error") ;
        return -1 ;
    }

    flag = msgctl( msqid, IPC_STAT, &info ) ;

    if ( flag < 0 ){
        perror("get message status error") ;
        return -1 ;
    }

    printf("uid:%d, gid = %d, cuid = %d, cgid= %d\n" ,
           info.msg_perm.uid,  info.msg_perm.gid,  info.msg_perm.cuid,  info.msg_perm.cgid  ) ;

    printf("read-write:%03o, cbytes = %lu, qnum = %lu, qbytes= %lu\n" ,
           info.msg_perm.mode&0777, info.msg_cbytes, info.msg_qnum, info.msg_qbytes ) ;

    system("ipcs -q") ;
    recvlength = sizeof(struct msgbuf) - sizeof(long) ;
    memset(&buf1, 0x00, sizeof(struct msgbuf)) ;

    flag = msgrcv( msqid, &buf1, recvlength ,3,0 ) ;
    if ( flag < 0 ){
        perror("recv message error") ;
        return -1 ;
    }
    printf("type=%d, message=%s\n", buf1.mtype, buf1.mtext) ;

    flag = msgctl( msqid, IPC_RMID,NULL) ;
    if ( flag < 0 ){
        perror("rm message queue error") ;
        return -1 ;
    }
    system("ipcs -q") ;
    return 0 ;
}

image.png
作者:小Q_wang
链接:https://www.jianshu.com/p/7e3045cf1ab8
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关文章:

  • 【C++】C++ STL stack 用法
  • 【C++】什么是函数对象和函数对象的用处
  • 【C++】STL标准容器的排序操作和选择合适的排序算法
  • 【C++】程序猿c++(11) 字符串比较误区总结
  • 【C++】C++ STL中 next_permutation,prev_permutation函数的用法
  • 【C++】search、search_n和find、find_if
  • 【C++】迭代器iterator研究(input iterator、output iterator等)----编辑中
  • 【WebRTC】WebRTC介绍及简单应用
  • 【高并发】高并发测试笔记
  • 【SSL】HTTPS 和 SSL证书原理
  • 【mySQL】WAL和MVCC ----待消化
  • 【GDB】GDB 调试多线程和多进程总结
  • 【C++】C++的类|C++类的内存结构
  • 【算法】详解二分查找算法(思路很简单,细节是魔鬼)
  • 【数据结构】linux 内核的list
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • ES6简单总结(搭配简单的讲解和小案例)
  • Gradle 5.0 正式版发布
  • Python中eval与exec的使用及区别
  • springboot_database项目介绍
  • SQLServer之创建显式事务
  • Vue学习第二天
  • 类orAPI - 收藏集 - 掘金
  • 马上搞懂 GeoJSON
  • 强力优化Rancher k8s中国区的使用体验
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 深入浅出Node.js
  • 试着探索高并发下的系统架构面貌
  • 算法之不定期更新(一)(2018-04-12)
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 学习笔记DL002:AI、机器学习、表示学习、深度学习,第一次大衰退
  • 正则表达式小结
  • 交换综合实验一
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • (10)STL算法之搜索(二) 二分查找
  • (6)STL算法之转换
  • (done) 两个矩阵 “相似” 是什么意思?
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (原)本想说脏话,奈何已放下
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .NET 4 并行(多核)“.NET研究”编程系列之二 从Task开始
  • .Net CF下精确的计时器
  • .NET Core、DNX、DNU、DNVM、MVC6学习资料
  • .Net Framework 4.x 程序到底运行在哪个 CLR 版本之上
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • ?php echo $logosrc[0];?,如何在一行中显示logo和标题?
  • @Autowired标签与 @Resource标签 的区别
  • [20150321]索引空块的问题.txt
  • [C#C++]类CLASS
  • [C++] Windows中字符串函数的种类
  • [EFI]英特尔 冥王峡谷 NUC8i7HVK 电脑 Hackintosh 黑苹果efi引导文件
  • [Everyday Mathematics]20150130