一、訊息佇列
1、訊息佇列提供了一個從一個進程向另外一個進程發送一塊資料的方法
2、每個資料區塊都被認為是有一個類型,接收者進程接收的資料區塊可以有不同的類型值
3、訊息佇列與管道不同的是,訊息佇列是基於訊息的,而管道是基於位元組流的,且訊息佇列的讀取不一定是先入先出。
4、訊息佇列也有管道一樣的不足,就是每個訊息的最大長度是有上限的(MSGMAX),每個訊息佇列的總的位元組數是有上限的(MSGMNB),系統上訊息佇列的總數也有一個上限(MSGMNI),這三個參數都可以查看:
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ cat /proc/sys/kernel/msgmax
8192
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ cat /proc/sys/kernel/msgmnb
16384
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ cat /proc/sys/kernel/msgmni
1711
二、IPC對象資料結構
核心為每個IPC對象維護一個資料結構
struct ipc_perm {
key_t __key; /* Key supplied to xxxget(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 */
};
訊息佇列,共用記憶體和訊號量都有這樣一個共同的資料結構。
三、訊息佇列結構
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) */
};
可以看到第一個條目就是IPC結構體,即是共有的,後面的都是訊息佇列所私人的成員。
四、訊息佇列在核心中的表示
訊息佇列是用鏈表實現的,這裡需要提出的是MSGMAX指的是一條訊息的純資料大小的上限,是一個訊息佇列,則其純資料總和不能超過MSGMNB,像這樣一條訊息佇列,系統含有的總數不能超過MSGMNI 個。
五、msgget 和 msgctl 函數
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
(1)int msgget(key_t key, int msgflg);
功能:用來建立和訪問一個訊息佇列
參數
key: 某個訊息佇列的名字
msgflg:由九個許可權標誌構成,它們的用法和建立檔案時使用的mode模式標誌是一樣的
傳回值:成功返回一個非負整數,即該訊息佇列的標識碼;失敗返回-1
建立流程如所示:
注意,IPC_PRIVATE 不是一個msgflg 而是一個key_t 類型,如果指定key = IPC_PRIVATE,則無論某個 key_value 這個訊息佇列是否存在,都會再建立一個key_value 訊息佇列,但他們的標識碼msqid是不一樣的,且指定IPC_PRIVATE 產生的是私人的訊息佇列。
寫個小程式測試一下這個函數:
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
|
/************************************************************************* > File Name: basic.c > Author: Simba > Mail: dameng34@163.com > Created Time: Tue 12 Mar 2013 06:54:20 PM CST ************************************************************************/#include<stdio.h> #include<stdlib.h> #include<sys/ipc.h> #include<sys/msg.h> #include<sys/types.h> #include<unistd.h> #include<errno.h> #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(void) { int msgid; msgid = msgget(1234, 0666 | IPC_CREAT); if (msgid == -1) ERR_EXIT("msgget"); printf("msgget success\n"); msgid = msgget(1234, 0); printf("msgid=%d\n", msgid); return 0; } |
程式先建立一個訊息佇列,名字為1234,接著開啟這個訊息佇列,當flags = 0 表示按原來許可權開啟。
輸出如下:
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./msgget
msgget success
msgid=0
我們可以使用命令ipcs -q 查看:
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x000004d2 0 simba 666 0 0
可以看到0x4d2 也就是1234,msqid 為0。
(2)int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:訊息佇列的控制函數
參數
msqid: 由msgget函數返回的訊息佇列標識碼
cmd:是將要採取的動作,(有三個可取值)
傳回值:成功返回0,失敗返回-1
cmd 的取值如下:
我們可以通過ipsrm -q 刪除一條訊息佇列,也可以通過msgctl 函數刪除,此時設定cmd 為 IPC_RMID,如下:
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
|
/************************************************************************* > File Name: basic.c > Author: Simba > Mail: dameng34@163.com > Created Time: Tue 12 Mar 2013 06:54:20 PM CST ************************************************************************/#include<stdio.h> #include<stdlib.h> #include<sys/ipc.h> #include<sys/msg.h> #include<sys/types.h> #include<unistd.h> #include<errno.h> #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(void) { int msgid; msgid = msgget(1234, 0); if (msgid == -1) ERR_EXIT("msgget"); printf("msgget success\n"); printf("msgid=%d\n", msgid); msgctl(msgid, IPC_RMID, NULL); return 0; } |
如果我們想更改訊息佇列的一些參數,如許可權等,可以通過msgctl 函數,cmd 取值為IPC_SET
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
|
/************************************************************************* > File Name: basic.c > Author: Simba > Mail: dameng34@163.com > Created Time: Tue 12 Mar 2013 06:54:20 PM CST ************************************************************************/#include<stdio.h> #include<stdlib.h> #include<sys/ipc.h> #include<sys/msg.h> #include<sys/types.h> #include<unistd.h> #include<errno.h> #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(void) { int msgid; msgid = msgget(1234, 0); if (msgid == -1) ERR_EXIT("msgget"); printf("msgget success\n"); printf("msgid=%d\n", msgid); struct msqid_ds buf; msgctl(msgid, IPC_STAT, &buf); printf("permission : %o\n", buf.msg_perm.mode); sscanf("600", "%o", (unsigned int *)&buf.msg_perm.mode); msgctl(msgid, IPC_SET, &buf); return 0; } |
程式先通過IPC_STAT 擷取許可權,再通過IPC_SET 重新設定,輸出如下:
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./msgset
msgget success
msgid=32768
permission : 666
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x000004d2 32768 simba 600 0 0
參考:
《TCP/IP詳解 卷一》
《UNP》