• UNIX环境高级编程——system V消息队列


         unix早期通信机制中的信号能够传送的信息量有限,管道则只能传送无格式字节流,这远远是不够的。
         消息队列(也叫报文队列)客服了这些缺点:
         消息队列就是一个消息的链表。
         可以把消息看作一个记录,具有特定的格式
         进程可以按照一定的规则向消息队列中添加新消息;另一些进程可以从消息队列中读走消息。  

         消息队列是随内核持续的,只有内核重启或人工删除时,该消息队列才会被删除。   

          system V消息队列使用消息队列标识符标识。具有足够特权的任何进程都可以往一个给定队列放置一个消息,具有足够特权的任何进程都可以从一个给定队列读出一个消息。

          消息队列具有一定的FIFO特性,但是它可以实现消息的随机查询,比FIFO具有更大的优势。同时这些消息又存在于内核中,由“队列”ID来标识消息队列的实现包括创建或打开消息队列,添加消息,读取消息和控制消息队列这四种操作。

         对于系统中的每个消息队列,内核维护一个定义在<sys/msg.h>头文件中的信息结构。

    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 */
    };
         我们可以将内核中的某个特定的消息队列画为一个消息链表,如图假设有一个具有三个消息的队列,消息长度分别为1字节,2字节和3字节,而且这些消息就是以这样的顺序写入该队列的。再假设这三个消息的类型分别为100,200,300.



    1.msgget函数

          msgget函数用于创建一个新的消息队列或访问一个已存在的消息队列。

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

    返回值是一个整数标识符,其他三个msg函数就用它来指代该队列。它是基于指定的key产生的,而key即可以是ftok的返回值,也可以是常值IPC_PRIVATE。

          oflag是读写权限值得组合。它还可以与IPC_CREAT或IPC_CREAT | IPC_EXCL按位或,IPC_NOWAIT --- 读写消息队列要求无法得到满足时,不阻塞

    当创建一个新消息队列时,msqid_ds结构的如下成员被初始化。

    (1)msg_perm结构的uid和cuid成员被设置成当前进程的有效用户ID,gid和cgid成员被设置成当前进程的有效组ID。

    (2)oflag中的读写权限位存放在msg_perm.mode中。

    (3)msg_qnum,msg_lspid,msg_lrpid,msg_stime和msg_rtime被置为0.

    (4)msg_ctime被设置成当前时间。

    (5)msg_qbytes被设置成系统限制值。

    huangcheng@ubuntu:~$ ipcs -q  ----查看消息队列
    
    ------ Message Queues --------
    key        msqid      owner      perms      used-bytes   messages
    

    2.msgsnd函数

        使用msgget函数打开一个消息队列后,我们使用msgsnd往其上放置一个消息。

    #include <sys/msg.h>
    int msgsnd(int msgid,const void *ptr,size_t length,int flag);
    其中msqid是由msgget返回的标识符。ptr是一个结构指针,该结构具有如下模板,它定义在<sys/msg.h>中。

               struct msgbuf {
                   long mtype;       /* message type, must be > 0 */
                   char mtext[1];    /* message data */
               };

     msgsnd的length参数以字节为单位指定待发送消息的长度。这是位于长整数消息类型之后的用户自定义数据的长度(注意:不包括消息类型)。该长度可以是0.

        flag参数既可以是0,也可以是IPC_NUWAIT。IPC_NOWAIT标志使得msgsnd调用非阻塞如果没有存放新消息的可用空间(即消息队列已满),该函数马上返回。这个条件发生的情形包括:

    (1)在指定的队列中已有太多的字节(对于该队列的msqid_ds结构中的msg_qbytes值);

    (2)在系统范围存在太多的消息。

      如果这两个条件中有一个存在,而且IPC_NUWAIT标志已指定,msgsnd就返回一个EAGAIN错误。如果这两个条件有一个存在,但是IPC_NUWAIT标志未指定,那么调用线程被投入睡眠直到

    (1)具备存放新消息的空间;

    (2)由msqid标志的消息队列从系统中删除(这种情况下返回一个EIDRM错误);

    (3)调用线程被某个捕获的信号所中断(这种情况下返回一个EINTR错误)。

    3.msgrcv函数

          使用msgrcv函数从某个消息队列中读取一个消息。

    #include <sys/msg.h>
    ssize_t msgrcv(int msqid,void *ptr,size_t lengh,long type,int flag);

    其中ptr参数指定所接收消息的存放位置。跟msgsnd一样,该指针指向紧挨在真正的消息数据之前返回的长整数类型字段。

          length指定了由ptr指向的缓冲区中数据部分的大小。这是该函数能返回的最大数据量。该长度不包括长整数类型字段

          type指定希望从所给定的队列中读出什么样的消息。

    (1)如果type为0,那就返回该队列中的第一个消息,既然每个消息队列都是作为一个FIFO链表维护的,因此type为0指定返回该队列中最早的消息。

    (2)如果type大于0,那就返回其类型值为type的第一个消息。

    (3)如果type小于0,那就返回其类型小于或等于type参数的绝对值的消息中类型值最小的第一个消息。

          msgrcv的flag参数指定所请求类型的消息不在所指定的队列中时该做何处理。在没有消息可得的情况下,如果设置了flag中的IPC_NOWAIT位,msgrcv函数就立即返回一个ENOMSG错误。否则,设置了flag为0,调用者被阻塞到下列某个事件发生为止:

    (1)有一个所请求类型的消息可获取;

    (2)由msqid标志的消息队列从系统中删除(这种情况下返回一个EIDRM错误);

    (3)调用线程被某个捕获的信号所中断(这种情况下返回一个EINTR错误)。

        flag参数中另有一位可以指定:MSG_NOERROR。当所接收消息的真正数据部分大于length参数时,如果设置了该位,msgrcv函数就只是截短数据部分,而不返回错误。否则,ms_grcv返回一个E2BIC错误。

        成功返回时,msgrcv返回的是所接收消息中数据的字节数。它不包括也通过ptr参数返回的长整数消息类型所需的几个字节。

    4.msgctl函数

       msgctl函数提供在一个消息队列上的各种控制操作。

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

    msgctl函数提供3个命令。

    IPC_RMID   从系统中删除由msqid指定的消息队列。当前在该队列上的任何消息都被丢弃。对于该命令而言,msgctl函数的第三个参数被忽略。

    IPC_SET     给所指定的消息队列设置其msgid_ds结构的以下4个成员:msg_perm.uid,msg_perm.gid,msg_perm.mode和msg_qbytes。他们的值来自buff参数指向的结构中的相应的成员。

    IPC_STAT   读取消息队列的属性,并将其保存在buf指向的缓冲区中



    msgrcv.c用于接收消息,msgsend.c用于发送消息:


    msgrcv.c

    /* Here's the receiver program. */
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <unistd.h>
    
    #include <sys/msg.h>
    
    
    struct my_msg_st {
        long int my_msg_type;
        char some_text[BUFSIZ];
    };
    
    int main()
    {
        int running = 1;
        int msgid;
        struct my_msg_st some_data;
        long int msg_to_receive = 0;
    
    /* First, we set up the message queue. */
    
        msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    
        if (msgid == -1) {
            fprintf(stderr, "msgget failed with error: %d
    ", errno);
            exit(EXIT_FAILURE);
        }
    
    /* Then the messages are retrieved from the queue, until an end message is encountered.
     Lastly, the message queue is deleted. */
    
        while(running) {
            if (msgrcv(msgid, (void *)&some_data, BUFSIZ,msg_to_receive, 0) == -1) {
                fprintf(stderr, "msgrcv failed with error: %d
    ", errno);
                exit(EXIT_FAILURE);
            }
            printf("You wrote: %s", some_data.some_text);
            if (strncmp(some_data.some_text, "end", 3) == 0)
                running = 0;
        }
    
        if (msgctl(msgid, IPC_RMID, 0) == -1) {
            fprintf(stderr, "msgctl(IPC_RMID) failed
    ");
            exit(EXIT_FAILURE);
        }
    
        exit(EXIT_SUCCESS);
    } 



    msgsend.c

    /* The sender program is very similar to msg1.c. In the main set up, delete the
     msg_to_receive declaration and replace it with buffer[BUFSIZ], remove the message
     queue delete and make the following changes to the running loop.
     We now have a call to msgsnd to send the entered text to the queue. */
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <unistd.h>
    
    #include <sys/msg.h>
    
    #define MAX_TEXT 512
    
    struct my_msg_st {
        long int my_msg_type;
        char some_text[MAX_TEXT];
    };
    
    int main()
    {
        int running = 1;
        struct my_msg_st some_data;
        int msgid;
        char buffer[BUFSIZ];
    
        msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    
        if (msgid == -1) {
            fprintf(stderr, "msgget failed with error: %d
    ", errno);
            exit(EXIT_FAILURE);
        }
    
        while(running) {
            printf("Enter some text: ");
            fgets(buffer, BUFSIZ, stdin);
            some_data.my_msg_type = 1;
            strcpy(some_data.some_text, buffer);
    
            if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1) {
                fprintf(stderr, "msgsnd failed
    ");
                exit(EXIT_FAILURE);
            }
            if (strncmp(buffer, "end", 3) == 0) 
                running = 0;
         }
    
        exit(EXIT_SUCCESS);
    }

    运行结果:

    huangcheng@ubuntu:~$ ./msgsend
    Enter some text: ctt
    Enter some text: huangcheng
    Enter some text: end
    
    huangcheng@ubuntu:~$ ./msgrcv
    You wrote: ctt
    You wrote: huangcheng
    You wrote: end




  • 相关阅读:
    一个Winform下DataGridView控件外观的自定义类
    从以往子类化跟踪MouseLeave深入讨论VB6的自定义Hook类
    使用VB6写一个自定义的进度信息框窗口
    C#研究OpenXML之路(4-使用第三方库)
    Access SQL实现连续及不连续Rank排名
    C#研究OpenXML之路(3-OpenXMLSDKToolV25.msi)
    C#研究OpenXML之路(2-DocumentFormat.OpenXml命名空间)
    C#研究OpenXML之路(1-新建工作簿文件)
    统一我的博客文章的CSS样式代码
    C#图片灰度处理(位深度24→位深度8)
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6332665.html
Copyright © 2020-2023  润新知