文章目錄
- 基本介紹
- 程式設計接口
- 代碼執行個體
- 消息隊列的發送和接收
- 消息隊列中的消息對象的屬性控制
基本介紹
- 支援不同程序之間以消息(messages)的形式進行資料交換,消息能夠擁有自己的辨別,且核心使用連結清單方式進行消息管理。
-
程序之間的通信角色為:發送者和接受者
發送者:
a. 擷取消息隊列的ID(key或者msgid)
b. 将資料放入一個帶有辨別的消息結構體,發送到消息隊列
接受者:
a. 擷取消息隊列的ID
b. 指定辨別的消息從消息隊列中讀出,然後進一步後續處理
- 支援不同的程序标記不同的消息類型(1,2,3…),并由核心态維護對應消息類型的連結清單。
- 核心态的0号消息類型維護了一個連結清單,用來儲存按照時間順序加入的消息
程式設計接口
- 生成ipc對象的唯一辨別
key
的接口
a. 頭檔案
b. 函數聲明 <sys/ipc.h>
key_t ftok(const char *path, int id);
c. 參數描述
path
需指定一個已經存在的可通路的檔案
為使用者可自由指定的idid
-
建立或者打開一個消息隊列,并擷取system V 消息隊列中消息的身份辨別
a. 頭檔案
b. 函數聲明 <sys/types.h> <sys/ipc.h> <sys/msg.h>
int msgget (ket_t key, int msgflg)
c. 參數描述
key
為ipc對象的唯一辨別,生成的消息身份辨別與該參數相關
msgflg
當該函數沒有搜尋到系統中與key值對應的消息隊列,則msgflg會指定IPC_CREAT,建立一個隊列,并傳回消息辨別
d. 傳回值:成功傳回消息身份辨別,失敗傳回-1
-
發送消息到消息隊列
a. 頭檔案
b. 函數聲明 <sys/types.h> <sys/ipc.h> <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
c. 參數描述
msqid
消息辨別,類似于檔案描述符fd
msgp
消息内容指針
msgsz
消息大小
當出現消息隊列沒有足夠的可用空間時,可以通過設定msgflg為msgflg
IPC_NOWAIT
來讓發送函數不産生阻塞,傳回失敗
d. 傳回值 失敗傳回-1,以及對應失敗碼;成功則傳回0
-
從消息隊列中接收消息
a. 頭檔案
b. 函數聲明 <sys/types.h> <sys/ipc.h> <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
c. 參數描述
msqid
消息辨別,類似于檔案描述符fd
msgp
消息内容指針
msgsz
消息大小,同時也是目前接收的消息最大能夠接收的大小
當出現實際的消息内容大于設定的msgsz,可以通過msgflg
來将消息裁剪為MSG_NOERROR
msgsz
大小進行擷取,否則會傳回失敗。而消息仍然存在于消息隊列。
msgtyp
如果是0,則會讀取處于消息隊列的第一個消息;大于0,則會讀取對應type處于消息隊列中的第一個消息;如果小于0,則會讀取type絕對值或者小于絕對值的消息隊列的第一個消息。
d. 傳回值 失敗傳回-1,成功傳回對應消息的大小
-
控制消息隊列的各個操作
a. 頭檔案
b. 函數聲明 <sys/types.h> <sys/ipc.h> <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
c. 參數描述
msgqid
消息辨別
針對消息辨別的操作,合法的操作如下:cmd
-
擷取msgqid的消息對象的資訊,将各個屬性從核心拷貝到一個臨時的資料結構IPC_STAT
類型的buf;調用者需要對消息隊列有讀權限msgqid_ds
-
自己可以通過臨時的IPC_SET
來設定核心中消息的對應msgqid_ds
的屬性msgqid_ds
-
立即移除消息隊列;目前調用者需要擁有 消息隊列的所有者權限,或者高于所有者的權限(root)IPC_RMID
-
傳回消息隊列的參數限制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