一、消息隊列
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 產生的 是私有的消息隊列。
寫個小程序測試一下這個函數:
/************************************************************************* > File Name: basic.c > Author: Simba > Mail: [email protected] > 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,如下:
/************************************************************************* > File Name: basic.c > Author: Simba > Mail: [email protected] > 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
/************************************************************************* > File Name: basic.c > Author: Simba > Mail: [email protected] > 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