linux程式間通訊-----System V訊息佇列總結例項

readyao發表於2015-12-17

什麼是訊息佇列?

訊息佇列提供了一個從一個程式向另外一個程式傳送一塊資料的方法,訊息佇列具有核心持續性;
每個資料塊都被認為是有一個型別,接收者程式接收的資料塊可以有不同的型別值;
訊息佇列也有管道一樣的不足,就是每個訊息的最大長度是有上限的(MSGMAX),每個訊息佇列的總的位元組數是有上限的(MSGMNB),系統上訊息佇列的總數也有一個上限(MSGMNI)

cat /proc/sys/kernel/msgmax 最大訊息長度限制,8192=8K
cat /proc/sys/kernel/msgmnb 訊息佇列總的位元組數,16384 = 16K
cat /proc/sys/kernel/msgmni 訊息條目數,169


核心為每個IPC物件維護一個資料結構,在呼叫msgctl的時候就會用到;

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) */
};

//The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):

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 */
};


主要函式API:

1:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
對訊息佇列進行控制
msqid: 由msgget函式返回的訊息佇列標識碼
cmd:是將要採取的動作,(有三個可取值)
IPC_STAT(將訊息佇列的msqid_ds結構體的值複製到當前buf指向的記憶體空間,讀取訊息佇列的資訊)
IPC_SET(當前buf指向的記憶體空間的值複製給訊息佇列的msqid_ds結構體的值,設定訊息佇列的資訊)
IPC_RMID(刪除訊息佇列)

2:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
用來建立和訪問一個訊息佇列
key: 某個訊息佇列的名字, 比如0x1234, 0x3232.....
msgflg:由九個許可權標誌構成,它們的用法和建立檔案時使用的mode模式標誌是一樣的;
If msgflg specifies both IPC_CREAT and IPC_EXCL and a message queue already exists for key, then msgget() fails with errno set to EEXIST
返回值:成功返回一個非負整數,即該訊息佇列的標識碼;失敗返回-1
返回的錯誤資訊:
On failure, errno is set to one of the following values:
EACCES A message queue exists for key, but the calling process does not have permission to access the queue, and does not have the CAP_IPC_OWNER capability.
EEXIST A message queue exists for key and msgflg specified both IPC_CREAT and IPC_EXCL.
ENOENT No message queue exists for key and msgflg did not specify IPC_CREAT.
ENOMEM A message queue has to be created but the system does not have enough memory for the new data structure.
ENOSPC A message queue has to be created but the system limit for the maximum number of message queues (MSGMNI) would be exceeded.

3:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
引數msgp指向類似於下面的結構體,形式一樣,但是具體的值自己改,msgbuf這個名字一定要改:
struct msgbuf {
   long mtype;       /* message type, must be > 0 */
   char mtext[1];    /* message data ,長度可以自己改,不能超過系統規定的長度*/
};
msgid: 由msgget函式返回的訊息佇列標識碼
msgp:是一個指標,指標指向準備傳送的訊息,
msgsz:是msgp指向的訊息長度,這個長度不含儲存訊息型別的那個long int長整型
msgflg:控制著當前訊息佇列滿或到達系統上限時將要發生的事情,msgflg=IPC_NOWAIT表示佇列滿不等待,返回EAGAIN錯誤。

4:

#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: 由msgget函式返回的訊息佇列標識碼
msgp:是一個指標,指標指向準備接收的訊息,
msgsz:是msgp指向的訊息長度,這個長度不含儲存訊息型別的那個long int長整型
msgtype:它可以實現接收優先順序的簡單形式
msgflg:控制著佇列中沒有相應型別的訊息可供接收時將要發生的事
返回值:成功返回實際放到接收緩衝區裡去的字元個數,失敗返回-1

msgtype=0返回佇列第一條資訊
msgtype>0返回佇列第一條型別等於msgtype的訊息 
msgtype<0返回佇列第一條型別小於等於msgtype絕對值的訊息,並且是滿足條件的訊息型別最小的訊息

msgflg=IPC_NOWAIT,佇列沒有可讀訊息不等待,返回ENOMSG錯誤。
msgflg=MSG_NOERROR,訊息大小超過msgsz時被截斷 
msgtype>0且msgflg=MSG_EXCEPT,接收型別不等於msgtype的第一條訊息。

5:

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
用這個函式為訊息佇列產生一個key值;


例項1:

程式先建立一個訊息佇列,並向該訊息佇列中寫入訊息,訊息型別是自身的PID;然後睡眠3秒之後再從訊息佇列中按照訊息型別讀取訊息;
從執行結果可以看出,當寫入之後,訊息佇列的大小變為了12, 當將訊息讀走之後訊息佇列的大小變為了0;

/*************************************************************************
	> File Name: msg1.cpp
	> Author: 
	> Mail: 
	> Created Time: 2015年12月17日 星期四 20時25分44秒
 ************************************************************************/

#include <iostream>
#include <cstdlib>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
using namespace std;


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

/*
生成一個key值,ftok(首先要先建立一個檔案myMsgFile)
建立訊息佇列,msgget
生成訊息結構體物件並填充訊息型別-一般是PID和資料內容
將訊息寫到訊息佇列
睡眠3秒
從訊息佇列裡面讀出來
*/

int main()
{
    key_t key;
    key = ftok("./myMsgFile", 'k');
   
    int msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
    if(msgid == -1){
        if(errno == EEXIST){
            key = ftok("./myMsgFile", 'k');
            msgid = msgget(key, IPC_CREAT | 0666);
        }
        else{
            cout << "msgget error..." << endl;
            exit(-1);
        }
    }

    struct msg_buf msgbuf;
    int msg_type = getpid();
    msgbuf.mtype = msg_type;
    strcpy(msgbuf.mtext, "message data");
    
    int ret = msgsnd(msgid, &msgbuf, strlen(msgbuf.mtext), IPC_NOWAIT);
    if(ret == -1){
        cout << "msgsnd error..." << endl;
        exit(-1);
    }

    sleep(3);
    memset(&msgbuf, 0, sizeof(msgbuf));

    ret = msgrcv(msgid, &msgbuf, sizeof(msgbuf), msg_type, IPC_NOWAIT); 
    if(ret == -1){
        cout << "msgrcv error..." << endl;
        exit(-1);
    }
    
    cout << "receive the data is: " << msgbuf.mtext << endl;

    exit(0);
}





備註:ipcs檢視系統的共享記憶體,訊息佇列,訊號量    ipcrm -q msqid刪除訊息佇列

例項2:

這次是父子程式通過訊息佇列來傳遞資訊;
程式先建立一個訊息佇列,並向該訊息佇列中寫入訊息,訊息型別是自身的PID;然後又向該訊息佇列中寫入訊息,訊息型別是111;此時檢視訊息佇列已用了26位元組;
然後睡眠5秒之後建立子程式,子程式再從訊息佇列中獲得訊息型別位111的訊息並讀走;讀走了13位元組,還有一個訊息,剩下13位元組;

/*************************************************************************
	> File Name: msg1.cpp
	> Author: 
	> Mail: 
	> Created Time: 2015年12月17日 星期四 20時25分44秒
 ************************************************************************/

#include <iostream>
#include <cstdlib>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
using namespace std;

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

/*
生成一個key值,ftok(首先要先建立一個檔案myMsgFile)
建立訊息佇列,msgget
生成訊息結構體物件並填充訊息型別-一般是PID和資料內容
將訊息寫到訊息佇列
睡眠3秒
從訊息佇列裡面讀出來
*/

int main()
{
    key_t key;
    key = ftok("./myMsgFile", 'k');
   
    int msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
    if(msgid == -1){
        if(errno == EEXIST){
            key = ftok("./myMsgFile", 'k');
            msgid = msgget(key, IPC_CREAT | 0666);
        }
        else{
            cout << "msgget error..." << endl;
            exit(-1);
        }
    }

    struct msg_buf msgbuf;
    int msg_type = getpid();
    msgbuf.mtype = msg_type;
    strcpy(msgbuf.mtext, "message1 data");
    
    int ret = msgsnd(msgid, &msgbuf, strlen(msgbuf.mtext), IPC_NOWAIT);
    if(ret == -1){
        cout << "msgsnd error..." << endl;
        exit(-1);
    }

    memset(&msgbuf, 0, sizeof(msgbuf));
    msgbuf.mtype = 111;
    strcpy(msgbuf.mtext, "message2 data");
    ret = msgsnd(msgid, &msgbuf, strlen(msgbuf.mtext), IPC_NOWAIT);
    if(ret == -1){
        cout << "msgsnd error..." << endl;
        exit(-1);
    }


    sleep(5);
    pid_t pid = fork();
    if(pid < 0){
        cout << "fork error..." << endl;
        exit(-1);
    }
    else if (pid == 0){
        memset(&msgbuf, 0, sizeof(msgbuf));
        ret = msgrcv(msgid, &msgbuf, sizeof(msgbuf), 111, IPC_NOWAIT); //獲得第二條訊息
        if(ret == -1){
            cout << "msgrcv error..." << endl;
            exit(-1);
        }
        cout << "child process receive the data is: " << msgbuf.mtext << endl;
        cout << "child exit..." << endl;
        exit(0);
    }
    
    int child_status;
    wait(&child_status);
    
    cout << "parent exit..." << endl;
    exit(0);
}




相關文章