精华内容
下载资源
问答
  • Linux消息队列详解

    千次阅读 2012-08-15 10:08:05
    Linux消息队列(queue)实质上是一个链表, 它有消息队列标识符(queue ID). msgget创建一个新队列或打开一个存在的队列; msgsnd向队列末端添加一条新消息; msgrcv从队列中取消息, 取消息是不一定遵循先进先出的, 也...
    Linux的消息队列(queue)实质上是一个链表, 它有消息队列标识符(queue ID). msgget创建一个新队列或打开一个存在的队列; msgsnd向队列末端添加一条新消息; msgrcv从队列中取消息, 取消息是不一定遵循先进先出的, 也可以按消息的类型字段取消息. 


    1. 标识符(des)和键(key):


        消息队列, 信号量和共享存储段, 都属于内核中的IPC结构, 它们都用标识符来描述. 这个标识符是一个非负整数, 与文件描述符不同的是, 创建时并不会重复利用通过删除回收的整数, 而是每次+1, 直到整数最大值回转到0.


        标识符是IPC对象的内部名, 而它的外部名则是key(键), 它的基本类型是key_t, 在头文件<sys/types.h>中定义为长整型. 键由内核变换成标识符.


    2. 消息队列状态msqid_ds:

        每个消息队列都有一个msqid_ds结构与其关联:

    struct msqid_ds
      {
        struct msqid_ds {
        struct ipc_perm msg_perm;
        struct msg *msg_first;      /* first message on queue,unused  */
        struct msg *msg_last;       /* last message in queue,unused */
        __kernel_time_t msg_stime;  /* last msgsnd time */
        __kernel_time_t msg_rtime;  /* last msgrcv time */
        __kernel_time_t msg_ctime;  /* last change time */
        unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */
        unsigned long  msg_lqbytes; /* ditto */
        unsigned short msg_cbytes;  /* current number of bytes on queue */
        unsigned short msg_qnum;    /* number of messages in queue */
        unsigned short msg_qbytes;  /* max number of bytes on queue */
        __kernel_ipc_pid_t msg_lspid;   /* pid of last msgsnd */
        __kernel_ipc_pid_t msg_lrpid;   /* last receive pid */
    };

    结构体ipc_perm:http://blog.csdn.net/yuzhoudiwang/article/details/4377064


    3. 由路径名和项目ID产生一个key:


        如果客户进程和服务器进程认同一个路径名和项目ID(0~255间的字符值), 接着调用ftok将这两个值变换为一个key.


    原型: key_t ftok(const char *path, int id); 
    头文件: <sys/ipc.h> 
    返回值: 成功则返回key, 出错则返回(key_t)-1. 
    参数: path参数必须引用一个现存文件. 当产生key时 只使用id参数的低8位. 
    说 明: 如果两个路径名引用两个不同的文件, 对这两个路径名调用ftok通常返回不同的key. 但是, 因为i节点号和key通常存放在长整型中, 于是创建key时可能会丢失信息. 这意味着, 如果使用同一项目ID, 那么对于不同文件的两个路径名可能产生相同的key. 该函数的工作方式为: 
    按给定的路径名取得其stat结构. 
    从该结构中取出部分st_dev和st_ino字段, 与项目ID组合起来. 
    4. 创建/打开消息队列:


        msgget可以创建一个新队列或打开一个存在的队列.


    原型: int msgget(key_t key, int flag); 
    头文件: <sys/msg.h> 
    返回值: 成功则返回消息队列ID, 出错则返回-1. 
    参数: 
    key: 消息队列的key值. 
    flag: 标志位. 
    说明: 
    创建队列有两种方法: 
    key是IPC_PRIVATE. 
    key当前未与特定类型的IPC结构相结合, 并且flag中指定了IPC_CREAT位. 
    初始化msqid_ds成员: 
    ipc_perm中的mode成员按flag进行设置. 
    msg_qnum, msg_lspid, msg_lrpid, msg_stime和msg_rtime都设置为0. 
    msg_ctime设置为当前时间. 
    msg_qbytes设置为系统限制值. 
    5. 消息队列的垃圾桶函数:


        msgctl类似于驱动程序中的ioctl函数, 可对消息队列执行多种操作.


    原型: int msgctl(int msqid, int cmd, struct msgqid_ds *buf); 
    头文件: <sys/msg.h> 
    返回值: 成功则返回0, 出错则返回-1. 
    参数: cmd参数说明对msqid指定的队列要执行的命令: 
    IPC_STAT: 取此队列的msqid_ds结构, 并将它存放在buf指向的结构中. 
    IPC_SET: 按由buf指向结构中的值, 设置与此队列相关结构中的msg_perm.uid, msg_perm.gid, msg_perm.mode和msg_qbytes. 该命令只有下列两种进程可以执行: 
    有效用户ID等于msg_perm.cuid或msg_per.uid. 
    具有超级用户特权的进程. 
    IPC_RMID: 从系统中删除该消息队列以及仍在该队列中的所有数据. 执行权限同上. 
    6. 将数据放到消息队列:


        调用msgsnd将数据放到消息队列中.


    原型: int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag); 
    头文件: <sys/msg.h> 
    返回值: 成功则返回0, 出错则返回-1. 
    说 明: 可以定义一个消息结构, 结构中带类型, 这样就可用非先进先出顺序取消息了. 当msgsnd成功返回, 与消息队列相关的msqid_ds结构得到更新, 以标明发出该调用的进程ID(msg_lsqid), 进行该调用的时间(msg_stime), 并指示队列中增加了一条消息(msg_qnum). 
    7. 从消息队列中取消息:


        调用msgrcv将从消息队列中取消息.


    原型: ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag); 
    头文件: <sys/msg.h> 
    返回值: 成功则返回消息的数据部分的长度, 出错则返回-1. 
    参数: 
    ptr: 指向一个长整型数(返回的消息类型存放在其中), 跟随其后的是存放实际消息数据的缓冲区. 
    nbytes: 数据缓冲区的长度. 若返回的消息大于nbytes, 且在flag中设置了MSG_NOERROR, 则该消息被截短. 
    type: 
    type == 0: 返回队列中的第一个消息. 
    type > 0: 返回队列中消息类型为type的第一个消息. 
    type < 0: 返回队列中消息类型值小于或等于type绝对值的消息, 如果这种消息有若干个, 则取类型值最小的消息. 

    说明: 当msgrcv成功返回时, 与消息队列相关的msqid_ds结构被更新, 以指示调用者的进程ID(msg_lrpid), 调用时间(msg_rtime)和队列中的消息数(msg_qnum)减1.

    from:http://blog.csdn.net/ypist/article/details/6739560



    展开全文
  • linux消息队列详解

    千次阅读 2018-04-23 19:45:04
    消息队列的不足之处在于每个消息最大长度有限度,每个消息队列总字节数有限制,系统的消息队列有限制。 命令: cat /proc/sys/kernel/msgmax :查看一条信息最大有多大 cat /proc/sys/kernel/msgmnb
    消息队列提供了从一个进程向另外一个进程发送一块数据的方法,每个数据块认为有一个类型(通俗说法是一个通道),但是接受通道与发送通道必须一致才能实现通信。消息队列的不足之处在于每个消息最大长度有限度,每个消息队列总字节数有限制,系统的消息队列有限制。 
    

    命令:

    cat /proc/sys/kernel/msgmax :查看一条信息最大有多大

    cat /proc/sys/kernel/msgmnb :查看消息队列中信息最大有多大

    cat /proc/sys/kernel/msgmni :查看系统中消息队列最大有多大

    命令 ipcs -q :查看消息队列

    命令 ipcrm -Q 消息队列名:删除消息队列

    任务:

    创建一个消息队列、往消息队列中发数据、在消息队列中取数据、删除消息队列(在内核中删除这个对象)

    1)创建一个消息队列:

    1. int main()  
    2.   8 {  
    3.   9     int id=msgget(1234,IPC_CREAT|0644);//创建一个消息队列,名字为1234  
    4.  10     if(id==-1)  
    5.  11     {  
    6.  12         perror("msgget failure\n");  
    7.  13         exit(0);  
    8.  14     }  
    9.  15     printf("creat is ok\n");  
    10.  16     return 0;  
    11.  17   
    12.  18 }  

    (2)往消息队列中发数据

    1.  3 #include <stdlib.h>  
    2.  4 #include <fcntl.h>  
    3.  5 #include <sys/ipc.h>  
    4.  6 #include <sys/msg.h>  
    5.  7 #include <string.h>  
    6.  8   
    7.  9 //消息队列发送数据的要求,以一个结构体  
    8. 10 struct mybuf  
    9. 11 {  
    10. 12     long channel;//通道号  
    11. 13     char mtext[100];//一块缓冲区,装发送数据  
    12. 14 };  
    13. 15   
    14. 16 int main()  
    15. 17 {  
    16. 18     //打开之前创建的消息队列  
    17. 19     int id=msgget(1234,0);  
    18. 20     if(id==-1)  
    19. 21     {  
    20. 22         perror("msgget failure\n");  
    21. 23         exit(1);  
    22. 24     }  
    23. 25   
    24. 26     struct mybuf buf;//内存中创建一个待发送数据结构体  
    25. 27   
    26. 28     while(1)  
    27. 29     {  
    28. 30         printf("您要输入的通道号channel:");  
    29. 31         scanf("%d",&buf.channel);//将输入的通道号,放入结构体对应通道号的位置  
    30. 32   
    31. 33         printf("您要往此通道输入的内容mtext:");  
    32. 34         scanf("%s",buf.mtext);//将输入的内容,放在结构体对应存放主体内容的位置  
    33. 35   
    34. 36         //将buf里面的数据发送到id这个消息队列  
    35. 37         if(msgsnd(id,&buf,strlen(buf.mtext),0)==-1)  
    36. 38         {  
    37. 39             perror("snd failure\n");  
    38. 40             exit(1);  
    39. 41         }  
    40. 42         printf("send is ok\n");  
    41. 43     }  

    (3)在消息队列中取数据

    1. 3 #include <stdio.h>  
    2.  4 #include <stdlib.h>  
    3.  5 #include <fcntl.h>  
    4.  6 #include <sys/ipc.h>  
    5.  7 #include <sys/msg.h>  
    6.  8 #include <string.h>  
    7.  9   
    8. 10 //消息队列发送数据的要求,以一个结构体  
    9. 11 struct mybuf  
    10. 12 {  
    11. 13     long channel;//通道号  
    12. 14     char mtext[100];//一块缓冲区,装发送数据  
    13. 15 };  
    14. 16   
    15. 17 int main()  
    16. 18 {  
    17. 19     //打开之前创建的消息队列  
    18. 20     int id=msgget(1234,0);  
    19. 21     if(id==-1)  
    20. 22     {  
    21. 23         perror("msgget failure\n");  
    22. 24         exit(1);  
    23. 25     }  
    24. 26   
    25. 27     struct mybuf buf;//内存中创建一个待发送数据结构体  
    26. 28   
    27. 29     while(1)  
    28. 30     {  
    29. 31         int channel=0;  
    30. 32         printf("请输入您要读取通道号:");  
    31. 33         scanf("%d",&channel);  
    32. 34         memset(&buf, 0x00, sizeof(buf));  
    33. 35         msgrcv(id,&buf,100,channel,0);  
    34. 36         printf("buf.mtext=%s\n",buf.mtext);  
    35. 37     }  
    36. 38     return 0;  
    37. 39 }  

    (4)删除消息队列(在内核中删除这个对象)

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

    Msgctl为创建消息队列返回的消息队列标识符

    Cmd为命令,当执行删除消息队列时,就用IPC_RMID

    Buf也是当执行删除时,就用0

    展开全文
  • 主要介绍了Linux消息队列实现进程间通信实例详解的相关资料,需要的朋友可以参考下
  • 消息队列就是一个消息的链表,每个消息队列都有一个队列头,用结构struct msg_queue来描述。队列头中包含了该队列的大量信息,包括消息队列的键值、用户ID、组ID、消息数目、读写进程ID等。其定义如下: struct msg_...

    消息队列的概念

    消息队列就是一个消息的链表,每个消息队列都有一个队列头,用结构struct msg_queue来描述。队列头中包含了该队列的大量信息,包括消息队列的键值、用户ID、组ID、消息数目、读写进程ID等。其定义如下:

    struct msg_queue
    {
        struct ipc_perm q_perm;
        time_t q_stime;     // last msgsnd time
        time_t q_rtime;     // last msgrcv time
        time_t q_ctime;     // last change time
        unsigned long q_cbytes;      // current number of bytes on queue
        unsigned long q_qnum;        // number of message in queue
        unsigned long q_qbytes;      // max number of bytes on queue
        pid_t q_lspid;     // pid of last msgsnd
        pid_t q_lrpid;     // last receive pid
        struct list_head q_messages;
        struct list_head q_receives;
        struct list_head q_senders;
    };
    

    结构体msqid_ds用来设置或返回消息队列的信息,定义如下:

    // 摘自所用ubuntu18.04电脑中的/usr/include/i386-linux-gnu/bits/msq.h
    struct msqid_ds
    {
      struct ipc_perm msg_perm;     /* structure describing operation permission */
      __time_t msg_stime;           /* time of last msgsnd command */
    #ifndef __x86_64__
      unsigned long int __glibc_reserved1;
    #endif
      __time_t msg_rtime;           /* time of last msgrcv command */
    #ifndef __x86_64__
      unsigned long int __glibc_reserved2;
    #endif
      __time_t msg_ctime;           /* time of last change */
    #ifndef __x86_64__
      unsigned long int __glibc_reserved3;
    #endif
      __syscall_ulong_t __msg_cbytes; /* current number of bytes on queue */
      msgqnum_t msg_qnum;           /* number of messages currently on queue */
      msglen_t msg_qbytes;          /* max number of bytes allowed on queue */
      __pid_t msg_lspid;            /* pid of last msgsnd() */
      __pid_t msg_lrpid;            /* pid of last msgrcv() */
      __syscall_ulong_t __glibc_reserved4;
      __syscall_ulong_t __glibc_reserved5;
    };
    

    消息队列的创建与打开

    消息队列具有一个唯一的键值,或称引用标识符、消息队列的ID号,通过使用ftok()函数获取,函数原型:

    #include <sys/types.h>
    #include <sys/ipc.h>
    key_t ftok(char *pathname, char proj);
    

    获取成功返回消息队列的键值,失败返回-1。

    参数pathname为一任意存在的路径名,参数proj为1~255之间的任一数字,ftok根据路径名,提取文件信息,再根据这些文件信息及proj的值合成key。

    ftok()函数并不直接对消息队列操作,生成的键值用于msgget()函数使用,该函数用于创建或打开一个消息队列,其函数原型如下:

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    int msgget(key_t key, int msgflg);
    

    运行成功则返回消息队列的引用标识符(ID),失败则返回-1。

    参数key是ftok()产生的键值,参数msgflg是一些标志位,可以取IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的逻辑或结果。

    在以下两种情况下,msgget()将创建一个新的消息队列:

    • 如果没有消息队列与键值key相对应,且msgflg中包含了IPC_CREAT标志位
    • key参数为IPC_PRIVATE

    需要C/C++ Linux服务器架构师学习资料加群812855908(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
    在这里插入图片描述
    在这里插入图片描述

    消息队列的读写

    消息队列传递的消息由两部分组成,包括消息类型和所传的数据,用结构体struct msgbuf表示:

    struct msgbuf
    {
        long msgtype;
        char msgtext[1024];
    };
    

    msgtype成员代表消息类型,msgtext成员为消息内容,长度不一定是1024。对于发送端,首先预置一个这样的msgbuf缓冲区并写入消息类型和内容,然后调用相应的发送函数;对于接收端,首先分配一个msgbuf缓冲区,然后把消息读入缓冲区即可。

    发送数据(写)
    向消息队列发送数据使用msgsnd()函数,发送的一个消息数据会被添加到队列的末尾,函数原型如下:

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    int msgsnd(int msqid, const void *prt, size_t nbytes, int flags);
    

    运行成功返回0,失败返回-1。参数msqid为消息队列的引用标识符(ID),参数prt为void型指针,指向要发送到的消息,参数nbytes为发送的消息的字节长度,参数flag用于指定消息队列满时的处理方法。

    对发送消息来说,有意义的flags标志为IPC_NOWAIT,在消息队列没有足够的空间容纳要发送的数据时,设置了该标志,则msgsnd()函数立刻出错返回,否则发送消息的进程被阻塞,直至消息队列有空间或队列被删除时返回。

    接收数据(读)
    从消息队列接收数据使用msgrcv()函数,函数原型如下:

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    int msgrcv(int msqid, const void *prt, size_t nbytes, long type, int flags);
    

    运行成功返回0,失败返回-1。

    参数含义与msgsnd()函数类似,参数flag用于指定消息队列满时的处理方法,取值有3种以及3种的或结果,参数type表示接收的数据类型。在这里插入图片描述
    在这里插入图片描述

    消息队列属性设置

    消息队列的信息基本都保存在消息队列头中,可分配一个类似于消息队列头的结构struct msqid_ds来返回消息队列的属性,同样可以设置该数据结构。属性设置使用msgctl()函数,函数原型如下:

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    

    运行成功返回0,失败返回-1。

    函数msgctl()将对参数msqid标识的消息队列执行参数cmd所指的命令,包括3种命令:

    • IPC_STAT:用于获取消息队列信息,返回的信息存贮在参数buf
    • IPC_SET:用于设置消息队列的属性,要设置的属性存储在参数buf
    • PC_RMID:删除msqid标识的消息队列

    编程示例

    消息队列编程步骤:

    • 使用ftok()生成key
    • 使用msgget()创建/获取消息队列,返回值为队列标识符
    • 发送消息msgsnd()/接收消息msgrcv()
    • 消息队列属性与删除msgctl()

    示例1
    简单使用。

    发送端,msg1_snd.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    
    int main()
    {
        // generate key
        key_t key = ftok(".", 100);
        if(key == -1)
        {
            perror("ftoke failed");
            exit(1);
        }
        printf("key = %#x\n", key);
    
        // create message queue
        int msgid = msgget(key, 0666|IPC_CREAT|IPC_EXCL);
        if(msgid == -1)
        {
            perror("msgget failed");
            exit(2);
        }
    
        // send data
        msgsnd(msgid, "hello world!\n", 14, 0);
    
        printf("use Enter to destory the message queue!\n");
        getchar();
        // detele message queue
        if(msgctl(msgid, IPC_RMID, NULL) == -1)
        {
            perror("msgctl failed");
            exit(3);
        }
    
        return 0;
    }
    

    接收端,msg1_rcv.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    
    int main()
    {
        // generate key
        key_t key = ftok(".", 100);
        if(key == -1)
        {
            perror("ftoke failed");
            exit(1);
        }
        printf("key = %#x\n", key);
    
        // get message queue
        int msgid = msgget(key, 0);
        if(msgid == -1)
        {
            perror("msgget failed");
            exit(2);
        }
    
        // read from the message queue
        char buf[100] = {};
        msgrcv(msgid, buf, 100, 0, 0);
        printf("read from message queue:%s\n", buf);
    
        return 0;
    }
    

    在一个shell中运行消息队列发送程序:

    $ ./msg1_snd
    key = 0x641102ed
    use Enter to destory the message queue!
    

    在另一个shell中运行消息队列接收程序:

    $ ./msg1_rcv
    key = 0x641102ed
    read from message queue:hello world!
    

    示例2
    发送带消息类型的数据。

    发送端,msg2_send.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <string.h>
    
    struct _msg
    {
        long mtype;
        char buf[256];
    }msg1,msg2;
    
    int main()
    {
        // generate key
        key_t key = ftok(".", 100);
        if(key == -1)
        {
            perror("ftoke failed");
            exit(1);
        }
        printf("key = %#x\n", key);
    
        // create message queue
        int msgid = msgget(key, 0666|IPC_CREAT);
        if(msgid == -1)
        {
            perror("msgget failed");
            exit(2);
        }
    
        //send data
        msg1.mtype = 2;
        strcpy(msg1.buf, "hello2");
        msgsnd(msgid, &msg1, sizeof(msg1.buf), 0);
    
        msg2.mtype = 1;
        strcpy(msg2.buf, "hello1");
        msgsnd(msgid, &msg2, sizeof(msg2.buf), 0);
    
        printf("use Enter to destory the message queue!\n");
        getchar();
        // destroy the messsage queue
        if(msgctl(msgid, IPC_RMID, NULL) == -1)
        {
            perror("msgctl failed");
            exit(3);
        }
    
        return 0;
    }
    

    接收端,msg2_rcv.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <string.h>
    
    struct _msg
    {
        long mtype;
        char buf[256];
    }msg1,msg2;
    
    int main()
    {
        // generate key
        key_t key = ftok(".", 100);
        if(key == -1)
        {
            perror("ftoke failed");
            exit(1);
        }
        printf("key = %#x\n", key);
    
        // get message queue
        int msgid = msgget(key, 0);
        if(msgid == -1)
        {
            perror("msgget failed");
            exit(2);
        }
    
        // read from the message queue
        int res = msgrcv(msgid, &msg1, sizeof(msg1)-4, 0, 0);
        while(res != 1)
        {
            printf("Message:%s, Type:%ld\n", msg1.buf, msg1.mtype);
            res = msgrcv(msgid, &msg1, sizeof(msg1)-4, 0, 0);
        }
    
        return 0;
    }
    

    在一个shell中运行消息队列发送程序:

    $ ./msg2_snd
    key = 0x651102ed
    use Enter to destory the message queue!
    

    在另一个shell中运行消息队列接收程序:

    $ ./msg2_rcv
    key = 0x651102ed
    Message:hello2, Type:2
    Message:hello1, Type:1
    

    示例3
    消息队列的综合编程使用举例,msg_app.c:

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <unistd.h>
    #include <time.h>
    #include <stdio.h>
    
    void msg_stat(int, struct msqid_ds);
    
    int main(void)
    {
        int gflags, sflags, rflags;
        key_t key;
        int msgid;
        int reval;
    
        struct msgsbuf
        {
            int mtype;
            char mtext[10];
        }msg_sbuf;// send
    
        struct msgmbuf
        {
            int mtype;
            char mtext[10];
        }msg_rbuf;// receivr
    
        struct msqid_ds msg_ginfo, msg_sinfo;
    
        // create key
        key = ftok(".", 30);
        if(key == -1)
        {
            printf("ftok failed!");
            return -1;
        }
        printf("key = %#x\n", key);
    
        // create message queue
        gflags = IPC_CREAT|IPC_EXCL;
        msgid = msgget(key, 0666|gflags);
        if(msgid == -1)
        {
            printf("msg create error\n");
            return -1;
        }
        else
            printf("msg create ok\n");
    
        // after create the message queue, show it's property, use msg_stat 1st
        printf("\n msg_stat1:");
        msg_stat(msgid, msg_ginfo);
    
        // send message
        sflags = IPC_NOWAIT;
        msg_sbuf.mtype = 8;
        msg_sbuf.mtext[0] = 'a';
        msg_sbuf.mtext[1] = 'b';
        msg_sbuf.mtext[2] = 'c';
        reval = msgsnd(msgid, &msg_sbuf, sizeof(msg_sbuf.mtext), sflags);
        if(reval == -1)
            printf("message send error\n");
        else
            printf("message send ok\n");
    
        // after send a message, shoe it's property, use msg_stat 2st
        printf("\n msg_stat2:");
        msg_stat(msgid, msg_ginfo);
    
        // receive message
        rflags = IPC_NOWAIT|MSG_NOERROR;
        reval = msgrcv(msgid, &msg_rbuf, 3, 0, rflags);
        if(reval == -1)
            printf("msg read error\n");// ===
        else
        {
            printf("read from msg queue %d bytes\n", reval);
            printf("type:%d, message:%s\n", msg_rbuf.mtype, msg_rbuf.mtext);
        }
    
        // use msg_stat 3st
        printf("\n msg_stat3:");
        msg_stat(msgid, msg_ginfo);
    
        // change message property
        msg_sinfo = msg_ginfo;
        msg_sinfo.msg_perm.uid = 8; // user ID
        msg_sinfo.msg_perm.gid = 8; // group ID
        msg_sinfo.msg_qbytes = 16388; // queue bytes 16384->16388
        reval = msgctl(msgid, IPC_SET, &msg_sinfo);
        if(reval == -1)
        {
            printf("msg set info error\n");// ===
        }
    
        // use msg_stat 4st
        printf("\n msg_stat4:");
        msg_stat(msgid, msg_ginfo);
    
        // delete the message queue
        reval = msgctl(msgid, IPC_RMID, NULL);
        if(reval == -1)
        {
            printf("unlink msg queue error\n");
        }
    
        return 0;
    }
    
    void msg_stat(int msgid, struct msqid_ds msg_info)
    {
        int reval;
        sleep(1);
        // get message property
        reval = msgctl(msgid, IPC_STAT, &msg_info);
        if(reval == -1)
        {
            printf("get msg info error\n");
            return;
        }
    
        printf("\n");
        printf("current number of bytes on queue is %ld\n", msg_info.msg_cbytes);
        printf("number of message in queue is %ld\n", msg_info.msg_qnum);
        printf("max number of bytes on queue is %ld\n", msg_info.msg_qbytes);
        //
        printf("pid of last msgsnd is %d\n", msg_info.msg_lspid); // last send opera                                                       te process's ID
        printf("pid of last msgrcv is %d\n", msg_info.msg_lrpid); // last receive op                                                       erate process's ID
        printf("last msgsnd time is %s", ctime(&(msg_info.msg_stime))); // last send                                                        time
        printf("last msgrcv time is %s", ctime(&(msg_info.msg_rtime))); // last rece                                                       ive time
        printf("last change time is %s", ctime(&(msg_info.msg_ctime))); // last chan                                                       ge time
        printf("msg uid is %d\n", msg_info.msg_perm.uid); // message queue user ID
        printf("msg gid is %d\n", msg_info.msg_perm.gid); // message queue group ID
    }
    

    编译后执行,需要sudo权限,否则change message property 步骤会失败:

    $ sudo ./msg_app
    [sudo] password for deeplearning:
    key = 0x1e11060e
    msg create ok
    
     msg_stat1:
    current number of bytes on queue is 0
    number of message in queue is 0
    max number of bytes on queue is 16384
    pid of last msgsnd is 0
    pid of last msgrcv is 0
    last msgsnd time is Thu Jan  1 08:00:00 1970
    last msgrcv time is Thu Jan  1 08:00:00 1970
    last change time is Wed Nov 27 10:40:41 2019
    msg uid is 0
    msg gid is 0
    
     message send ok
    
     msg_stat2:
    current number of bytes on queue is 10
    number of message in queue is 1
    max number of bytes on queue is 16384
    pid of last msgsnd is 3912
    pid of last msgrcv is 0
    last msgsnd time is Wed Nov 27 10:40:42 2019
    last msgrcv time is Thu Jan  1 08:00:00 1970
    last change time is Wed Nov 27 10:40:41 2019
    msg uid is 0
    msg gid is 0
    
     read from msg queue 3 bytes
     type:8, message:abc
    
     msg_stat3:
    current number of bytes on queue is 0
    number of message in queue is 0
    max number of bytes on queue is 16384
    pid of last msgsnd is 3912
    pid of last msgrcv is 3912
    last msgsnd time is Wed Nov 27 10:40:42 2019
    last msgrcv time is Wed Nov 27 10:40:43 2019
    last change time is Wed Nov 27 10:40:41 2019
    msg uid is 0
    msg gid is 0
    
     msg set ok
    
     msg_stat4:
    current number of bytes on queue is 0
    number of message in queue is 0
    max number of bytes on queue is 16388
    pid of last msgsnd is 3912
    pid of last msgrcv is 3912
    last msgsnd time is Wed Nov 27 10:40:42 2019
    last msgrcv time is Wed Nov 27 10:40:43 2019
    last change time is Wed Nov 27 10:40:44 2019
    msg uid is 8
    msg gid is 8
    
     unlink msg queue ok
    

    程序首先生成key并创建消息队列,第1次输出消息队列的属性信息,接着发送数据,第2次输出属性信息,然后接收数据,第3次输出属性信息,再然后修改属性,第4次输出属性信息,最后删除消息队列。

    注意观察结果中的时间变化以及各种ID数值的变化

    展开全文
  • Linux — IPC进程通信之消息队列详解

    千次阅读 2017-06-22 22:33:07
    消息队列 消息队列与命名管道相比,消息队列的优势在于: 1、消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难。 2、同时通过发送消息还可以避免命名管道的...

    消息队列

     



    消息队列提供了一个一种从一个进程向另一个进程发送一个数据块的方法. 每个数据块都被认为是有一个类型,接受者进程接收的数据块可以有不同的

    类型值。我们可以通过发送消息来避免命名管道的同步和阻塞问题。 消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的,且

    消息队列的读取不一定是先入先出. 消息队列与命名管道有一个的不足,就是每个消息最大长度是有上限的.而且呢消息队列的生命周期是伴随内核. 

    所以如果你没有显示的删除它,那么在关机前它一直在.这里的消息队列我们可以看成一个链表队列,具体为什么? 让我们来看看消息队列的结构.


    消息队列的结构:



    在这里它的结构已经解释它的链式队列,_first和_last就是它的头指针和尾指针.接下来我们进行探究.大家都知道消息队列的核心就是如何表示出这

    个消息队列?举个例子,就是我要找出消息队列的"身份证".我调出 struct msqid_ds结构体的第一个条目,也就是IPC的共有结构体,我们再看看它的

    结构:




    我们现在看到的这里的_key,就是消息队列的唯一标识,想找到一个消息队列就是靠他找,这个我们以后可以会说到.



    消息队列基本命令:


    这里我就介绍2个最基本的命令对初学者一定够用,一定够用!


    首先是显示消息队列:

    [liang@www IPC2]$ ipcs 






    [liang@www IPC2]$ ipcs -q





    接下是删除一个消息队列:

    [liang@www IPC2]$ ipcrm -q msgid(消息队列对应的msgid)





    核心函数:


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


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

     

    参数: 

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

     

    msgflg:只有在共享内存不存在的时候,新的共享内存才建立,否则就会产生错误.如果单独使用IPC_CREAT 和msgget()函数么返回一个已经存在的共享

    内存的操作符,要么返回一个新建的共享内存的标识符.如果将IPC_CREAT和IPC_EXCL标志一起使用,msgget(),将返回一个新建的IPC标识符;如果该

    IPC资源已存在,或者返回-1.IPC_EXCL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得对象是新建的,而不是打开已有

    的对象.



    2.向队列读/写消息

     

    msgrev从队列中取用消息:

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

     

    msgsnd将数据放到消息队列中:

    int msgsnd(int msqid,const void* msgp,size_t msgsz,int sagflg):

     

     

    msqid:消息队列的标识码

     

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

     

    struct msgstru

    {

    long mtype; //大于0

    char mtext; 消息的大小

    };

     

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

     

    msgflg:用来致命核心程序在队列没有数据的情况的下所采取的行动.如果msgflg和常数IPC_NOWAIT合用,在msgsnd()执行时若是消息队列已满,则

    msgsnd()不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码ENOMSG。当msgflg为0时,

    msgrev()及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标识的消息队列.



    浅析key_t:


    ftok函数:


    函数ftok把一个已存在的路径名和一个整数标识得转换成一个key_t值,成为IPC键:


    #include<sys/types.h>

    #include<sys/ipc.h>

    key_t ftok(const char* pathname,int proj_id);

    它的实际实现其实就是把从pathname导出的信息与id的低序8位组合成一个整数IPC键.


    这里函数最后生成的key_t类型的_key就是该消息队列的唯一标识.





    运用消息队列形成一个双向通信:



    首先实现双向通信,因为双向通信设计多处代码复用所以我们对对应的功能进行函数封装,然后编写一个头文件确认函数.




    这里声明的宏后面都会用到,仔细看代码相信大家会看明白.

    接下来我把comm.c贴出来.

    /*************************************************************************
    > File Name: comm.c
    > Author: ma6174
    > Mail: ma6174@163.com
    > Created Time: Wed 21 Jun 2017 11:50:33 PM PDT
    ************************************************************************/
    
    #include"comm.h"
    
    static int commMsgQueue(int flags)
    {
      //这里的两个都是头文件里面定义的宏
    	key_t _key = ftok(PATHNAME, PROJ_ID);
    	if (_key < 0){
    		perror("ftok");
    		return -1;
    	}
    	int msgid = 0;
    	if (msgid = msgget(_key, flags)<0){
    		perror("msgget");
    		return -2;
    	}
    	return msgid;
    }
    //创建消息队列
    int creatMsgQueue()
    {
    	return commMsgQueue(IPC_CREAT | IPC_EXCL | 0666);
    }
    //得到一个消息队列
    int getMsgQueue()
    {
    	return commMsgQueue(IPC_CREAT);
    }
    //销毁一个消息队列
    int destroyMsgQueue(int msgid)
    {
    	if (msgctl(msgid, IPC_RMID, NULL) < 0){
    		perror("msgctl");
    		return -1;
    	}
    }
    //接收一个消息队列
    int recvMsg(int msgid, int type, char* _outMsg)
    {
    	struct msgbuf _mb;
    	if (msgrcv(msgid, &_mb, sizeof(_mb.mtext), type, 0)< 0){
    		perror("msgrcv");
    		return -1;
    	}
    	strcpy(_outMsg, _mb.mtext);
    	return 0;
    }
    //发送一个消息队列
    int sendMsg(int msgid, int who, const char *msg)
    {
    	struct msgbuf _mb;
    	_mb.mtype = who;
    	strcpy(_mb.mtext, msg);
    	if (msgsnd(msgid, (void*)&_mb, sizeof(_mb.mtext), 0) < 0)
    	{
    		perror("msgsnd");
    		return -1;
    	}
    	return 0;
    }

    这里都是灵活运用上面的几个函数,认真的看一看代码相信你一定可以看明白的.

    接下来就是两个互相通信的进程,我分别贴出来:

    Client.c



    server.c




    当我们分别运行这两个进程就可以实现进程之间的通信,最后的最后我也把Makefile贴出来吧.




    这些代码都是算是对核心函数的最基本的应用,认真看代码分析相信你一定可以掌握,我们最重要的是掌握函数的使用方法,和理

    解消息如何进行通信.

    最后看一下使用结果:




    最后总结:


    消息队列与命名管道相比,消息队列的优势在于:


    1.消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难。

    2.同时通过发送消息还可以避免命名管道的同步和阻塞问题,不需要由进程自己来提供同步方法。

    3.接收程序可以通过消息类型有选择地接收数据,而不是像命名管道中那样,只能默认地接收。


    4.双向通信 双向通信  双向通信


    展开全文
  • 学习环境 Centos6.5 Linux 内核 2.6一、什么是消息队列消息队列是SystemV版本中三种进程通信机制之一,另外两种是信号量和共享存储段。消息队列提供了进程间发送数据块的方法,而且每个数据块都有一个类型标识。...
  • 它的含义是,如果该消息队列不存在,那么就创建它,它的隐含含义就是如果该消息队列是存在的,那么O_CREAT不起作用,而仅仅执行打开操作。编译测试代码后重复运行该执行文件,都次都会成功打开,打印如下: $ ./...
  • IPC之Posix消息队列详解

    千次阅读 2016-08-27 20:49:19
    有足够写权限的线程可往队列中放置消息,有足够读权限的线程可从队列中取走消息,每个消息都是一个记录(非字节流式,也就是不需要自定义边界),它由发送者赋予一个优先级。在某个进程往一个队列写入消息之前,并不...
  • Linux内核等待队列详解

    千次阅读 2018-01-25 16:45:34
    等待队列用于管理应等待某个条件成立或者事件发生而防人之心不可无的进程。进程发出的请求暂时无法满足的时候,需要将进程阻塞直到条件成立。内核将因相同原因而阻塞的进程集中在一个队列上,该队列就是等待队列,...
  • linux 消息队列实现通信

    千次阅读 2016-10-27 08:36:03
    一、什么是消息队列 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步...
  • 1、消息队列简介 2、Linux消息队列的查看、删除指令 3、函数详解 4、通过父子进程实现互发消息代码
  • linux工作队列机制详解

    千次阅读 2013-05-29 15:43:54
    Linux自从2.6.20之后,工作队列发生了一些变化,目前从网络上搜索的资料一般都是介绍老版本的工作队列,很少见到对新版本的介绍。本文对新老版本都做了简要概述,并分别提供了简单的实作案例。 ***************...
  •  关于线程的管理(互斥量和条件变量)见:Linux线程管理必备:解析互斥量与条件变量的详解 一、消息队列的特点  1.消息队列消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识. 2.消息队列允许一个或...
  • linux 进程通信-消息队列详解,详细阐述了linux或者unix下面的进程通过消息队列通信
  • 为了大家都能很好的学习Linux,本人不才,贴出了自己写的 消息队列 编程,用的是多线程,不是多进程,原理一样。以后有时间会后续把其他几个技术一一详解,并贴上代码。好了,废话不多说,进入下面的实例讲解。 /***...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 37,093
精华内容 14,837
关键字:

linux消息队列详解

linux 订阅