<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    POSIX和SYSTEM的消息隊列應該注意的問題

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

     

    首先看看POSIX的代碼:

    1.posix_mq_server.c

    #include <mqueue.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <stdio.h>
    #define MQ_FILE "/mq_test"
    #define BUF_LEN 128

    int main()
    {
         mqd_t mqd;
        char buf[BUF_LEN];
        int  por = 0;
        int ret = 0;
        struct mq_attr attr;
        attr.mq_flags = 0;
        attr.mq_maxmsg = 3;
        attr.mq_msgsize = 50;
        attr.mq_curmsgs= 0;
        mqd = mq_open(MQ_FILE, O_WRONLY,0666,&attr);
        if (-1 == mqd)
        {
            printf("mq_open error.\n");
            return -1;
        }

        do{
            buf[BUF_LEN-1]='\0';
            printf("MQ_MSG : ");
            scanf("%s", buf);
            if(buf[BUF_LEN-1]!= '\0')
            {
                continue;
            }
            printf("strlen:%d\nMQ_POR : ",strlen(buf));
            scanf("%d", &por);
            ret== mq_send(mqd, buf, strlen(buf)+1, por);
            if (ret != 0)
            {
                perror("mq_send error.\n");
            }
            memset(buf,'\0',BUF_LEN);
        }while(strcmp(buf, "quit"));

        mq_close(mqd);
        mq_unlink(MQ_FILE);

        return 0;
    }
    2.posix_mq_client.c


    #include <mqueue.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <stdio.h>
    #define MQ_FILE "/mq_test"
    #define BUF_LEN 128

    int main()
    {
        mqd_t mqd;
        struct mq_attr attr;
        char buf[BUF_LEN + 1] = "quit";
        int cnt;
        int  por = 0;
        attr.mq_flags = 0;
        attr.mq_maxmsg = 128;
        attr.mq_msgsize = 128;
        attr.mq_curmsgs = 0;
        //mqd = mq_open(MQ_FILE, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR, NULL);
        mqd = mq_open(MQ_FILE, O_RDONLY | O_CREAT, 0644, &attr);
        if (-1 == mqd)
        {
            printf("mq_open error.\n");
            return -1;
        }

        do{
            cnt = mq_receive(mqd, buf, BUF_LEN, &por);
            if (0 < cnt)
            {
                printf("mq receive : ");
                fflush(stdout);
                buf[cnt] = '\0';
                printf("%s  por:%d\n", buf,por);
            }

        }while(strcmp(buf, "quit")==0);

        printf("\n");
        mq_close(mqd);
        mq_unlink(MQ_FILE);

        return 0;
    }

    3.makefile

    target:client  server
    client: posix_mq_client.c
        gcc posix_mq_client.c -o client -lrt
    server:posix_mq_server.c
        gcc posix_mq_server.c -o server -lrt

    clean:
        rm -f client server
        rm -f *.o

    運行make:

    ==[]==root@gaoke:~/code$./server 
    MQ_MSG : fgsdfgsdfgsdfg
    MQ_POR : 9
    MQ_MSG : dfgsdfgsdfg
    MQ_POR : 3
    MQ_MSG : dfghsdfhgjghdj
    MQ_POR : 6
    MQ_MSG : sdfgdgfhgjh
    MQ_POR : 2
    MQ_MSG : dsfghgjghjkh
    MQ_POR : 8
    MQ_MSG : sdfgsdfgsdfgsd
    MQ_POR : 5
    MQ_MSG : 

    ==[]==root@gaoke:~/code$./client 
    mq receive : fgsdfgsdfgsdfg  por:9
    mq receive : dsfghgjghjkh  por:8
    mq receive : dfghsdfhgjghdj  por:6
    mq receive : sdfgsdfgsdfgsd  por:5
    mq receive : dfgsdfgsdfg  por:3
    mq receive : sdfgdgfhgjh  por:2
    我們發現POSIX是嚴格按照優先級排序讀出的,而且先讀出作業優先級最高的作業。

    好了再看看我們的SYSTEM是如何進行的,先寫個簡單的代碼調試運行看看結果如何:

    首先我先說明一下System V系統的消息對列對象結構:

    01 02 03 04 05 06 07 08 09 10 11 12 13 struct msqid_ds {     struct ipc_perm     msg_perm; // 權限,跟共享內存一樣     struct msg      *msg_first;// 指向隊列的第一條消息     struct msg      *msg_last; // 指向隊列的最后一條消息     msglen_t        msg_cbytes;// 當前隊列所占字節數     msgnum_t        msg_qnum;  // 當前隊列的消息數     msglen_t        msg_qbytes;// 隊列允許的最大字節數     pid_t           msg_lspid; // 最后調用msgsnd的PID     pid_t           msg_lrpid; // 最后調用msgrcv的PID     time_t          msg_stime; // 最后調用msgsnd的時間     time_t          msg_rtime; // 最后調用msgrcv的時間     time_t          msg_ctime; // 最后調用msgctl的時間 }

    使用其中一個IPC機制時,系統內核會維護一個ipc權限對象,用于設置讀寫權限

    1 2 3 4 5 6 7 8 9 struct ipc_perm {     uid_t   uid;   // owner’s user id     gid_t   gid;       // owner’s group id     uid_t   cuid;      // creator’s user id     gid_t   cgid;      // creator’s group id     mode_t  mode;  // 讀寫權限     ulong_t seq;       // 序列號     key_t   key;       // IPC key 其次我們還知道在Linux下,消息隊列被創建在虛擬文件系統中。(其它實現可能也提供這樣的特性,但細節可能不一樣)此文件系統可以使用以下命令掛載(由超級用戶):
    # mkdir /dev/mqueue
    # mount -t mqueue none /dev/mqueue

     

    1.sys_msq_server.c

    #include <stdio.h>
    #include <string.h>
    #include <limits.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <mqueue.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #define MQ_FILE "./mq_test"
    #define BUF_LEN 128
    struct msgbuf {
       long mtype;     /*  message type, must be > 0 */
       char mtext[256];  /*  message data */
    };
    int  main()
    {
        struct msqid_ds info={0};
        struct msgbuf MSG={0};
        key_t key = ftok(MQ_FILE,10);
        int cnt =  msgget(key,IPC_CREAT|0666);//0:取消息隊列標識符,若不存在則函數會報錯IPC_CREAT:當msgflg&IPC_CREAT為真時,如果內核中不存在鍵值與key相等的消息隊列,則新建一個消息隊列;如果存在這樣的消息隊列,返回此消息隊列的標識符IPC_CREAT|IPC_EXCL:如果內核中不存在鍵值與key相等的消息隊列,則新建一個消息隊列;如果存在這樣的消息隊列則報錯
        if(cnt == -1)
        {
            perror("error!");
        }
        while(1){
            printf("enter the MSG:\n");
            scanf("%s",MSG.mtext);
            MSG.mtype = 1;
            //

            msgsnd(cnt,&MSG,strlen(MSG.mtext)+1,IPC_NOWAIT);// 最后一個參數:0:當消息隊列滿時,msgsnd將會阻塞,直到消息能寫進消息隊列IPC_NOWAIT:當消息隊列已滿的時候,msgsnd函數不等待立即返回IPC_NOERROR:若發送的消息大于size字節,則把該消息截斷,截斷部分將被丟棄,且不通知發送進程
            msgctl(cnt,IPC_STAT,&info);//IPC_STAT:獲得msgid的消息隊列頭數據到buf中IPC_SET:設置消息隊列的屬性,要設置的屬性需先存儲在buf中,可設置的屬性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes

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

     

    2.sys_msq_client.c

    #include <stdio.h>
    #include <string.h>
    #include <limits.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <mqueue.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #define MQ_FILE "./mq_test"
    #define BUF_LEN 128
    struct msgbuf {
       long mtype;     /*  message type, must be > 0 */
       char mtext[256];  /*  message data */
    };
    int  main()
    {
        struct msqid_ds info={0};
        struct msgbuf MSG={0};
        key_t key = ftok(MQ_FILE,10);
        int cnt =  msgget(key,IPC_CREAT|0666);
        int size =0;
        if(cnt == -1)
        {
            perror("error!");
        }
        while(1){
            size = msgrcv(cnt,&MSG,256,1,IPC_NOWAIT);
            if(size > 0)
            {
                   puts(MSG.mtext);
            msgctl(cnt,IPC_STAT,&info);
            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 ) ;
            }
        }
    }
    3.makefile

    target:client  server
    client: sys_msq_client.c
        gcc sys_msq_client.c -o client -lrt
    server:sys_msq_server.c
        gcc sys_msq_server.c -o server -lrt

    clean:
        rm -f client server
        rm -f *.o

    然后make運行

    ==[]==root@gaoke:~/code$./server 
    enter the MSG:
    aaaa
    uid:0, gid = 0, cuid = 0, cgid= 0
    read-write:666, cbytes = 5, qnum = 1, qbytes= 65536

    ------ Message Queues --------
    key        msqid      owner      perms      used-bytes   messages    
    0xffffffff 0          root       666        5            1          

    enter the MSG:
    dddd
    uid:0, gid = 0, cuid = 0, cgid= 0
    read-write:666, cbytes = 10, qnum = 2, qbytes= 65536

    ------ Message Queues --------
    key        msqid      owner      perms      used-bytes   messages    
    0xffffffff 0          root       666        10           2          

    enter the MSG:
    fffff
    uid:0, gid = 0, cuid = 0, cgid= 0
    read-write:666, cbytes = 16, qnum = 3, qbytes= 65536

    ------ Message Queues --------
    key        msqid      owner      perms      used-bytes   messages    
    0xffffffff 0          root       666        16           3          

    enter the MSG:
    ggggg
    uid:0, gid = 0, cuid = 0, cgid= 0
    read-write:666, cbytes = 22, qnum = 4, qbytes= 65536

    ------ Message Queues --------
    key        msqid      owner      perms      used-bytes   messages    
    0xffffffff 0          root       666        22           4           

     ==[]==root@gaoke:~/code$./client 
    asdfasdfas
    uid:0, gid = 0, cuid = 0, cgid= 0
    read-write:666, cbytes = 0, qnum = 0, qbytes= 65536
    asdfasdfasdf
    uid:0, gid = 0, cuid = 0, cgid= 0
    read-write:666, cbytes = 0, qnum = 0, qbytes= 65536
    ssss
    uid:0, gid = 0, cuid = 0, cgid= 0
    read-write:666, cbytes = 0, qnum = 0, qbytes= 65536

    ==[]==root@gaoke:~/code$./client 
    aaaa
    uid:0, gid = 0, cuid = 0, cgid= 0
    read-write:666, cbytes = 17, qnum = 3, qbytes= 65536

    dddd
    uid:0, gid = 0, cuid = 0, cgid= 0
    read-write:666, cbytes = 12, qnum = 2, qbytes= 65536
    fffff
    uid:0, gid = 0, cuid = 0, cgid= 0
    read-write:666, cbytes = 6, qnum = 1, qbytes= 65536
    ggggg
    uid:0, gid = 0, cuid = 0, cgid= 0
    read-write:666, cbytes = 0, qnum = 0, qbytes= 65536
    由此我們還發現了什么呢?有沒有發現SYSTEM的消息輸入長度是固定的,然而POSIX的是可變長的。

     

    SYSTEM的消息隊列函數由msgget、msgctl、msgsnd、msgrcv四個函數組成。下面的表格列出了這四個函數的函數原型及其具體說明。

    1.   msgget函數原型

    msgget(得到消息隊列標識符或創建一個消息隊列對象)

    所需頭文件

    #include <sys/types.h>

    #include <sys/ipc.h>

    #include <sys/msg.h>

    函數說明

    得到消息隊列標識符或創建一個消息隊列對象并返回消息隊列標識符

    函數原型

    int msgget(key_t key, int msgflg)

    函數傳入值

    key

    0(IPC_PRIVATE):會建立新的消息隊列

    大于0的32位整數:視參數msgflg來確定操作。通常要求此值來源于ftok返回的IPC鍵值

    msgflg

    0:取消息隊列標識符,若不存在則函數會報錯

    IPC_CREAT:當msgflg&IPC_CREAT為真時,如果內核中不存在鍵值與key相等的消息隊列,則新建一個消息隊列;如果存在這樣的消息隊列,返回此消息隊列的標識符

    IPC_CREAT|IPC_EXCL:如果內核中不存在鍵值與key相等的消息隊列,則新建一個消息隊列;如果存在這樣的消息隊列則報錯

    函數返回值

    成功:返回消息隊列的標識符

    出錯:-1,錯誤原因存于error中

    附加說明

    上述msgflg參數為模式標志參數,使用時需要與IPC對象存取權限(如0600)進行|運算來確定消息隊列的存取權限

    錯誤代碼

    EACCES:指定的消息隊列已存在,但調用進程沒有權限訪問它

    EEXIST:key指定的消息隊列已存在,而msgflg中同時指定IPC_CREAT和IPC_EXCL標志

    ENOENT:key指定的消息隊列不存在同時msgflg中沒有指定IPC_CREAT標志

    ENOMEM:需要建立消息隊列,但內存不足

    ENOSPC:需要建立消息隊列,但已達到系統的限制

    如果用msgget創建了一個新的消息隊列對象時,則msqid_ds結構成員變量的值設置如下:

    Ÿ        msg_qnum、msg_lspid、msg_lrpid、 msg_stime、msg_rtime設置為0。

    Ÿ        msg_ctime設置為當前時間。

    Ÿ        msg_qbytes設成系統的限制值。

    Ÿ        msgflg的讀寫權限寫入msg_perm.mode中。

    Ÿ        msg_perm結構的uid和cuid成員被設置成當前進程的有效用戶ID,gid和cuid成員被設置成當前進程的有效組ID。

    2.   msgctl函數原型

    msgctl (獲取和設置消息隊列的屬性)

    所需頭文件

    #include <sys/types.h>

    #include <sys/ipc.h>

    #include <sys/msg.h>

    函數說明

    獲取和設置消息隊列的屬性

    函數原型

    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,卻無足夠的權限執行

    3.   msgsnd函數原型

    msgsnd (將消息寫入到消息隊列)

    所需頭文件

    #include <sys/types.h>

    #include <sys/ipc.h>

    #include <sys/msg.h>

    函數說明

    將msgp消息寫入到標識符為msqid的消息隊列

    函數原型

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

    函數傳入值

    msqid

    消息隊列標識符

    msgp

    發送給隊列的消息。msgp可以是任何類型的結構體,但第一個字段必須為long類型,即表明此發送消息的類型,msgrcv根據此接收消息。msgp定義的參照格式如下:

        struct s_msg{ /*msgp定義的參照格式*/
         long type; /* 必須大于0,消息類型 */
               char mtext[256]; /*消息正文,可以是其他任何類型*/
        } msgp;

    msgsz

    要發送消息的大小,不含消息類型占用的4個字節,即mtext的長度

    msgflg

    0:當消息隊列滿時,msgsnd將會阻塞,直到消息能寫進消息隊列

    IPC_NOWAIT:當消息隊列已滿的時候,msgsnd函數不等待立即返回

    IPC_NOERROR:若發送的消息大于size字節,則把該消息截斷,截斷部分將被丟棄,且不通知發送進程。

    函數返回值

    成功:0

    出錯:-1,錯誤原因存于error中

    錯誤代碼

    EAGAIN:參數msgflg設為IPC_NOWAIT,而消息隊列已滿

    EIDRM:標識符為msqid的消息隊列已被刪除

    EACCESS:無權限寫入消息隊列

    EFAULT:參數msgp指向無效的內存地址

    EINTR:隊列已滿而處于等待情況下被信號中斷

    EINVAL:無效的參數msqid、msgsz或參數消息類型type小于0

       msgsnd()為阻塞函數,當消息隊列容量滿或消息個數滿會阻塞。消息隊列已被刪除,則返回EIDRM錯誤;被信號中斷返回E_INTR錯誤。

     如果設置IPC_NOWAIT消息隊列滿或個數滿時會返回-1,并且置EAGAIN錯誤。

    msgsnd()解除阻塞的條件有以下三個條件:

    ①    不滿足消息隊列滿或個數滿兩個條件,即消息隊列中有容納該消息的空間。

    ②    msqid代表的消息隊列被刪除。

    ③    調用msgsnd函數的進程被信號中斷。

    4.   msgrcv函數原型

    msgrcv (從消息隊列讀取消息)

    所需頭文件

    #include <sys/types.h>

    #include <sys/ipc.h>

    #include <sys/msg.h>

    函數說明

    從標識符為msqid的消息隊列讀取消息并存于msgp中,讀取后把此消息從消息隊列中刪除

    函數原型

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

                          int msgflg);

    函數傳入值

    msqid

    消息隊列標識符

    msgp

    存放消息的結構體,結構體類型要與msgsnd函數發送的類型相同

    msgsz

    要接收消息的大小,不含消息類型占用的4個字節

    msgtyp

    0:接收第一個消息

    >0:接收類型等于msgtyp的第一個消息

    <0:接收類型等于或者小于msgtyp絕對值的第一個消息

    msgflg

    0: 阻塞式接收消息,沒有該類型的消息msgrcv函數一直阻塞等待

    IPC_NOWAIT:如果沒有返回條件的消息調用立即返回,此時錯誤碼為ENOMSG

    IPC_EXCEPT:與msgtype配合使用返回隊列中第一個類型不為msgtype的消息

    IPC_NOERROR:如果隊列中滿足條件的消息內容大于所請求的size字節,則把該消息截斷,截斷部分將被丟棄

    函數返回值

    成功:實際讀取到的消息數據長度

    出錯:-1,錯誤原因存于error中

    錯誤代碼

    E2BIG:消息數據長度大于msgsz而msgflag沒有設置IPC_NOERROR

    EIDRM:標識符為msqid的消息隊列已被刪除

    EACCESS:無權限讀取該消息隊列

    EFAULT:參數msgp指向無效的內存地址

    ENOMSG:參數msgflg設為IPC_NOWAIT,而消息隊列中無消息可讀

    EINTR:等待讀取隊列內的消息情況下被信號中斷

    msgrcv()解除阻塞的條件有以下三個:

    ①    消息隊列中有了滿足條件的消息。

    ②    msqid代表的消息隊列被刪除。

    ③    調用msgrcv()的進程被信號中斷。

     

    消息隊列使用程序范例

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全