消息隊列內核結構和msgget、msgctl 函數


一、消息隊列

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 的取值如下:



我們可以通過ipcrm -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  

 


注:ipcrm --刪除ipc對象

 

ipcrm -m|-q|-s shm_id
%ipcrm -m 105

例如,我們在以0x12345678為KEY創建了一個共享內存,可以直接使用ipcrm -M 0x12345678來刪除共享內存區域。


參考:

《TCP/IP詳解 卷一》

《UNP》



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM