摘錄自 《Linux網絡編程》宋敬彬,孫海濱著
Linux 消息隊列
消息隊列是內核地址空間中的內部鏈表,通過Linux內核在各個進程之間傳遞內容。消息順序地發送到消息隊列中,並以幾種不同的方式從隊列中獲取,每個消息隊列可以用IPC標識符唯一的進行標識。內核中的消息隊列是通過IPC的標識符來區別的,不同的消息隊列之間是相對獨立的。每個消息隊列中的消息,又構成一個獨立的鏈表。
-
消息緩衝區結構
常用的結構是msgbuf結構。程序員可以以這個結構爲模板定義自己的消息結構。在頭文件<linux/msg.h>中,它的定義如下
struct msgbuf{ long mtype; char mtext[1]; };
在結構msgbuf中有兩個成員:
- mtype : 消息類型,以正數表示。區分消息,可以自定義
- mtext : 消息數據
消息數據的類型爲char, 長度爲1。但在構建自己的消息結構時,這個域不一定要爲char或者長度爲1.可以根據實際情況自行設定,這個域可以存放任意形式的數據,示例如下
struct msgmbuf{ long mtype; char mtext[10]; long length; };
上面定義的消息結構與系統模板定義的不一致,但mtype一致。消息在通過內核在進程間收發時,內核不對mtext域進行轉換,任意的消息都可以發送。具體的轉換工作是在應用程序之間進行的。但是消息的大小有一個內部的限制。在Linux中(如Ubuntu 16),它在/usr/include/linux/msg.h中的定義如下
#define MSGMAX 8192 /* <= INT_MAX */ /* max size of message (bytes) */
消息總大小不能超過8192個字節,其中包含mtype成員的大小,它的長度爲當前機器long類型的長度。
-
結構msgid_ds
內核msgid_ds結構-IPC對象分爲3類,每一類都有一個內部數據結構,該數據結構是由內核維護的。對於消息隊列而言,它的內部數據結構爲msgid_ds。對於系統上創建的每個消息隊列,內核均爲其創建,存儲和維護該結構的一個實例。該結構在Linux/msg.h中定義如下
/* Obsolete, used only for backwards compatibility and libc5 compiles */ struct msqid_ds { struct ipc_perm msg_perm; struct msg *msg_first; /* first message on queue,unused */ struct msg *msg_last; /* last message in queue,unused */ __kernel_time_t msg_stime; /* last msgsnd time */ __kernel_time_t msg_rtime; /* last msgrcv time */ __kernel_time_t msg_ctime; /* last change time */ unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */ unsigned long msg_lqbytes; /* ditto */ unsigned short msg_cbytes; /* current number of bytes on queue */ unsigned short msg_qnum; /* number of messages in queue */ unsigned short msg_qbytes; /* max number of bytes on queue */ __kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */ __kernel_ipc_pid_t msg_lrpid; /* last receive pid */ };
-
結構ipc_perm
內核將IPC對象的許可權限信息存放在ipc_perm類型的結構中。例如 mshid_ds結構體的成員msg_perm就是ipc_perm類型的,它的定義在 linux/ipc.h 中,如下所示
/* Obsolete, used only for backwards compatibility and libc5 compiles */ struct ipc_perm { __kernel_key_t key; //函數msgget()使用的鍵值 __kernel_uid_t uid; //用戶UID __kernel_gid_t gid; //用戶GID __kernel_uid_t cuid; //建立者的UID __kernel_gid_t cgid; //建立者的GID __kernel_mode_t mode; //權限 unsigned short seq; //序列號 };
消息隊列程序示例
/*******************************************************************************************
* Filename : msg.c
* Description :
* Created : 2019-01-01 17:45:30
*******************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <errno.h>
#include <time.h>
#define Equit() return -1
//輸出消息屬性
void msg_show_attr(int msg_id, struct msqid_ds *msg_info){
int ret = -1;
sleep(1);
ret = msgctl(msg_id, IPC_STAT, msg_info);
if(-1 == ret){
printf("獲取消息失敗\n");
return;
}
printf("\n");
printf("消息中當前字節數: %d\n", msg_info->__msg_cbytes);
printf("隊列中消息數: %d\n", msg_info->msg_qnum);
printf("隊列中最大字節數: %d\n", msg_info->msg_qbytes);
printf("最後發送消息的進程PID: %d\n", msg_info->msg_lspid);
printf("最後接收消息的進程PID: %d\n", msg_info->msg_lrpid);
printf("最後發送消息的時間: %s",ctime(&(msg_info->msg_stime)));
printf("最後接收消息的時間: %s", ctime(&(msg_info->msg_rtime)));
printf("最後變化時間: %s", ctime(&(msg_info->msg_ctime)));
printf("消息UID: %d\n", msg_info->msg_perm.uid); //permission
printf("消息GID: %d\n", msg_info->msg_perm.gid);
}
int main(void){
int ret = -1;
int msg_flags, msg_id;
key_t key;
//消息的緩衝區結構
struct msgmbuf{
long mtype;
char mtext[13];
};
struct msqid_ds msg_info;
struct msgmbuf msg_buf;
int msg_sflags, msg_rflags; //發送和接收標誌
char *msgpath = "/tmp/msg"; //消息key的產生路徑
ret = mkdir(msgpath, S_IRWXU); //創建文件夾
if(-1 == ret){
if(errno != EEXIST){
printf("error: %s\n", strerror(errno));
return -1;
}
}
key = ftok(msgpath, 'b'); //產生key
if(-1 == key){
printf("failed to create key\n");
printf("error: %s\n", strerror(errno));
return -1;
}else{
printf("key created\n");
}
//msg_flags = IPC_CREAT|IPC_EXCL; //消息標誌位
msg_flags = IPC_CREAT; //消息標誌位
msg_id = msgget(key, msg_flags|00666); //建立消息
if(-1 == msg_id){
printf("消息建立失敗, 原因: %s\n",strerror(errno));
Equit();
}
// msg_show_attr(msg_id, &msg_info); //輸出消息屬性
msg_sflags = IPC_NOWAIT; //非阻塞
msg_buf.mtype = 10;
memcpy(msg_buf.mtext, "測試消息", sizeof("測試消息"));
printf("%s\n", msg_buf.mtext);
ret = msgsnd(msg_id, &msg_buf, sizeof("測試消息"), msg_sflags);//發送消息
if(-1 == ret){
printf("發送消息失敗, error: %s\n",strerror(errno));
}
msg_show_attr(msg_id, &msg_info);
msg_rflags = IPC_NOWAIT|MSG_NOERROR;
ret = msgrcv(msg_id, &msg_buf, 10, 10, msg_rflags);
if(-1 == ret){
printf("接收消息失敗\n");
}
else{
printf("接收消息成功, 長度: %d\n",ret);
}
msg_show_attr(msg_id, &msg_info);
msg_info.msg_perm.uid = 8;
msg_info.msg_perm.gid = 8;
msg_info.msg_qbytes = 12345;
ret = msgctl(msg_id, IPC_SET, &msg_info); //設置消息屬性
if(-1 == ret){
printf("設置消息屬性失敗\n");
return 0;
}
msg_show_attr(msg_id, &msg_info);
ret = msgctl(msg_id, IPC_RMID, NULL); //刪除消息隊列
if(-1 == ret){
printf("刪除消息失敗\n");
return 0;
}
return 0;
}