消息隊列 posix API
消息隊列(也叫做封包隊列)能夠克服早期unix通信機制的一些缺點。信号這種通信方式更像\"即時\"的通信方式,它要求接受信号的程序在某個時間範圍内對信号做出反應,是以該信号最多在接受信号程序的生命周期内才有意義,信号所傳遞的資訊是接近于随程序持續的概念(process-persistent);管道及有名管道則是典型的随程序持續IPC,并且,隻能傳送無格式的位元組流無疑會給應用程式開發帶來不便,另外,它的緩沖區大小也受到限制消息隊列就是一個消息的連結清單。可以把消息看作一個記錄,具有特定的格式以及特定的優先級。對消息隊列有寫權限的程序可以向中按照一定的規則添加新消息;對消息隊列有讀權限的程序則可以從消息隊列中讀走消息。消息隊列是随核心持續的。
mq_open - 打開一個消息隊列
概要
#include <fcntl.h> /* 定義 了O_* 常量 */
#include <sys/stat.h> /* 定義了 mode 常量 */
#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);
需要 -lrt 來連結。
描述
mq_open() 建立一個新的 POSIX 消息隊列或打開一個存在的隊列。這個隊列使用 name 辨別。每個消息隊列都通過形如/somename 的名字來區分,這是一個包含起始斜杠在内的最大長度不超過 NAME_MAX(如 225)的以空字元結尾的字元串,在起始斜杠之後不允許再有斜杠。
參數 oflag 參數指定控制操作的标志。(這個标志的值可以通過包含 <fcntl.h> 來獲得。)下面給出的标志至少需要指定一個:
O_RDONLY 以隻能收取消息的方式打開隊列。 O_WRONLY 以隻能發送消息的方式打開的隊列。 O_RDWR 以可發送也可收取消息的方式打開隊列。
零個或多個下面列出的标志可以用 位或 到 oflag 裡:
O_NONBLOCK 以非阻塞的方式打開隊列。具體情況是在 mq_receive(3) 和 mq_send(3) 要阻塞的時候,這些函數使用錯誤 EAGAIN來替代。 O_CREAT 如果不存在則建立消息隊列。消息隊列的所有者(使用者 ID)被設定為調用程序的有效用名 ID。組屬性(組 ID)被設定為調用程序的有效組 ID。 O_EXCL 如果 O_CREAT 在 oflag 裡指定,當名為 name 隊列已經存在時,讓函數以 EEXIST 失敗。
如果 O_CREAT 在 oflag 裡指定,那麼兩個額外的參數也必須給定。mode 參數指定新隊列的權限,這與 open(2) 一樣。(描述權限的符号常量可以通過包含 <sys/stat.h> 來獲得。)權限設定會被程序掩碼處理。參數 attr 指定隊列的屬性。參考 mq_getattr(3) 來了解細節。如果 attr 是 NULL,隊列将使用實作定義的預設屬性來建立。
傳回值
成功時,mq_open() 傳回一個能被其它消息隊列函數使用的消息隊列描述符。失敗時,mq_open() 傳回 (mqd_t) -1,并設定errno 來指明錯誤。
錯誤 EACCES 隊列存在,但是調用者沒有權限以指定的方式打開。 EACCES name 包含多個斜杠。 EEXIST 在 oflag 裡同時指定了O_CREAT 和 O_EXCL,但是名字 name 的隊列已經存在。 EINVAL O_CREAT 在 oflag 裡存在,并且 attr 不是 NULL,但是 attr->mq_maxmsg 或 attr->mq_msqsize 是無效的。這兩個域都必須大于零。對于一個非特權(沒有 CAP_SYS_RESOURCE 能力)程序attr->mq_maxmsg 必須小于或等于 msg_max 限制,并且 attr->mq_msgsize 也必須小于或等于 msgsize_max 限制。此外,就算是特權程序,attr->mq_maxmsg 也不能超過 HARD_MAX 限制。(參看 mq_overview(7) 了解限制的更多細節。) EMFILE 程序已經達到打開的檔案和消息隊列的最大數目。 ENAMETOOLONG name 太長了。 ENFILE 達到系統允許打開檔案個數和消息隊列的全局上限。 ENOENT O_CREAT 沒有在 oflag 裡指定,并且沒有名字 name 的隊列存在。 ENOENT name 隻是 "/" 而沒有其它字元。 ENOMEM 記憶體不足。 ENOSPC 沒有足夠的記憶體來建立新的消息隊列。這個可能是因為達到 queues_max 限制而引起的,參考 mq_overview(7)。
mq_close - 關閉一個消息隊列描述符
概要#include <mqueue.h> int mq_close(mqd_t mqdes);
mq_close() 關閉消息隊列描述符 mqdes。
如果調用程序在消息隊列 mqdes 綁定了通知請求,那麼這個請求被删除,此後其它程序就可以綁定通知請求到此消息隊列。
成功時,mq_close() 傳回 0;錯誤時,傳回 -1,并把 errno 設定為合适的值。
錯誤 EBADF mqdes 指定的描述符無效。
mq_getattr, mq_setattr - 取得/設定消息隊列屬性
#include <mqueue.h>
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr);
描述mq_getattr() 和 mq_setattr() 取得和更改 mqdes 引用的消息隊列的屬性。
mq_getattr() 在 attr 指向的記憶體裡傳回一個 mq_attr 結構體。這個結構定義為:
struct mq_attr {
long mq_flags; /* 标志:0 或 O_NONBLOCK */
long mq_maxmsg; /* 隊列中消息個數最大值 */
long mq_msgsize; /* 消息大小最大值 */
long mq_curmsgs; /* 隊列中目前消息個數 */
};
mq_flags 的域包含打開消息隊列描述符相關的标志。這個标志由 mq_open(3) 初始化。能存在于這個标志的域隻有 O_NONBLOCK。
mq_curmsgs 域用于傳回目前隊列裡已經有消息個數。
mq_setattr() 設定消息隊列的屬性,設定時使用由 newattr 指針指向的 mq_attr 結構的資訊。屬性中隻有标志 mq_flasgs 裡的O_NONBLOCK 标志可以更改,其它在 newattr 内的域都被忽略。如果 oldattr 不是空指針,那麼它指向的記憶體用于傳回現有的屬性結構mq_attr,此時包含資訊與通過 mq_getattr() 取得的資訊相同。
傳回值成功時 mq_getattr() 和 mq_setattr() 傳回 0;錯誤時,-1 被傳回,并同時設定 errno 值來指明錯誤。 錯誤 EBADF mqdes 指定的描述符無效。 EINVAL newattr->mq_flags 包含除 O_NONBLOCK 之外的位。
mq_notify - 注冊一個當消息到達時的通知
int mq_notify(mqd_t mqdes, const struct sigevent *sevp);
描述mq_notify() 允許調用程序針對 mqdes 引用的空隊列注冊或登出新消息異步通知的遞送。
sevp 參數是一個指向 sigevent 結構的指針。關于這個結構的定義與相關細節,見 sigevent(7)。
如果 sevp 是一個非空指針,那麼 mq_notify() 注冊調用程序以在消息到達時收到通知。sevp 指向的 sigevent 結構中的 sigev_notify域用于指明通知的方式。這個域可以有如下幾個值:
SIGEV_NONE 一個“空”通知:調用程序作為通知的目标被注冊,但是當消息達到時,沒有通知被遞送。 SIGEV_SIGNAL 通過發送在sigev_signo 指定的信号來通知程序。見 sigevent(7) 中的細節描述。siginfo_t 結構中的 si_code 域将被設定為 SI_MESGQ。進一步說,si_pid 将被設定為發送消息的程序的程序 ID,同時 si_uid 将被設定為發送程序的真實使用者 ID。 SIGEV_THREAD 當消息到達時,啟動一個新線程來調用 sigev_notify_function。參見 sigevent(7) 來了解其中細節。
對于一個消息隊列隻能有一個程序可以注冊到達通知。
如果 sevp 是 NULL,并且調用程序目前已經在相應的隊列裡注冊過通知,則這個注冊被删除;此後其它程序就可以注冊到這個隊列裡以接收通知。
消息通知隻有在一個新消息到達且之前隊列是空的情況下被遞送。如果 mq_notify() 調用時隊列非空,則通知會在隊列變空之後且有新消息到達時遞送。
如果其它程序或線程正在通過 mq_receive(3) 等待一個空隊列的消息到達,則任何新到達的消息都被忽略:這些消息會傳遞給調用mq_receive(3) 程序或線程,而消息通知注冊仍然有效。
通知隻發生一次:在一個通知遞送之後,這個通知注冊會被删除,并且其它程序可以注冊一個通知。如果收到消息的程序想接收下一個通知,它可能使用 mq_notify() 請求一個全新通知。這可以在所有未讀消息被收到完之前來執行。(此時把隊列設定非阻塞模式會很有用,在清空隊列消息的時候而不用擔心一旦隊列變空之後被阻塞。)
傳回值成功時,mq_notify() 傳回 0;錯誤時,傳回 -1,并把 errno 設定為合适的值。 錯誤 EBADF mqdes 指定的描述符無效。 EBUSY 其它程序已經在這個消息隊列裡注冊的通知。 EINVAL sevp->sigev_notify 不是一個合法的值;或 sevp->sigev_notify 是SIGEV_SIGNAL 而 sevp->sigev_signo 不是一個信号值。 ENOMEM 記憶體不足。
POSIX.1-2008 說當 sevp 是 NULL 時,實作可以産生一個 EINVAL 錯誤,并且調用程序不會被注冊到隊列 mqdes 的通知裡。
mq_send, mq_timedsend - 發送一個消息到消息隊列
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio); #include <time.h>
int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio, const struct timespec *abs_timeout);
glibc 需要特性測試宏(參看 feature_test_macros(7)):
mq_timedsend():
_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L
mq_send() 把 msg_ptr 指向的消息加入由 mqdes 引用的消息隊列裡。參數 msg_len 指定消息 msg_ptr 的長度:這個長度必須小于或等于隊列 mq_msgsize 屬性的值。零長度的消息是允許。
msg_prio 是一個用于指定消息優先級的非負整數。消息在隊列裡依據優先級排序,相同優先級的消息新消息放于舊消息後面。
如果消息隊列已經滿了(如目前隊列消息個數等于隊列的 mq_maxmsg 屬性),那麼,預設地,mq_send() 會一直阻塞到有足夠的空間來把目前消息入隊,或者在阻塞的時候被一個信号處理器中斷。如果 O_NONBLOCK 标志在消息隊列描述符裡有設定,那麼調用将直接由 EAGAIN 錯誤傳回。
mq_timedsend() 的行為與 mq_send() 很類似,隻是消息隊列滿且 O_NONBLOCK 标志沒有設定時,abs_timeout 指向的結構指定了一個阻塞的軟上限時間。這個上限通過從 Epoch 1970-01-01 00:00:00 +0000 (UTC) 開始的絕對的秒和納秒數指定逾時,它的結構定義如下:
struct timespec { time_t tv_sec; /* 秒 */ long tv_nsec; /* 納秒 */ };
如果消息隊列滿,并且調用時逾時設定已經達到,mq_timedsend() 立刻傳回。
成功時,mq_send() 和 mq_timedsend() 傳回零;錯誤時,傳回 -1,并把 errno 設定為合适的值。
錯誤 EAGAIN 隊列已滿,并且由 mqdes 消息隊列描述符 O_NONBLOCK 标志設定。 EBADF mqdes 指定的描述符無效。 EINTR 這個調用被信号處理器中斷,參看 signal(7)。 EINVAL 調用需要阻塞,但 abs_timeout 無效,要麼因為 tv_sec 小于零,要麼因為 tv_nsec小于零或大于 100 百萬。 EMSGSIZE msg_len 大于消息隊列的 mq_msgsize 屬性。 ETIMEDOUT 在消息傳輸完成之前已逾時。
mq_receive, mq_timedreceive - 從一個消息隊列裡收一條消息
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
#include <time.h>
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio, const struct timespec *abs_timeout);
mq_timedreceive():
_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L 描述mq_receive() 從由描述符 mqdes 引用的隊列時删除優先級最高的最老的消息,并把放置到 msg_ptr 的緩存區内。參數 msg_len 指定緩沖區 msg_ptr 的大小:它必須大于隊列的 mq_msgsize 屬性(參數 mq_getattr(3))。如果 prio 不是 NULL,那麼它指向的記憶體用于傳回收到消息相關的優先級。
如果隊列是空的,預設情況下,mq_receive() 會阻塞到有消息準備好為止,或者被信号處理器中斷為止。如果消息隊列描述符的O_NONBLOCK 标志啟用,則調用會以錯誤 EAGAIN 立即傳回。
mq_timedreceive() 的行為與 mq_receive() 很相似,隻不過當隊列是空且 O_NONBLOCK 标志沒有針對相應消息隊列描述符啟用時,調用會阻塞到 abs_timeout 時間點時傳回。這個軟上限是一個絕對的時刻值,它計算從 Epoch 1970-01-01 00:00:00 +0000 (UTC) 開始的秒數和納秒數,這個結構的定義如下:
struct timespec { time_t tv_sec; /* 秒 */ long tv_nsec; /* 納秒 */ }; 如果沒有消息有效,并且逾時的時刻已經達到,mq_timedreceive() 立即傳回。 傳回值成功時,mq_receive() 和 mq_timedreceive() 傳回收到的消息位元組數;失敗時,-1 被傳回,并設定 errno 來指明錯誤。 錯誤 EAGAIN 隊列是空的,并且 O_NONBLOCK 标志在 mqdes 引用的消息隊列描述符上啟用。 EBADF mqdes 指定的描述符無效。 EINTR 這個調用被信号處理器中斷,參看 signal(7)。 EINVAL 調用需要阻塞,但 abs_timeout 無效,要麼因為 tv_sec 小于零,要麼因為 tv_nsec 小于零或大于 100 百萬。 EMSGSIZE msg_len 小于消息隊列的 mq_msgsize 屬性。 ETIMEDOUT 在消息傳輸完成之前已逾時。
mq_unlink - 删除消息隊列
int mq_unlink(const char *name);
描述mq_unlink() 删除名為 name 的消息隊列。消息隊列名将被直接删除。消息隊列本身在所有引用這個隊列的描述符被關閉時銷毀。 傳回值成功時 mq_unlink() 傳回0;錯誤時,傳回 -1,并把 errno 設定為合适的值。 錯誤 EACCES 調用者沒有權限删除這個消息隊列。 ENAMETOOLONG name 太長了。 ENOENT 沒有名為 name 的消息隊列。mq_overview - POSIX 消息隊列概述 描述POSIX 消息隊列允許程序以消息的形式交換資料。這組 API 與 System V 的消息隊列(msgget(2)、msgsnd(2)、msgrcv(2) 等等)有所不同,但是提供了相似的功能。
消息隊列通過 mq_open(3) 建立和打開,這個函數傳回一個 消息隊列描述符 (mqd_t),它将在之後調用中被用于引用那個打開的消息隊列。每個消息隊列都通過形如 /somename 的名字來區分,這是一個包含起始斜杠在内的最大長度不超過 NAME_MAX(如 225)的以空字元結尾的字元串,在起始斜杠之後不允許再有斜杠。兩個程序可能通過在使用 mq_open(3) 時傳遞相同的名字來打開相同的消息隊列。
消息可以使用 mq_send(3) 和 mq_receive(3) 在一個隊列裡傳入和傳出。當一個程序不再使用隊列時,可以通過 mq_close(3) 來關閉它,并且要是這個隊列在将來也不會使用時,可以通過 mq_unlink(3) 來删除它。隊列屬性可以通過 mq_getattr(3) 和 mq_setattr(3) 來取得和更改(在一些情況下)。一個程序可以使用 mq_notify(3) 在一個空的隊列時請求異步的卡片消息到達通知。
一個消息隊列描述符引用一個 消息隊列打開描述符 (參考 open(2))。在調用 fork(2) 之後,子程序繼承了父親消息隊列描述符的複本,并且這些描述符引用與父親中相應描述符相同消息隊列打開描述符。在兩個程序裡相關描述符共享消息隊列打開描述符相關的标志位(mq_flags)。
每個消息都有一個相關的 優先級,并且高優先級的消息總是首先傳遞給接收程序。消息優先級範圍在 0(低優先級) 到sysconf(_SC_MQ_PRIO_MAX) - 1 (高優先級)。在 Linux 系統裡,sysconf(_SC_MQ_PRIO_MAX) 傳回 32768,但是 POSIX.1-2001 隻要求實作提供優先級範圍 0 到 31;一些實作隻提供了這個範圍。
版本POSIX 消息隊列 Linux 核心從 2.6.6 開始支援。Glibc 從版本 2.3.4 開始支援。 核心選項對 POSIX 消息隊列的支援可以核心配制選項 CONFIG_POSIX_MQUEUE 來控制。這個選項在預設是打開的。 生命期(持續性)POSIX 消息隊列是核心生命期的;如果沒有使用mq_unlink(3) 删除的話,一個消息隊列會一直存在,直到系統關閉。 連結使用 POSIX 消息隊列 API 的程式必須在連結的時候加入選項cc -lrt 來連結實時庫,librt。 /proc 接口下列接口可以用于限制 POSIX 消息隊列消耗的記憶體數目: /proc/sys/fs/mqueue/msg_max 這個檔案可以用于檢視和更改隊列裡緩存的軟最大消息數目。這個值是 mq_open(3) 中參數 attr->mq_maxmsg 的軟上限。msg_max 的預設值是 10。最小允許的值是 1(在核心 2.6.28 前是 10)。實體上限是 HARD_MAX:(131072 / sizeof(void *)) (在 Linux/86 上是 32678)。這個軟限制可以被特權(CAP_SYS_RESOURCE)程序忽略,但是 HARD_MAX 不能被突破。 /proc/sys/fs/mqueue/msgsize_max 這個檔案可以用于檢視和更改最大消息長度的軟上限。這個值作用于 mq_open(3) 的參數 attr->mq_msgsize。msgsize_max 的預設值是 8192 個位元組。允許的最小值是 128(在 2.6.28 核心之前是 8192)。msgsize_max 的上限是 1,048,576(核心 2.6.28 之前上限是 INT_MAX:對于 Linux/86 而言是 2,147,483,647 )。這個限制可以被特權(CAP_SYS_RESOURCE)程序忽略。 /proc/sys/fs/mqueue/queues_max 這個檔案可以用于檢視和更改系統範圍内的消息隊列個數的限制。一旦這個限制達到了,隻有特權程序(CAP_SYS_RESOURCE)可以建立新的隊列。queues_max 的預設值是 256,它可以被更改為 0 到 INT_MAX 的任何值。資源限制RLIMIT_MSGQUEUE 是一個資源限制值,它限制程序真實 UID 可以用于消息的最大記憶體數目。在 getrlimit(2) 中有詳細說明。 挂載消息隊列檔案系統在 Linux 系統裡,已建立的消息隊列在一個虛拟的檔案系統裡。(其它實作可能也提供類似的特性,但具體細節可能不同。)這個檔案系統可以(被超級使用者)使用下面指令挂載: # mkdir /dev/mqueue # mount -t mqueue none /dev/mqueue 粘滞位會自動在挂載的目錄打開。
一旦這個檔案系統被挂載,系統裡的消息隊列就可以使用大家熟悉的指令(如 ls(1) 和 rm(1))來檢視和維護了。
目錄裡的每一個檔案都隻包含相應隊列資訊一行内容:
$ cat /dev/mqueue/mymq
QSIZE:129 NOTIFY:2 SIGNO:0 NOTIFY_PID:8260
這些域的含意如下: QSIZE 隊列裡所有消息的總位元組數 NOTIFY_PID 如果這不是零,那麼 程序号為 PID 程序已經使用 mq_notify(3) 來注冊異步消息通知,并且剩餘的域用來描述通知如何發出。 NOTIFY 通知方式:0 是 SIGEV_SIGNAL;1 是 SIGEV_NONE;以及 2 是 SIGEV_THREAD。 SIGNO 對于 SIGEV_SIGNAL 方式所使用信号數字。輪詢消息隊列描述符在 Linux 裡,一個消息隊列描述符是一個真實的檔案描述符,凍僵可以使用 select(2)、poll(2) 或 epoll(7) 來監視。這不具移植性。
mq_overview - POSIX 消息隊列概述
描述POSIX 消息隊列允許程序以消息的形式交換資料。這組 API 與 System V 的消息隊列(msgget(2)、msgsnd(2)、msgrcv(2) 等等)有所不同,但是提供了相似的功能。
POSIX 消息隊列 Linux 核心從 2.6.6 開始支援。Glibc 從版本 2.3.4 開始支援。 核心選項對 POSIX 消息隊列的支援可以核心配制選項CONFIG_POSIX_MQUEUE 來控制。這個選項在預設是打開的。 生命期(持續性)POSIX 消息隊列是核心生命期的;如果沒有使用mq_unlink(3) 删除的話,一個消息隊列會一直存在,直到系統關閉。 連結使用 POSIX 消息隊列 API 的程式必須在連結的時候加入選項cc -lrt 來連結實時庫,librt。 /proc 接口下列接口可以用于限制 POSIX 消息隊列消耗的記憶體數目: /proc/sys/fs/mqueue/msg_max 這個檔案可以用于檢視和更改隊列裡緩存的軟最大消息數目。這個值是 mq_open(3) 中參數 attr->mq_maxmsg 的軟上限。msg_max 的預設值是 10。最小允許的值是 1(在核心 2.6.28 前是 10)。實體上限是 HARD_MAX:(131072 / sizeof(void *)) (在 Linux/86 上是 32678)。這個軟限制可以被特權(CAP_SYS_RESOURCE)程序忽略,但是 HARD_MAX 不能被突破。 /proc/sys/fs/mqueue/msgsize_max 這個檔案可以用于檢視和更改最大消息長度的軟上限。這個值作用于 mq_open(3) 的參數 attr->mq_msgsize。msgsize_max 的預設值是 8192 個位元組。允許的最小值是 128(在 2.6.28 核心之前是 8192)。msgsize_max 的上限是 1,048,576(核心 2.6.28 之前上限是 INT_MAX:對于 Linux/86 而言是 2,147,483,647 )。這個限制可以被特權(CAP_SYS_RESOURCE)程序忽略。 /proc/sys/fs/mqueue/queues_max 這個檔案可以用于檢視和更改系統範圍内的消息隊列個數的限制。一旦這個限制達到了,隻有特權程序(CAP_SYS_RESOURCE)可以建立新的隊列。queues_max 的預設值是 256,它可以被更改為 0 到 INT_MAX 的任何值。資源限制RLIMIT_MSGQUEUE 是一個資源限制值,它限制程序真實 UID 可以用于消息的最大記憶體數目。在 getrlimit(2) 中有詳細說明。 挂載消息隊列檔案系統在 Linux 系統裡,已建立的消息隊列在一個虛拟的檔案系統裡。(其它實作可能也提供類似的特性,但具體細節可能不同。)這個檔案系統可以(被超級使用者)使用下面指令挂載: # mkdir /dev/mqueue # mount -t mqueue none /dev/mqueue 粘滞位會自動在挂載的目錄打開。
$ cat /dev/mqueue/mymq QSIZE:129 NOTIFY:2 SIGNO:0 NOTIFY_PID:8260 這些域的含意如下: QSIZE 隊列裡所有消息的總位元組數 NOTIFY_PID 如果這不是零,那麼 程序号為 PID 程序已經使用 mq_notify(3) 來注冊異步消息通知,并且剩餘的域用來描述通知如何發出。 NOTIFY 通知方式:0 是 SIGEV_SIGNAL;1 是 SIGEV_NONE;以及 2 是 SIGEV_THREAD。 SIGNO 對于 SIGEV_SIGNAL 方式所使用信号數字。輪詢消息隊列描述符在 Linux 裡,一個消息隊列描述符是一個真實的檔案描述符,凍僵可以使用 select(2)、poll(2) 或epoll(7) 來監視。這不具移植性。 遵循于POSIX.1-2001. 注意System V 消息隊列(msgget(2)、msgsnd(2)、msgrcv(2) 等等)是一組老的用于程序互動消息的 API 。POSIX 消息隊列提供了一組優于 System V 消息隊列的接口設計;另一方面 POSIX 消息隊列又不如 System V 消息隊列受到的支援程度好(尤其是老的系統裡)。
目前(2.6.26) Linux 不支援在 POSIX 消息隊列裡應用通路控制清單(ACLs)。
====================================================
消息隊列system v接口API
int msgget(key_t key, int msgflg);
msgget() 系統調用傳回一個與參數 key 相關聯的消息隊列辨別符。如果這個 key是值 IPC_PRIVATE 或 key 不是 IPC_PRIVATE 而又不存在關聯于給定key 的消息隊列并且 IPC_CREAT 在 msgflg中指定,那麼一個新的消息隊列被建立。
如果 msgflg 同時指定了 IPC_CREAT 和 IPC_EXCL 并且一個關聯key 的消息隊列存在,則 msgget() 将失敗并且設定 errno為EEXIST。(這與 open(2) 組合O_CREAT | O_EXCL 有相同效果。)
如果建立了消息,則參數 msgflg 有意義的最後幾個位定義了消息隊列的權限。這些權限位的格式與語義同open(2) 中的 mode參數相同。(執行權限沒有使用。)
如果新消息隊列被建立了,那麼它關聯的 msqid_ds 結構(參考 msgctl(2))按如下方式初始化:
msg_perm.cuid 和 msg_perm.uid 被設定用調用程序的有效使用者ID。
msg_perm.cgid 和 msg_perm.gid 設定為調用程序的有效組 ID。
msg_perm.mode 最後有效的 9 個位使用 msgflg 最後有效的 9個位來設定。
msg_qnum、msg_lspid、msg_lrpid、msg_stime和 msg_rtime 都設定為 0。
msg_ctime 設定為目前時間。
msg_qbytes 設定為系統限制 MSGMNB。
如果一個消息隊列已經存在并且權限認證通過,則會額外檢視一下隊列是不是被标志為釋放。
傳回值如果成功,傳回值将是消息隊列辨別符(一個非負整數),否則的話 -1 被傳回并設定 errno 來指明錯誤。錯誤如果失敗,errno 可能被設定為如下值:EACCES已經有一個關聯于 key 的消息隊列,但是調用程序沒有對這個隊列的通路權限,同時這個程序沒有CAP_IPC_OWNER 特權。
EEXIST關聯 key 的消息隊列已經存在而 msgflg 卻同時設定了 IPC_CREAT和 IPC_EXCL。
ENOENT不存在關聯于 key 的消息隊列可 msgflg 沒有指定IPC_CREAT。
ENOMEM必需建立一個消息隊列但系統沒有足夠的記憶體來建立相應的資料結構。
ENOSPC必需建立一個消息隊列但是系統允許的最大消息隊列個數(MSGMNI) 已經達到。遵循于SVr4, POSIX.1-2001。注意IPC_PRIVATE 不是一個标志域而一個 key_t 類型的值。如果使用這一個特殊值作為key,msgflg 除了最後 9 位以外的其它内容都會被系統調用忽略,并且建立一個(成功)消息。
下面是一些對 msgget() 有效果的系統限制:
MSGMNI系統範圍内的消息隊列最大數目:政策相關的(在 Linux 系統裡,這個限制可能通過/proc/sys/kernel/msgmni 讀取和悠。
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
參數:
msqid:消息隊列的識别碼。
msgp:指向消息緩沖區的指針,此位置用來暫時存儲發送和接收的消息,是一個使用者可定義的通用結構,形态如下
struct msgbuf {
long mtype;
char mtext[1];
msgsz:消息的大小。
msgtyp:從消息隊列内讀取的消息形态。如果值為零,則表示消息隊列中的所有消息都會被讀取。
msgflg:用來指明核心程式在隊列沒有資料的情況下所應采取的行動。如果msgflg和常數IPC_NOWAIT合用,則在msgsnd()執行時若是消息隊列已滿,則msgsnd()将不會阻塞,而會立即傳回-1,如果執行的是msgrcv(),則在消息隊列呈空時,不做等待馬上傳回-1,并設定錯誤碼為ENOMSG。當msgflg為0時,msgsnd()及msgrcv()在隊列呈滿或呈空的情形時,采取阻塞等待的處理模式。
傳回說明:
成功執行時,msgsnd()傳回0,msgrcv()傳回拷貝到mtext數組的實際位元組數。失敗兩者都傳回-1,errno被設為以下的某個值
[對于msgsnd]
EACCES:調用程序在消息隊列上沒有寫權能,同時沒有CAP_IPC_OWNER權能
EAGAIN:由于消息隊列的msg_qbytes的限制和msgflg中指定IPC_NOWAIT标志,消息不能被發送
EFAULT:msgp指針指向的記憶體空間不可通路
EIDRM:消息隊列已被删除
EINTR:等待消息隊列空間可用時被信号中斷
EINVAL:參數無效
ENOMEM:系統記憶體不足,無法将msgp指向的消息拷貝進來
[對于msgrcv]
E2BIG:消息文本長度大于msgsz,并且msgflg中沒有指定MSG_NOERROR
EACCES:調用程序沒有讀權能,同時沒具有CAP_IPC_OWNER權能
EAGAIN:消息隊列為空,并且msgflg中沒有指定IPC_NOWAIT
EFAULT:msgp指向的空間不可通路
EIDRM:當程序睡眠等待接收消息時,消息已被删除
EINTR:當程序睡眠等待接收消息時,被信号中斷
ENOMSG:msgflg中指定了IPC_NOWAIT,同時所請求類型的消息不存在
int msgctl(int msqid, int cmd, struct msqid_ds *buf)
msgctl 系統調用對 msgqid 辨別的消息隊列執行 cmd 操作,系統定義了 3 種 cmd 操作: IPC_STAT , IPC_SET , IPC_RMID ,意義分别如下:
IPC_STAT : 該指令用來擷取消息隊列對應的 msqid_ds 資料結構,并将其儲存到 buf 指定的位址空間。
IPC_SET : 該指令用來設定消息隊列的屬性,要設定的屬性存儲在 buf 中,可設定的屬性包括: msg_perm.uid , msg_perm.gid , msg_perm.mode 以及 msg_qbytes .
IPC_RMID : 從核心中删除 msqid 辨別的消息隊列。
當cmd是IPC_RMID時,buf指定為NULL,對msqid對應的消息隊列進行删除
當cmd是IPC_SET 時,将buf set為 msqid對應的消息隊列
當cmd是IPC_STAT ,将msqid對應的消息隊列 get到buf