消息隊列接口API(posix 接口和 system v接口)

消息隊列 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_CREAToflag 里指定,當名為 name 隊列已經存在時,讓函數以 EEXIST 失敗。

如果 O_CREAToflag 里指定,那么兩個額外的參數也必須給定。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_CREATO_EXCL,但是名字 name 的隊列已經存在。 EINVAL O_CREAToflag 里存在,并且 attr 不是 NULL,但是 attr->mq_maxmsgattr->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);

需要 -lrt 來鏈接。

描述

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);

需要 -lrt 來鏈接。

描述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_maxmsgmq_msgsize 兩個域由 mq_open(3) 在隊列創建時設置的。mq_maxmsg 是使用 mq_sen(3) 發送數據大小的上限。mq_msgsize 是當前隊列可以緩存消息個數的上限。用于設置這些域的軟上限的兩個 /proc 文件在 mq_open(3) 中有描述。

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 - 注冊一個當消息到達時的通知
概要
#include <mqueue.h>
int mq_notify(mqd_t mqdes, const struct sigevent *sevp);

需要 -lrt 來鏈接。

描述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_notifySIGEV_SIGNALsevp->sigev_signo 不是一個信號值。 ENOMEM 內存不足。

POSIX.1-2008 說當 sevp 是 NULL 時,實現可以產生一個 EINVAL 錯誤,并且調用進程不會被注冊到隊列 mqdes 的通知里。

mq_send, mq_timedsend - 發送一個消息到消息隊列

#include <mqueue.h>

int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio); #include <time.h>

?#include <mqueue.h>

?int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio, const struct timespec *abs_timeout);

需要 -lrt 來鏈接。

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 - 從一個消息隊列里收一條消息
概要
#include <mqueue.h>
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
#include <time.h>
#include <mqueue.h>
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio, const struct timespec *abs_timeout);

需要 -lrt 來鏈接。

glibc 需要特性測試宏(參看 feature_test_macros(7)):

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 - 刪除消息隊列
概要
#include <mqueue.h>
int mq_unlink(const char *name);

需要 -lrt 來鏈接。

描述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_msgsizemsgsize_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) 等等)有所不同,但是提供了相似的功能。

消息隊列通過 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_msgsizemsgsize_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) 來監視。這不具移植性。 遵循于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_PRIVATEkey 不是 IPC_PRIVATE 而又不存在關聯于給定key 的消息隊列并且 IPC_CREATmsgflg中指定,那么一個新的消息隊列被創建。

如果 msgflg 同時指定了 IPC_CREATIPC_EXCL 并且一個關聯key 的消息隊列存在,則 msgget() 將失敗并且設置 errnoEEXIST。(這與 open(2) 組合O_CREAT | O_EXCL 有相同效果。)

如果創建了消息,則參數 msgflg 有意義的最后幾個位定義了消息隊列的權限。這些權限位的格式與語義同open(2) 中的 mode參數相同。(執行權限沒有使用。)

如果新消息隊列被創建了,那么它關聯的 msqid_ds 結構(參考 msgctl(2))按如下方式初始化:

msg_perm.cuidmsg_perm.uid 被設置用調用進程的有效用戶ID。

msg_perm.cgidmsg_perm.gid 設置為調用進程的有效組 ID。

msg_perm.mode 最后有效的 9 個位使用 msgflg 最后有效的 9個位來設置。

msg_qnummsg_lspidmsg_lrpidmsg_stime msg_rtime 都設置為 0。

msg_ctime 設置為當前時間。

msg_qbytes 設置為系統限制 MSGMNB

?

如果一個消息隊列已經存在并且權限認證通過,則會額外查看一下隊列是不是被標志為釋放。

返回值如果成功,返回值將是消息隊列標識符(一個非負整數),否則的話 -1 被返回并設置 errno 來指明錯誤。錯誤如果失敗,errno 可能被設置為如下值:EACCES已經有一個關聯于 key 的消息隊列,但是調用進程沒有對這個隊列的訪問權限,同時這個進程沒有CAP_IPC_OWNER 特權。

EEXIST關聯 key 的消息隊列已經存在而 msgflg 卻同時設置了 IPC_CREATIPC_EXCL

ENOENT不存在關聯于 key 的消息隊列可 msgflg 沒有指定IPC_CREAT

ENOMEM必需創建一個消息隊列但系統沒有足夠的內存來建立相應的數據結構。

ENOSPC必需創建一個消息隊列但是系統允許的最大消息隊列個數(MSGMNI) 已經達到。遵循于SVr4, POSIX.1-2001。注意IPC_PRIVATE 不是一個標志域而一個 key_t 類型的值。如果使用這一個特殊值作為keymsgflg 除了最后 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:當進程睡眠等待接收消息時,被信號中斷
EINVAL:參數無效
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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445059.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445059.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445059.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

算法(6)-leetcode-explore-learn-數據結構-數組字符串的雙指針技巧

leetcode-explore-learn-數據結構-數組4-雙指針技巧1.雙指針技巧--適用情形11.1概述1.2 例題1.2.1 反轉字符串1.2.2數組拆分1.2.3 兩數之和22雙指針技巧-適用情形22.1概述2.2例題2.2.1 移除元素2.2.2 最大連續1的個數2.2.3長度最小的子數組本系列博文為leetcode-explore-learn子…

POSIX和SYSTEM的消息隊列應該注意的問題

首先看看POSIX的代碼&#xff1a; 1.posix_mq_server.c #include <mqueue.h> #include <sys/stat.h> #include <string.h> #include <stdio.h> #define MQ_FILE "/mq_test" #define BUF_LEN 128 int main() { mqd_t mqd; char b…

算法(7)-leetcode-explore-learn-數據結構-數組-小結

leetcode-explore-learn-數據結構-數組5-小結1.概述2.例題2.1旋轉數組2.2 楊輝三角22.3翻轉字符串里的單詞2.4反轉字符串中的單詞32.5 刪除排序數組中的重復項2.6 移動零本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處&#xff0c;請參考leetcode官…

fcntl函數詳解

功能描述&#xff1a;根據文件描述詞來操作文件的特性。 #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock); [描述] fcntl()針對(文件)描述符提供控…

使用nohup讓程序永遠后臺運行

使用nohup讓程序永遠后臺運行 Unix/Linux下一般比如想讓某個程序在后臺運行&#xff0c;很多都是使用 & 在程序結尾來讓程序自動運行。比如我們要運行mysql在后臺&#xff1a; /usr/local/mysql/bin/mysqld_safe --usermysql &但是加入我們很多程序并不象mysqld一樣做…

算法(8)-leetcode-explore-learn-數據結構-鏈表

leetcode-explore-learn-數據結構-鏈表11.概述1.1 鏈表插入操作1.2 鏈表刪除操作2.設計鏈表本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處&#xff0c;請參考leetcode官網&#xff1a;https://leetcode-cn.com/explore/learn/card/linked-list/所…

Mysql索引優化實例講解

MYSQL描述&#xff1a;一個文章庫&#xff0c;里面有兩個表&#xff1a;category和article。category里面有10條分類數據。article里面有20萬條。article里面有一個"article_category"字段是與category里的"category_id"字段相對應的。article表里面已經把…

給自己的VIM配置

編輯 .vimrc 文件如下&#xff1a; filetype plugin on "autocmd Filetype cpp,c,java,cs set omnifunccppcomplete#Complete set nu set nocp set nobackup let g:C_AuthorName gaoke let g:C_AuthorRef gaoke let g:C_Email gaoketaomee.…

shell一文入門通

簡單來說“Shell編程就是對一堆Linux命令的邏輯化處理”。 W3Cschool 上的一篇文章是這樣介紹 Shell的 hello world 學習任何一門編程語言第一件事就是輸出HelloWord了&#xff01;下面我會從新建文件到shell代碼編寫來說下Shell 編程如何輸出Hello World。 (1)新建一個文件…

算法(9)--兩個數的最大公約數

兩個數的最大公約數1.輾轉相除法求解兩個數的最大公約數2.更相減損術求解兩個數的最大公約數3.不嚴格理解1.輾轉相除法求解兩個數的最大公約數 輾轉相除法&#xff1a;兩個正整數a和b&#xff08;a>b&#xff09;的最大公約數等于a除以b的余數與b 之間的最大公約數。–如果…

RPC編程

圖 3 說明在客戶機和服務器之間完成 RPC 涉及的步驟。 圖 3. 在客戶機和服務器之間完成 RPC 涉及的步驟服務器 RPC 應用程序初始化期間它會向 RPC 運行時庫注冊接口。需要注冊接口是因為&#xff0c;客戶機在向服務器發出遠程過程調用時&#xff0c;要檢查它是否與服務器兼容。…

synchronized使用和原理全解

synchronized是Java中的關鍵字&#xff0c;是一種同步鎖。它修飾的對象有以下幾種&#xff1a; 修飾一個方法 被修飾的方法稱為同步方法&#xff0c;其作用的范圍是整個方法&#xff0c;作用的對象是調用這個方法的對象&#xff1b; 修飾一個靜態的方法 其作用的范圍是整個…

RPC學習筆記

在查看libc6-dev軟件包提供的工具&#xff08;用 dpkg -L libc6-dev 命令&#xff09;的時候&#xff0c;發現此軟件包提供了一個有用的工具rpcgen命令。通過rpcgen的man手冊看到此工具的作用是把RPC源程序編譯成C語言源程序&#xff0c;從而輕松實現遠程過程調用。下面的例子程…

算法(10)-leetcode-explore-learn-數據結構-鏈表雙指針技巧

leetcode-explore-learn-數據結構-鏈表21.概述2.例題2.1 環形鏈表判斷2.2 環形鏈表22.3 相交鏈表2.4 刪除鏈表的倒數第N個節點3.小結本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處&#xff0c;請參考leetcode官網&#xff1a;https://leetcode-cn…

一個簡單的游戲服務器框架

最近看到百度空間的一個帖子&#xff0c;不錯&#xff0c;在這里整理下&#xff0c;轉載至我的博客里&#xff0c;開始自己慢慢琢磨寫一個框架。 我先從上層結構說起&#xff0c;一直到實現細節吧&#xff0c;想起什么就寫什么。 第一部分 服務器邏輯 服務器這邊簡單的分為三…

堆和棧的精華大總結

Java內存分配原理 棧、堆、常量池雖同屬Java內存分配時操作的區域&#xff0c;但其適用范圍和功用卻大不相同。 一般Java在內存分配時會涉及到以下區域&#xff1a; ◆寄存器&#xff1a;我們在程序中無法控制 ◆棧&#xff1a;存放基本類型的數據和對象的引用&#xff0c;但…

算法(11)-leetcode-explore-learn-數據結構-鏈表的經典問題

leetcode-explore-learn-數據結構-鏈表31.反轉一個鏈表2.移除鏈表元素3.奇偶鏈表4.回文鏈表5.小結本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處&#xff0c;請參考leetcode官網&#xff1a;https://leetcode-cn.com/explore/learn/card/linked-l…

探索式軟件測試

James A.Whittaker [美] 詹姆斯惠特克&#xff08;軟件測試領域絕對的大師&#xff09;著作《Exploratory Software Testing》&#xff0c;中文名《探索式軟件測試》&#xff0c;記得當時被這本書深深吸引啦&#xff08;我不知道有多少做測試的小伙伴看過這本書&#xff09;&am…

Linux線程池的設計

我設計這個線程池的初衷是為了與socket對接的。線程池的實現千變萬化&#xff0c;我得這個并不一定是最好的&#xff0c;但卻是否和我心目中需求模型的。現把部分設計思路和代碼貼出&#xff0c;以期拋磚引玉。個人比較喜歡搞開源&#xff0c;所以大家如果覺得有什么需要改善的…

算法(12)-leetcode-explore-learn-數據結構-雙鏈表的設計

leetcode-explore-learn-數據結構-鏈表4雙鏈表的設計本系列博文為leetcode-explore-learn子欄目學習筆記&#xff0c;如有不詳之處&#xff0c;請參考leetcode官網&#xff1a;https://leetcode-cn.com/explore/learn/card/linked-list/所有例題的編程語言為python 雙鏈表的設…