linux網路編程之POSIX 訊息佇列 和 系列函數

來源:互聯網
上載者:User

一、在前面介紹了system v 訊息佇列的相關知識,現在來稍微看看posix 訊息佇列。

其實訊息佇列就是一個可以讓進程間交換資料的場所,而兩個標準的訊息佇列最大的不同可能只是api 函數的不同,如system v 的系列函數是msgxxx,而posix 是mq_xxx。posix 訊息佇列也有一些對訊息長度等的限制,man 7 mq_overview:

simba@ubuntu:~/Documents/code/linux_programming/UNP/posix$ cat /proc/sys/fs/mqueue/msg_max 
10
simba@ubuntu:~/Documents/code/linux_programming/UNP/posix$ cat /proc/sys/fs/mqueue/msgsize_max 
8192
simba@ubuntu:~/Documents/code/linux_programming/UNP/posix$ cat /proc/sys/fs/mqueue/queues_max 
256

即一個訊息佇列最多能有10條訊息,每條訊息的最大長度為8192位元組,一個系統最多能有256個訊息佇列。

還有一點是,在Linux上,posix 訊息佇列是以虛擬檔案系統實現的,必須將其掛載到某個目錄才能看見,如


           # mkdir /dev/mqueue
           # mount -t mqueue none /dev/mqueue

通過cat 命令查看訊息佇列的狀態,假設mymq 是建立的一條訊息佇列的名字
           $ cat /dev/mqueue/mymq
           QSIZE:129     NOTIFY:2    SIGNO:0    NOTIFY_PID:8260

QSIZE:訊息佇列所有訊息的資料長度

NOTIFY_PID:某個進程使用mq_notify 註冊了訊息到達非同步通知事件,即此進程的pid

NOTIFY:通知方式: 0 is SIGEV_SIGNAL; 1 is SIGEV_NONE; and 2 is SIGEV_THREAD.

SIGNO:當以訊號方式通知的時候,表示訊號的編號.


二、系列函數,編譯時間候加上 -lrt 選項,即串連librt 庫 (即時庫)

      #include <fcntl.h>           /* For O_* constants */
      #include <sys/stat.h>        /* For mode constants */
      #include <mqueue.h>

功能:用來建立和訪問一個訊息佇列
原型
mqd_t mq_open(const char *name, int oflag);
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
參數
name: 某個訊息佇列的名字,必須以/打頭,並且後續不能有其它/ ,形如/somename長度不能超過NAME_MAX(255)
oflag:與open函數類似,可以是O_RDONLY、O_WRONLY、O_RDWR,還可以按位或上O_CREAT、O_EXCL、O_NONBLOCK;
mode:如果oflag指定了O_CREAT,需要設定mode。
傳回值:成功返回訊息佇列檔案描述符;失敗返回-1


功能:關閉訊息佇列
原型
mqd_t mq_close(mqd_t mqdes);
參數
mqdes : 訊息佇列描述符
傳回值:成功返回0;失敗返回-1


功能:刪除訊息佇列
原型
mqd_t mq_unlink(const char *name);
參數
name: 訊息佇列的名字
傳回值:成功返回0;失敗返回-1


功能:擷取/設定訊息佇列屬性
原型
mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr);
mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr);
傳回值:成功返回0;失敗返回-1

           struct mq_attr {
               long mq_flags;       /* Flags: 0 or O_NONBLOCK */
               long mq_maxmsg;      /* Max. # of messages on queue */ 
               long mq_msgsize;     /* Max. message size (bytes) */
               long mq_curmsgs;     /* # of messages currently in queue */
           };

mq_flags 是標誌;mq_maxmsg 即一個訊息佇列訊息個數上限;mq_msgsize即一條訊息的資料上限;mq_curmsgs即當前訊息個數


功能:發送訊息
原型
mqd_t mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);
參數
mqdes:訊息佇列描述符
msg_ptr:指向訊息的指標
msg_len:訊息長度
msg_prio:訊息優先順序
傳回值:成功返回0;失敗返回-1


功能:接收訊息
原型
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
參數
mqdes:訊息佇列描述符
msg_ptr:返回接收到的訊息
msg_len:訊息長度,一般需要指定為msgsize_max
msg_prio:返回接收到的訊息優先順序
傳回值:成功返回接收到的訊息位元組數;失敗返回-1
注意:返回指定訊息佇列中最高優先順序的最早訊息,優先順序最低為0


功能:建立或者刪除訊息到達通知事件
原型
mqd_t mq_notify(mqd_t mqdes, const struct sigevent *notification);
參數
mqdes:訊息佇列描述符
notification:
非空表示當訊息到達且訊息佇列先前為空白,那麼將得到通知;
NULL表示撤消登入的通知
傳回值:成功返回0;失敗返回-1
通知方式:
產生一個訊號
建立一個線程執行一個指定的函數

union sigval {          /* Data passed with notification */
int     sival_int;/* Integer value */
void   *sival_ptr;/* Pointer value */
};

struct sigevent {
int          
sigev_notify; /* Notification method */
int          
sigev_signo; /* Notification signal */
union sigval
sigev_value; /* Data passed with notification */
void (*sigev_notify_function) (union sigval);
/* Function for thread notification */
void *sigev_notify_attributes;
/* Thread function attributes */
};

sigev_notify 的取值有3個:

SIGEV_NONE:訊息到達不會發出通知

SIGEV_SIGNAL:以訊號方式發送通知,當設定此選項時,sigev_signo 設定訊號的編號,且只有當訊號為即時訊號時才可以通過sigev_value順帶資料,參加這裡。

SIGEV_THREAD:以線程方式通知,當設定此選項時,sigev_notify_function 即一個函數指標,sigev_notify_attributes 即線程的屬性


mq_notify 函數注意點:

1、任何時刻只能有一個進程可以被註冊為接收某個給定隊列的通知
2、當有一個訊息到達某個先前為空白的隊列,而且已有一個進程被註冊為接收該隊列的通知時,只有在沒有任何線程阻塞在該隊列的mq_receive調用的前提下,通知才會發出。
3、當通知被發送給它的註冊進程時,其註冊被撤消。進程必須再次調用mq_notify以重新註冊(如果需要的話),重新註冊要放在從訊息佇列讀出訊息之前而不是之後。


下面寫兩個小程式測試一下:

mq_send.c

 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
40
41
42
43
44
45
46
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<mqueue.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<string.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

typedef struct stu
{
    char name[32];
    int age;
} STU;

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s <prio>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    mqd_t mqid;
    mqid = mq_open("/abc", O_WRONLY);
    if (mqid == (mqd_t) - 1)
        ERR_EXIT("mq_open");
    printf("mq_open succ\n");

    STU stu;
    strcpy(stu.name, "test");
    stu.age = 20;
    unsigned int prio = atoi(argv[1]);
    mq_send(mqid, (const char *)&stu, sizeof(stu), prio);
    mq_close(mqid);

    return 0;
}

mq_notify.c

 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<mqueue.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<string.h>
#include<signal.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

typedef struct stu
{
    char name[32];
    int age;
} STU;

size_t size;

mqd_t mqid;

struct sigevent sigev;

void handle(int sig)
{
    mq_notify(mqid, &sigev);
    STU stu;
    unsigned int prio;
    mq_receive(mqid, (char *)&stu, size, &prio);
    printf("name=%s, age=%d, prio=%d\n", stu.name, stu.age, prio);

}

int main(int argc, char *argv[])
{
    mqid = mq_open("/abc", O_RDONLY);
    if (mqid == (mqd_t) - 1)
        ERR_EXIT("mq_open");
    printf("mq_open succ\n");

    struct mq_attr attr;
    mq_getattr(mqid, &attr);
    size = attr.mq_msgsize;

    signal(SIGUSR1, handle);

    sigev.sigev_notify = SIGEV_SIGNAL;
    sigev.sigev_signo = SIGUSR1;

    mq_notify(mqid, &sigev);

    for (; ;)
        pause();

    mq_close(mqid);

    return 0;
}

假設之前已經用mq_open 建立了一個訊息佇列並mount 到/dev/mqueue 上,可以查看狀態:

simba@ubuntu:/dev/mqueue$ cat /dev/mqueue/abc 
QSIZE:0          NOTIFY:0     SIGNO:0     NOTIFY_PID:0  

接著運行./mq_notify 再查看狀態:

simba@ubuntu:/dev/mqueue$ cat /dev/mqueue/abc 
QSIZE:0          NOTIFY:0     SIGNO:10    NOTIFY_PID:3572 

準備通知的訊號是10號,即SIGUSR1,等待的進程pid即./mq_notify ,此時./mq_notify 進程是阻塞的,正在等待其他進程往訊息佇列寫入訊息,現在運行./mq_send


simba@ubuntu:~/Documents/code/linux_programming/UNP/posix$ ./mq_send 
Usage: ./mq_send <prio>
simba@ubuntu:~/Documents/code/linux_programming/UNP/posix$ ./mq_send 1
mq_open succ
simba@ubuntu:~/Documents/code/linux_programming/UNP/posix$ ./mq_send 1
mq_open succ
simba@ubuntu:~/Documents/code/linux_programming/UNP/posix$ 

回頭看./mq_notify 的輸出:

simba@ubuntu:~/Documents/code/linux_programming/UNP/posix$ ./mq_notify 
mq_open succ
name=test, age=20, prio=1
name=test, age=20, prio=1

...........................................

即./mq_send 發送的兩條訊息都接收到了,需要注意的是雖然通知是一次性的,但我們在訊息處理函數再次註冊了通知,故能多次接收到通知,但通知只發生在訊息佇列由空到不空的時候,如果先運行./mq_send 先往訊息佇列發送了n條訊息,那麼執行./mq_notify 是不會接收到通知的,一直阻塞著。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.