程式間通訊--訊息佇列

阿明明發表於2021-04-07

訊息佇列(message queue):是程式間通訊的一種常用的方式,可以傳遞多種型別的資料流,可以實現非同步傳輸。

訊息佇列是兩個或者多個應用程式約定好的一種互動方式,體現在彼此知曉共同的訊息佇列 ID, 該收取哪種型別的訊息。

下面開始介紹應用的過程,附著的程式碼是在某公司開發feature時候加入的

1.  建立IPC key

  key_t ftok(const char* pathname, int proj_id)

  • pathname 是一個檔案的路徑,需要注意的是在實際程式間通訊時,需要保證pathname一直存在且不會被修改, proj_id是一個識別符號,常用一個字元表示
  • 兩個程式想要實現訊息佇列通訊,必須有相同的key,因此pathname,proj_id是相同的
  • 建立成功返回key_t值,失敗為-1,原因可檢視errno
  • 程式碼如下:
    #define NEIGBOR_INFO_GET        "/tmp"
    #define NEIGBOR_INFO_GET_PROJ    'x'
    ...
    
    key = ftok(NEIGBOR_INFO_GET, NEIGBOR_INFO_GET_PROJ);
    if(key == -1) {
        printf("[%s][%d] creat key error!\n", __FUNCTION__, __LINE__);
        return res;
    }

2.  獲取訊息佇列 ID

  int msgget(key_t key, int msgflg)

  • key是ftok建立所得,flag是對訊息佇列的操作,常用IPC_CREAT,即如果訊息佇列已存在,則返回訊息佇列 ID,否則建立新的訊息佇列, 返回 ID
  • 訊息佇列在建立的時候是可以設定讀寫許可權的 | 0666即 設定為可讀可寫
  • flag如果指定為IPC_CREAT | IPC_EXCL ,在訊息佇列已存在下,會返回-1, errno為EEXIST
  • 返回值為訊息佇列的 ID, 否則為-1, 原因可檢視errno
  • 程式碼如下:
    ...
    int
    gs_getneigborinfo_qid; ... gs_getneigborinfo_qid = msgget(key, IPC_CREAT | 0666); if(gs_getneigborinfo_qid == -1) {   printf("[%s][%d], create message deque error!\n", __FUNCTION__, __LINE__); return res; }

     

3.  訊息型別定義 msgbuf

  msgbuf定義有一定的規範,常用如下:

struct msgbuf {
    long type;
    char mtext[1];
};

  每一個msg中必須含有一個 long 型別的type,用於區分訊息型別,後面的資料是可以自定義的,如下:

...
#define CHASSISID_MAX_SIZE  255
#define PORTID_MAX_SIZE     255
...

struct neighborInfo {
    long type;
    char chassis_id[CHASSISID_MAX_SIZE + 1];
    char port_id[PORTID_MAX_SIZE + 1];
};

 

4.  傳送訊息 msgsnd

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

  • msgid:指定的訊息佇列 ID
  • msgp:待傳送的資料buff
  • msgsz:待傳送資料大小,常用sizeof(struct msgbuf) - sizeof(long)
  • msgflg:常為0:表示阻塞傳送,ICP_NOWAIT:非阻塞傳送
  • 成功返回0,否則為-1, 錯誤碼為errno
  • 程式碼如下:
    ...
    #define RESPONSE_INFO_TYPE 1
    #define REQUEST_INFO_TYPE 2
    struct neighborInfo temp;
    memset(&temp, 0, sizeof(temp));
    int rs = -1; 
    temp.type = REQUEST_INFO_TYPE;
    
    ...
    
    rs = msgsnd(gs_getneigborinfo_qid, &temp, sizeof(temp) - sizeof(long), IPC_NOWAIT);
    if(rs == -1) {
      printf("[%s][%d] msg send error!\n", __FUNCTION__, __LINE__);
       // pthread_mutex_unlock(&gs_pthread_lock);
       return rs;
    }

     

5. 接收訊息 msgrcv

  ssize_t msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg)

  • msgtyp:需要接收的訊息型別,如果為0, 則讀取訊息佇列的第一條訊息,不管其訊息型別
  • 讀取成功返回讀取的訊息長度,否則為-1,錯誤碼檢視errno
  • 程式碼如下:
    ...
    //
    need to recv info memset(&temp, 0, sizeof(temp)); int count = 200; do { rs= msgrcv(gs_getneigborinfo_qid, &temp, sizeof(temp), RESPONSE_INFO_TYPE, IPC_NOWAIT); count--; if(rs == -1) { usleep(1000); // wait 1ms to rerecive data } } while(rs == -1 && count > 0 && errno == ENOMSG);
    ...

     

6. 訊息佇列控制 msgctl

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

  • msgid:訊息佇列 ID
  • cmd:控制命令 IPC_STAT::將訊息佇列資訊存放到buf中,IPC_RMID:刪除訊息佇列
  • buf: 訊息佇列資訊存放
    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 */
               };

     

相關文章