天天看點

linux程序間通信:system V消息隊列

文章目錄

  • ​​基本介紹​​
  • ​​程式設計接口​​
  • ​​代碼執行個體​​
  • ​​消息隊列的發送和接收​​
  • ​​消息隊列中的消息對象的屬性控制​​

基本介紹

  1. 支援不同程序之間以消息(messages)的形式進行資料交換,消息能夠擁有自己的辨別,且核心使用連結清單方式進行消息管理。
  2. 程序之間的通信角色為:發送者和接受者

    發送者:

    a. 擷取消息隊列的ID(key或者msgid)

    b. 将資料放入一個帶有辨別的消息結構體,發送到消息隊列

    接受者:

    a. 擷取消息隊列的ID

    b. 指定辨別的消息從消息隊列中讀出,然後進一步後續處理

  3. 支援不同的程序标記不同的消息類型(1,2,3…),并由核心态維護對應消息類型的連結清單。
  4. 核心态的0号消息類型維護了一個連結清單,用來儲存按照時間順序加入的消息

程式設計接口

  1. 生成ipc對象的唯一辨別​

    ​key​

    ​的接口

    a. 頭檔案 ​

    ​<sys/ipc.h>​

    ​ b. 函數聲明 ​

    ​key_t ftok(const char *path, int id);​

    ​ c. 參數描述

    ​path​

    ​需指定一個已經存在的可通路的檔案

    ​id​

    ​為使用者可自由指定的id
  2. 建立或者打開一個消息隊列,并擷取system V 消息隊列中消息的身份辨別

    a. 頭檔案 ​

    ​<sys/types.h> <sys/ipc.h> <sys/msg.h>​

    ​ b. 函數聲明 ​

    ​int msgget (ket_t key, int msgflg)​

    ​ c. 參數描述

    ​key​

    ​ 為ipc對象的唯一辨別,生成的消息身份辨別與該參數相關

    ​msgflg​

    ​當該函數沒有搜尋到系統中與key值對應的消息隊列,則msgflg會指定IPC_CREAT,建立一個隊列,并傳回消息辨別

    d. 傳回值:成功傳回消息身份辨別,失敗傳回-1

  3. 發送消息到消息隊列

    a. 頭檔案 ​

    ​<sys/types.h> <sys/ipc.h> <sys/msg.h>​

    ​ b. 函數聲明 ​

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

    ​ c. 參數描述

    ​msqid​

    ​ 消息辨別,類似于檔案描述符fd

    ​msgp​

    ​ 消息内容指針

    ​msgsz​

    ​ 消息大小

    ​msgflg​

    ​ 當出現消息隊列沒有足夠的可用空間時,可以通過設定msgflg為​

    ​IPC_NOWAIT​

    ​來讓發送函數不産生阻塞,傳回失敗

    d. 傳回值 失敗傳回-1,以及對應失敗碼;成功則傳回0

  4. 從消息隊列中接收消息

    a. 頭檔案 ​

    ​<sys/types.h> <sys/ipc.h> <sys/msg.h>​

    ​ b. 函數聲明 ​

    ​ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);​

    ​ c. 參數描述

    ​msqid​

    ​ 消息辨別,類似于檔案描述符fd

    ​msgp​

    ​ 消息内容指針

    ​msgsz​

    ​ 消息大小,同時也是目前接收的消息最大能夠接收的大小

    ​msgflg​

    ​ 當出現實際的消息内容大于設定的msgsz,可以通過​

    ​MSG_NOERROR​

    ​來将消息裁剪為​

    ​msgsz​

    ​大小進行擷取,否則會傳回失敗。而消息仍然存在于消息隊列。

    ​msgtyp​

    ​ 如果是0,則會讀取處于消息隊列的第一個消息;大于0,則會讀取對應type處于消息隊列中的第一個消息;如果小于0,則會讀取type絕對值或者小于絕對值的消息隊列的第一個消息。

    d. 傳回值 失敗傳回-1,成功傳回對應消息的大小

  5. 控制消息隊列的各個操作

    a. 頭檔案 ​

    ​<sys/types.h> <sys/ipc.h> <sys/msg.h>​

    ​ b. 函數聲明 ​

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

    ​ c. 參數描述

    ​msgqid​

    ​ 消息辨別

    ​cmd​

    ​ 針對消息辨別的操作,合法的操作如下:
  • ​IPC_STAT​

    ​​ 擷取msgqid的消息對象的資訊,将各個屬性從核心拷貝到一個臨時的資料結構​

    ​msgqid_ds​

    ​類型的buf;調用者需要對消息隊列有讀權限
  • ​IPC_SET​

    ​​ 自己可以通過臨時的​

    ​msgqid_ds​

    ​​來設定核心中消息的對應​

    ​msgqid_ds​

    ​的屬性
  • ​IPC_RMID​

    ​ 立即移除消息隊列;目前調用者需要擁有 消息隊列的所有者權限,或者高于所有者的權限(root)
  • ​IPC_INFO​

    ​ 傳回消息隊列的參數限制

其他辨別可以通過​

​man msgctl​

​來檢視

代碼執行個體

消息隊列的發送和接收

發送端​

​msg_snd.c​

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define MSG_TYPE1 1
#define MSG_TYPE2 2 

struct msgbuf
{
        long mtype;
        char mtext[100];
};

int main()
{
    //當多使用者的時候通過指定檔案以及設定id來擷取唯一的key辨別
        //key_t key = ftok(".",100);
         key_t key = 12345; //個人使用的時候可以直接指定key
    
    //建立msg_qid的對象
        int msg_qid = msgget(key, IPC_CREAT | 0666);
        struct msgbuf msg;
        memset(&msg, 0 , sizeof(msg));
    
    //初始化消息類型以及消息内容
        msg.mtype = MSG_TYPE2;
        strncpy(msg.mtext, "hello world" , 80);

    //發送消息到消息辨別的msg_qid IPC 對象中
        if( -1 == msgsnd(msg_qid,(void *)&msg,strlen(msg.mtext),0)) {
                printf("send msg failed\n");
                _exit(-1);
        }

        return 0;
}      

編譯運作:

​​

​gcc msg_snd.c -o msg_snd​

運作前檢視系統消息隊列

​​

​ipcs -q​

[root@node1 ~]# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x000004d2 0          root       666        0            0      

運作後,可以看到我們發送了消息隊列的各個屬性資訊。關于key值,它為我們設定的12345的16進制數值

[root@node1 ~]# ./msg_snd 
[root@node1 ~]# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x000004d2 0          root       666        0            0           
0x00003039 65538      root       666        11           1      

接收端​

​msg_rcv.c​

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define MSG_TYPE1 1
#define MSG_TYPE2 2 

struct msgbuf
{
        long mtype;
        char mtext[100];
};

int main()
{
        key_t key = 12345;
        int msg_qid = msgget(key, IPC_CREAT | 0666);
        struct msgbuf msg;
        memset(&msg, 0 , sizeof(msg));

        if (-1 == msgrcv(msg_qid,(void *)&msg,sizeof(msg.mtext),MSG_TYPE2,0)) {
                printf("receive msg failed\n");
                _exit(-1);
        }
        printf("%s\n",msg.mtext);
        
        //當完成接收之後從消息隊列中删除目前消息
    //msgctl(msg_id,IPC_RMID,NULL);
        return 0;
}      

編譯運作,可以看到已經接手到了我們之前發送的内容:

[root@node1 ~]# gcc msg_rcv.c -o msg_rcv
[root@node1 ~]# ./msg_rcv
hello world      

檢視消息隊列情況,消息隊列中的資料已經被接收,是以在used-bytes和messages中看不到消息内容了,但是沒有删除該消息隊列,是以消息辨別仍然存在。我們可以在上述代碼中加入​

​msgctl​

​:

[root@node1 ~]# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x000004d2 0          root       666        0            0                    
0x00003039 65538      root       666        0            0      

消息隊列中的消息對象的屬性控制

控制代碼​

​msg_ctl.c​

​​

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

​​ 其中主要控制的是消息隊列一個資料結構,可以通過​

​man msgctl​

​檢視 ​

​msqid_ds​

​結構體

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) */
           };      
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main()
{
        key_t key = 12345;
        int msg_id = msgget(key,IPC_CREAT|0666);

        struct msqid_ds info;
        
        //第一次先從已存在的12345的消息中擷取隊列狀态
        if (-1 == msgctl(msg_id,IPC_STAT,&info)) {
                printf("control msg failed\n");
                _exit(-1);
        }
  
    //列印各個狀态資訊
        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("mode:%o3o,cbytes:%lu,qnum:%lu,qbytes:%lu\n",\
                info.msg_perm.mode & 0777,info.__msg_cbytes,info.msg_qnum,info.msg_qbytes);

    //嘗試設定消息隊列允許的最大位元組内容
        info.msg_qbytes = 16000;

    //通過cmd為IPS_SET的标記進行設定
        if (-1 == msgctl(msg_id, IPC_SET, &info)) {
                printf("ipc_set failed\n");
                _exit(-1);
        }

        if (-1 == msgctl(msg_id, IPC_STAT, &info)) {
                printf("ipc_stat failed\n");
                _exit(-1);
        }


        printf("mode:%o3o,cbytes:%lu,qnum:%lu,qbytes:%lu\n",\
                info.msg_perm.mode & 0777,info.__msg_cbytes,info.msg_qnum,info.msg_qbytes);
        return 0;
}      
[root@node1 ~]# ./msg_ctl 
uid :0,gid:0,cuid:0,cgid:0
mode:6663o,cbytes:11,qnum:1,qbytes:16000
mode:6663o,cbytes:11,qnum:1,qbytes:16000