細說linux IPC(十):system V 訊息佇列

來源:互聯網
上載者:User

標籤:ipc

【著作權聲明:尊重原創,轉載請保留出處:blog.csdn.net/shallnet 或 .../gentleliu,文章僅供學習交流,請勿用於商業用途】

        system V訊息佇列和posix訊息佇列類似,linux系統這兩種訊息佇列都支援。先來看一下system V訊息佇列相關操作及其函數。
  • msgget()函數建立一個訊息佇列或開啟一個訊息佇列。
       #include <sys/types.h>       #include <sys/ipc.h>       #include <sys/msg.h>       int msgget(key_t key, int msgflg);
參數key為ftok返回值或IPC_PRIVATE;參數msgflg為IPC_CREAT或和IPC_CREAT | IPC_EXCL,當訊息佇列已經存在時,而msgflg指定為IPC_CREAT | IPC_EXCL,函數返回EEXIST。在系統中的每一個訊息佇列,核心維護這樣一個結構體:
           struct msqid_ds {               struct ipc_perm msg_perm;     /* Ownership and permissions */               time_t          msg_stime;    /* Time of last msgsnd(2) */               time_t          msg_rtime;    /* Time of last msgrcv(2) */               time_t          msg_ctime;    /* Time of last change */               unsigned long   __msg_cbytes; /* Current number of bytes in                                                queue (nonstandard) */               msgqnum_t       msg_qnum;     /* Current number of messages                                                in queue */               msglen_t        msg_qbytes;   /* Maximum number of bytes                                                allowed in queue */               pid_t           msg_lspid;    /* PID of last msgsnd(2) */               pid_t           msg_lrpid;    /* PID of last msgrcv(2) */           };
其中ipc_perm 結構體定義如下:
           struct ipc_perm {               key_t          __key;       /* Key supplied to msgget(2) */               uid_t          uid;         /* Effective UID of owner */               gid_t          gid;         /* Effective GID of owner */               uid_t          cuid;        /* Effective UID of creator */               gid_t          cgid;        /* Effective GID of creator */               unsigned short mode;        /* Permissions */               unsigned short __seq;       /* Sequence number */           };
當一個訊息佇列初始化完後,該訊息佇列對應的結構被作如下初始化:msg_perm.cuid 和 msg_perm.uid被設定為當前進程的使用者ID,msg_perm.cuid 和 msg_perm.uid被設定為當前進程的組ID;msg_perm.mode被設定為msgflg;msg_qnum, msg_lspid, msg_lrpid, msg_stime and msg_rtime  被設定為 0;msg_ctime被設定為目前時間;msg_qbytes被設定為系統限制值MSGMNB。
  • msgsnd()函數向訊息佇列中添加訊息。
       #include <sys/types.h>       #include <sys/ipc.h>       #include <sys/msg.h>       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
參數msgqid為訊息佇列標識,為msgget()返回值;參數msgp為指向使用者定義結構體的指標,結構體定義格式模板如下:
           struct msgbuf {               long mtype;       /* message type, must be > 0 */               char mtext[1];    /* message data */           };
            mtex域為數組,該數組大小為函數參數msgsz,mtype的值必須大於0;該結構由使用者自己定義,一般根據實際應用來定義。參數msgsz為將要發送訊息的長度,可以指定為0,一般指定為msgbuf結構體長度減去long類型長度(sizeof(msgbuf) - sizeof(long));參數msgflg為0,或IPC_NOWAIT,IPC_NOWAIT為非阻塞,發送時如果對列資料已滿,則函數馬上返回錯誤EAGAIN,如果msgflg為0,則阻塞直到訊息佇列由足夠空間添加新的訊息,或訊息佇列從系統中刪除,或被訊號中斷。
  • msgrcv()函數從訊息佇列中讀取訊息。
       #include <sys/types.h>       #include <sys/ipc.h>       #include <sys/msg.h>       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,  int msgflg);
參數msgid同函數msgsnd;參數msgp通函數函數msgsnd,為指向接收訊息結構體指標;參數msgsz為接受訊息的緩衝大小;參數msgtyp為讀取何種訊息,如果為:        0 - 讀取訊息佇列中最早的訊息,以先進先出的方式讀取訊息。        > 0 - 讀取訊息佇列類型為msgtyp最早的訊息。        < 0 - 讀取訊息佇列中,訊息類型小於或等於msgtyp絕對值的訊息中最小訊息類型的最早訊息。參數msgflg為如下的值或者幾個值得或:        IPC_NOWAIT - 如果隊列中沒有則立即返回;如果未指定該選項,函數將一直阻塞,直到指定的訊息可讀取,或訊息佇列從系統中刪除,或被訊號中斷;        MSG_NOERROR - 截斷訊息如果接收到的訊息大小大於msgsz給定的值;        MSG_EXCEPT - 使用大於0的msgtyp去讀取訊息類型不為msgtyp第一個訊息;
msgctl()函數對一個隊列做控制操作。       #include <sys/msg.h>       int msgctl(int msqid, int cmd, struct msqid_ds *buf);參數cmd為對隊列要做的操作,這些操作有:
        IPC_STAT - 擷取訊息佇列資訊,從核心資料結構中拷貝訊息佇列為msqid的結構體到buf指標指向的msqid_ds 結構體中。        IPC_SET - 使用buf指向的結構體來設定msgqid對應的訊息佇列核心結構體;        IPC_RMID - 刪除msgqid訊息佇列;        IPC_INFO,MSG_INFO, MSG_STAT詳見linux man函數手冊。樣本:服務進程:
#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/ipc.h>#include <errno.h>#include <unistd.h>#include <signal.h>#include <stdlib.h>#include "slnmq.h"static int recv_type, msgid;int sln_server_loop(void){    int                 rcvmsg_len;    key_t               key;    struct sln_msgbuf   slnmsg;    key = ftok(SLN_IPC_MQ_NAME, 0);    if (key < 0) {        fprintf(stderr, "ftok: %s\n", strerror(errno));        return -1;    }    msgid = msgget(key, IPC_CREAT | IPC_EXCL);    if ((msgid < 0) && (errno != EEXIST)) {        fprintf(stderr, "msgget: %s\n", strerror(errno));        return -1;    }    if (msgid < 0) {        printf("Open existing mq!\n");        msgid = msgget(key, IPC_CREAT);        if (msgid < 0) {            fprintf(stderr, "msgget: %s\n", strerror(errno));            return -1;        }    }    sleep(10); //just for test    for (;;) {        rcvmsg_len = msgrcv(msgid, &slnmsg, SLN_IPC_MQ_MSGSIZE, recv_type, 0);        if (rcvmsg_len < 0) {            fprintf(stderr, "msgrcv: %s\n", strerror(errno));            sleep(1);            continue;        }        fprintf(stdout, "receive -  recv len: %d, msg type: %d, msg: %s\n",                rcvmsg_len, slnmsg.msgtype, slnmsg.msg);    }    return 0;}static void sigint_func(int sig){    if (msgctl(msgid, IPC_RMID, NULL) < 0) {        fprintf(stderr, "msgctl: %s\n", strerror(errno));    }    exit(0);}int main(int argc, const char *argv[]){    FILE                *fp = NULL;    if (argc != 2) {        fprintf(stderr, "Usage: %s <msg type>\n", argv[0]);//運行時,指定服務要進程接收的訊息類型        return -1;    }    recv_type = atoi(argv[1]);    if (access(SLN_IPC_MQ_NAME, F_OK) < 0) {        fp = fopen(SLN_IPC_MQ_NAME, "w+");        if (NULL != fp) {            fclose(fp);        }    }    signal(SIGINT, sigint_func); //服務進程捕獲中斷訊號,在中斷處理函數裡面刪除訊息佇列並推出程式    sln_server_loop();    return 0;}
客戶進程:
#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/ipc.h>#include <errno.h>#include "slnmq.h"int sln_msgsnd(int msg_type, const void *sndmsg, int sndlen){    int                 msgid, rcvmsg_len;    key_t               key;    struct sln_msgbuf   slnmsg;    key = ftok(SLN_IPC_MQ_NAME, 0);    if (key < 0) {        fprintf(stderr, "ftok: %s\n", strerror(errno));        return -1;    }    msgid = msgget(key, IPC_CREAT);    if (msgid < 0) {        fprintf(stderr, "msgget: %s\n", strerror(errno));        return -1;    }    slnmsg.msgtype = msg_type;    memcpy(&slnmsg.msg, sndmsg, sndlen);    msgsnd(msgid, (void *)&slnmsg, sndlen, 0);}int main(int argc, const char *argv[]){    int     type;    if (argc != 3) {        fprintf(stderr, "Usage: %s <msg type> <msg content>\n", argv[0]);        return -1;    }    type = atoi(argv[1]);    sln_msgsnd(type, argv[2], strlen(argv[2]) + 1);    return 0;}
服務進程首先運行,並在開始讀取訊息處等待10秒,客戶進程在服務進程還未開始讀取資料時向訊息佇列加入多條訊息,如下:
./client 1 abcd./client 2 efghi./client 3 jkl./client 4 mn./client 5 opqrst./client 6 uv./client 7 w./client 8 xyz
當服務進程運行時會從訊息佇列中取出服務進程要取出類型(運行時參數指定)的訊息,下面分別是伺服器運行時指定的訊息類型以及取出的訊息(用戶端的運行都如上):
# ./server 0(讀取所有訊息)receive -  recv len: 5, msg type: 1, msg: abcdreceive -  recv len: 6, msg type: 2, msg: efghireceive -  recv len: 4, msg type: 3, msg: jklreceive -  recv len: 3, msg type: 4, msg: mnreceive -  recv len: 7, msg type: 5, msg: opqrstreceive -  recv len: 3, msg type: 6, msg: uvreceive -  recv len: 2, msg type: 7, msg: wreceive -  recv len: 4, msg type: 8, msg: xyz
# ./server 4 (讀取訊息類型為4的訊息)Open existing mq!receive -  recv len: 3, msg type: 4, msg: mn
# ./server -5     (讀取訊息小於5的訊息)        Open existing mq!receive -  recv len: 5, msg type: 1, msg: abcdreceive -  recv len: 6, msg type: 2, msg: efghireceive -  recv len: 4, msg type: 3, msg: jklreceive -  recv len: 3, msg type: 4, msg: mnreceive -  recv len: 7, msg type: 5, msg: opqrst
在伺服器未退出時,可以使用系統命令ipcs查看系統中的訊息佇列:
# ipcs------ Message Queues --------key        msqid      owner      perms      used-bytes   messages    0x001fffde 229376     root       0          0            0          
當使用者按下CTRL+C時,程式會刪除掉該訊息佇列,按下CTRL+C後,再查看:
# ipcs------ Message Queues --------key        msqid      owner      perms      used-bytes   messages    

可以看到程式中的刪除是成功的。
posix訊息佇列和system V訊息佇列大致類似,不過通過這兩節可以看到有一些差別:posix訊息佇列有優先順序的概念,system V沒有,只能按照訊息類型的先進先出來讀取訊息即FIFO;system V訊息佇列有訊息類型的概念,並且可以按照訊息類型取出訊息,而posix不能區分訊息類型;本節源碼下載:點擊開啟連結

細說linux IPC(十):system V 訊息佇列

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.