1. 程式人生 > >Linux程序間通訊之POSIX訊息佇列

Linux程序間通訊之POSIX訊息佇列

訊息佇列可認為是一個訊息連結串列,它允許程序之間以訊息的形式交換資料。有足夠寫許可權的程序或執行緒可往佇列中放置訊息,有足夠讀許可權的程序或執行緒可從佇列中取走訊息。每個訊息都是一個記錄,它由傳送者賦予一個優先順序。與管道不同,管道是位元組流模型,沒有訊息邊界。
本文介紹的是POSIX訊息佇列。POSIX訊息佇列與System V訊息佇列的相似之處在於資料的交換單位是整個訊息,但它們之間仍然存在一些顯著的差異。

  • POSIX訊息佇列是引用計數的。只有當所有當前使用佇列的程序都關閉了佇列之後才會對佇列進行標記以便刪除。
  • 每個System V訊息都有一個整數型別,並且通過msgrcv()可以以各種方式類選擇訊息。與之形成鮮明對比的是,POSIX訊息有一個關聯的優先順序,並且訊息之間是嚴格按照優先順序順序排隊的(以及接收)。
  • POSIX訊息佇列提供了一個特性允許在佇列中的一條訊息可用時非同步地通知程序。

概述

POSIX訊息佇列API中的主要函式如下。

  • mq_open()函式建立一個新訊息佇列或開啟一個既有佇列,返回後續呼叫中會用到的訊息佇列描述符。
  • mq_send()函式向佇列寫入一條訊息。
  • mq_recevie()函式從佇列中讀取一條訊息。
  • mq_close()函式關閉程序之前開啟的一個訊息佇列。
  • mq_unlink()函式刪除一個訊息佇列名並當所有程序關閉該佇列時對佇列進行標記以便刪除。

上面的函式所完成的功能是相當明顯的。此外,POSIX訊息佇列API還具備一些特別的特性。

  • 每個訊息佇列都有一組關聯的特性,其中一些特性可以在使用mq_open()建立或開啟佇列時進行設定。獲取和修改佇列特性的工作則是由兩個函式來完成的:mq_getattr()和mq_setattr()。
  • mq_notify()函式允許一個程序向一個佇列註冊接收訊息通知。在註冊完之後,當一條訊息可用時會通過傳送一個訊號或在一個單獨的執行緒中呼叫一個函式來通知程序。

開啟、關閉和斷開連結訊息佇列

開啟一個訊息佇列

mq_open()函式建立一個新訊息佇列或開啟一個既有佇列。

#include<fcntl.h>
#include<sys/stat.h>
#include<mqueue.h>

mqd_t mq_open(const char *name,int oflag,mode_t mode,struct mq_attr *attr);

name引數標識出了訊息佇列,訊息佇列物件的名字的最大長度為NAME_MAX(255)個字元。
oflag引數是一個位掩碼,它控制著mq_open()操作的各個方面。
oflag引數的位值:

標記 描述
O_CREAT 佇列不存在時建立佇列
O_EXCL 與O_CREAT一起使用,若訊息佇列已存在,則錯誤返回
O_RDONLY 只讀開啟
O_WRONLY 只寫開啟
O_RDWR 讀寫開啟
O_NONBLOCK 以非阻塞模式開啟

oflag引數的其中一個用途是,確定是開啟一個既有佇列還是建立和開啟一個新佇列。如果在oflag中不包含O_CREAT,那麼將會開啟一個既有佇列。如果在oflag中包含了O_CREAT,並且與給定的name對應的佇列不存在,那麼就會建立一個新的空佇列。
如果在oflag中同時包含O_CREAT和O_EXCL,並且與給定的name對應的佇列已經存在,那麼mq_open()就會失敗。
mq_open()通常用來開啟一個既有訊息佇列,這種呼叫只需要兩個引數,但如果在flags中指定了O_CREAT,那麼就還需要另外兩個引數:mode和attr。這些引數用法如下:

  • mode引數是一個位掩碼,它指定了施加於新訊息佇列之上的許可權。這個引數可取的位置與檔案上的掩碼值是一樣的,並且與open()一樣,mode中的值會與程序的umask取掩碼。要從一個佇列中讀取訊息就必須要將讀許可權賦予相應的使用者,要向佇列寫入訊息就需要寫許可權。
  • attr引數是一個mq_attr結構,它指定了新訊息佇列的特性。如果attr為NULL,那麼將使用實現定義的預設特性建立佇列。mq_attr結構後在後面進行介紹。

mq_open()在成功結束時會返回一個訊息佇列描述符,它是一個型別為mqd_t的值,在後續的呼叫中將會使用它來引用這個開啟著的訊息佇列。

fork()、exec()以及程序終止對訊息佇列描述符的影響

  • 在fork()中子程序會接收其父程序的訊息佇列描述符的副本,並且這些描述符會引用同樣 的開啟著的訊息佇列描述符。子程序不會繼承其父程序的任何訊息通知註冊。
  • 當一個程序執行了一個exec()或終止時,所有其開啟的訊息佇列描述符會被關閉。

關閉一個訊息佇列

mq_close()函式關閉訊息佇列描述符mqdes。

#include<mqueue.h>

int mq_close(mqd_t mqdes);

與檔案上的close()一樣,關閉一個訊息佇列並不會刪除該佇列。要刪除佇列則需要使用mq_unlinl(),它是unlink()在訊息佇列上的版本。

刪除一個訊息佇列

mq_unlink()函式刪除通過name標識的訊息佇列,並將佇列標記為在所有程序使用完該佇列之後銷燬該佇列。

#include<mqueue.h>

int mq_unlink(const char *name);

訊息佇列的特性

mq_open()、mq_getattr()以及mq_setattr()函式都會接收一個引數,它是一個指向mq_attr結構的指標。這個結構是在<mqueue.h>中進行定義的,其形式如下:

struct mq_attr

{

        long mq_flags;//阻塞標誌, 0或O_NONBLOCK

        long mq_maxmsg;//最大訊息數

        long mq_msgsize;//每個訊息最大大小

        long mq_curmsgs;//當前訊息數

};

在開始深入介紹mq_attr的細節之前有必要指出以下幾點。

  • 這三個函式中的每個函式都只用到了其中幾個欄位。上面給出的結構定義中的註釋指出了各個函式所用到的欄位。
  • 這個結構包含了與一個訊息描述符相關聯的開啟的訊息佇列描述的相關資訊以及該描述符所引用的佇列的相關資訊。
  • 其中一些欄位中包含的資訊在使用mq_open()建立佇列時就已經確定下來了(mq_maxmsg和mq_msgsize);其他欄位則會返回訊息佇列描述或訊息佇列的當前狀態相關資訊。

在建立佇列時設定訊息佇列特性

在使用mq_open()建立訊息佇列時可以通過下列mq_attr欄位來確定佇列的特性。

  • mq_maxmsg欄位定義了使用mq_send()向訊息佇列新增訊息的數量上限,其取值必須大於0。
  • mq_msgsize欄位定義了加入訊息佇列的每條訊息的大小的上限,其取值必須大於0。

mq_maxmsg和mq_msgsize特性是在訊息佇列被建立時就確定下來的,並且之後也無法修改這兩個特性。

獲取訊息佇列特性

mq_getattr()函式返回一個包含與描述符mqdes相關聯的訊息佇列描述和訊息佇列的相關資訊的mq_attr結構。

#include<mqueue.h>

int mq_getattr(mqd_t mqdes,struct mq_attr *attr);

除了上面已經介紹的mq_maxmsg和mq_msgsize欄位之外,attr指向的返回結構中還包含下列欄位。
mq_flags
這些是與描述符mqdes相關聯的開啟的訊息佇列描述的標記,其取值只有一個:O_NONBLOCK。這個標記是根據mq_open()的oflag引數來初始化的,並且使用mq_setattr()可以修改這個標記。
mq_curmsgs
當前位於佇列中的訊息數。這個資訊在mq_getattr()返回時可能已經發生了改變,前提是存在其他程序從佇列中讀取訊息或寫入訊息。

修改訊息佇列特性

mq_setattr()函式設定與訊息佇列描述符mqdes相關聯的訊息佇列描述的特性,並可選地返回與訊息佇列有關的訊息。

#include<mqueue.h>

int mq_setattr(mq_t mqdes,const struct mq_attr *newattr,struct mq_attr *oldattr);

mq_setattr()函式執行下列任務。

  • 它使用newattr指向的mq_attr結構中的mq_flags欄位來修改與描述符mqdes相關聯的訊息佇列描述的標記。
  • 如果oldattr不為NULL,那麼就返回一個包含之前的訊息佇列描述標記和訊息佇列特性的mq_attr結構。

交換訊息

傳送訊息

mq_send()函式將位於msg_ptr指向的緩衝區中的訊息新增到描述符mqdes所引用的訊息佇列中。

#include<mqueue.h>

int mq_send(mqd_t mqdes,const char *msg_ptr,size_t msg_len,unsigned int msg_prio);

msg_len引數指定了msg_ptr指向的訊息的長度,其值必須小於或等於佇列的mq_msgsize特性,否則mq_send()就會返回EMSGSIZE錯誤。長度為零的訊息是允許的。
每條訊息都擁有一個用非負整數表示的優先順序,它通過msg_prio引數指定。訊息在佇列中是按照優先順序倒序排列的(即0表示優先順序最低)。當一條訊息被新增到佇列中時,它會被放置在佇列中具有相同優先順序的所有訊息之後。如果一個應用程式無需使用訊息優先順序,那麼只需要將msg_prio指定為0即可。

接收訊息

mq_receive()函式從mqdes引用的訊息佇列中刪除一條優先順序最高、存在時間最長的訊息並將刪除的訊息放置在msg_ptr指向的緩衝區。

#include<mqueue.h>

ssize_t mq_receive(mqd_t mqdes, char *mdg_ptr,size_t msg_len,unsigned int *msg_prio);

呼叫著使用msg_len引數來指定msg_ptr指向的緩衝區中的可用位元組數。
不管訊息的實際大小是什麼,msg_len必須要大於或等於佇列的mq_msgsize特性,否則mq_receive()就會失敗並返回EMSGSIZE錯誤。如果不清楚一個佇列的mq_msgsize特性的值,那麼可以使用mq_getattr()來獲取這個值。
如果msg_prio不為NULL,那麼接收到的訊息的優先順序會被複制到msg_prio指向的位置處。

訊息通知

POSIX訊息佇列能夠接收之前為空的佇列上有可用訊息的非同步通知(即佇列從空變成了非空)。這個特性意味著已經無需執行一個阻塞的呼叫或將訊息佇列描述符標記為非阻塞並在佇列上定期執行mq_receive()呼叫。
程序可以選擇通過訊號的形式或者通過一個單獨的執行緒中呼叫一個函式的形式來接收通知。

mq_notify()函式註冊呼叫程序在一條訊息進入描述符mqdes引用的空佇列時接收通知。

#include<mqueue.h>

int mq_notify(mqd_t mqdes,const struct sigevent *notification);

notification引數指定了程序接收通知的機制。
有關訊息的通知需要注意以下幾點:

  • 在任何一個時刻都只有一個程序能夠向一個特定的訊息佇列註冊接收通知。如果一個訊息佇列上已經存在註冊程序了,那麼後續在該佇列上的註冊請求將會失敗。
  • 只有當一條新訊息進入之前為空的佇列時註冊程序才會收到通知。如果在註冊的時候佇列中已經包含訊息,那麼只有當佇列被清空之後有一條新訊息達到之時才會發出通知。
  • 當向註冊程序傳送了一個通知之後就會刪除註冊資訊,之後任何程序就能夠向佇列註冊接收通知了。當通知被髮送給註冊程序時,註冊即被撤銷,該程序若要重新註冊,則必須重新呼叫mq_notify()。
  • 註冊程序只有在當前不存在其他在該佇列上呼叫mq_receive()而發生阻塞的程序時才會收到通知。如果其他程序在mq_receive()呼叫中被阻塞了,那麼該程序會讀取訊息,註冊程序會保持註冊狀態。
  • 一個程序可以通過在呼叫mq_notify()時傳入一個值為NULL的notification引數來撤銷自己在訊息通知上的註冊資訊。
struct sigevent
{
    int sigev_notify;
    int sigev_signo;
    union sigval sigev_value;
    void (*sigev_notify_function)(union sigval);
    pthread_attr_t *sigev_notify_attributes;
};

sigev_notify取值:
SIGEV_NONE:事件發生時,什麼也不做;
SIGEV_SIGNAL:事件發生時,將sigev_signo指定的訊號傳送給指定的程序;SIGEV_THREAD:事件發生時,核心會(在此程序內)以sigev_notify_attributes為執行緒屬性建立一個執行緒,並讓其執行sigev_notify_function,並以sigev_value為其引數

sigev_signo:在sigev_notify=SIGEV_SIGNAL時使用,指定訊號類別

sigev_value:sigev_notify=SIGEV_SIGEV_THREAD時使用,作為sigev_notify_function的引數

union sigval
{
int sival_int;
void *sival_ptr;
};

sigev_notify_function:在sigev_notify=SIGEV_THREAD時使用,其他情況下置NULL
sigev_notify_attributes:在sigev_notify=SIGEV_THREAD時使用,指定建立執行緒的屬性,其他情況下置NULL

相關推薦

Linux程序通訊POSIX訊息佇列

訊息佇列可認為是一個訊息連結串列,它允許程序之間以訊息的形式交換資料。有足夠寫許可權的程序或執行緒可往佇列中放置訊息,有足夠讀許可權的程序或執行緒可從佇列中取走訊息。每個訊息都是一個記錄,它由傳送者賦予一個優先順序。與管道不同,管道是位元組流模型,沒有訊息邊界。

Linux程序通訊POSIX共享記憶體

共享記憶體是最高效的IPC機制,因為它不涉及程序之間的任何資料傳輸。這種高效率帶來的問題是,我們必須用其他輔助手段來同步程序對共享記憶體的訪問,否則會產生競態條件。因此,共享記憶體通常和其他程序間通訊方式一起使用。 Linux下有三種共享記憶體的IPC技術:S

LinuxLinux程序通訊訊息佇列

1、訊息佇列概念引入    訊息佇列提供了一個從一個程序向另外一個程序傳送一塊資料的方法每個資料塊都被認為是有一個型別,接收者程序接收的資料塊可以有不同的型別值訊息佇列也有管道一樣的不足,就是每個訊息的最大長度是有上限的(MSG

Linux程序通訊訊號量(semaphore)、訊息佇列(Message Queue)和共享記憶體(Share Memory)

System V 程序通訊方式:訊號量(semaphore)、訊息佇列(Message Queue)和共享記憶體(Share Memory) 訊號量 訊號量(semaphore)實際是一個整數,它的值由多個程序進行測試(test)和設定(set)。就每個程序所關心的測試和

程序程式設計程序通訊-管道和訊息佇列

1.程序間通訊 Linux作為一種新興的作業系統,幾乎支援所有的Unix下常用的程序間通訊方法:管道、訊息佇列、共享記憶體、訊號量、套介面等等。 2.管道 管道是程序間通訊中最古老的方式,它包括無名管道(或者匿名管道)和有名管道兩種,前者用於父程序和

linux 程序通訊FIFO

1.概述 FIFO與管道幾乎類似,所以FIFO也是一個位元組流,從FIFO讀取的順序也是與被寫入FIFO的順序一致,容量是也有限的,也是可以確保寫入不超過PIPE_BUF位元組的操作是原子的,FIFO的本質也是一個管道,但傳遞方向是可以雙向的,它們兩者之間的最大差別在於FIFO在檔案系統中擁有一個名稱,並且

Linux程序通訊訊號通訊

訊號通訊是Linux程序間通訊的一種方式。 1.什麼是訊號? 訊號是系統響應某些條件而產生的一個事件,接收到該訊號的程序會相應地採取一些措施。例如我們在windows系統中想強制結束一個程式我們需要用到的是工作管理員,而在Linux中,我們是通過訊號來實現的,執

程序通訊方式總結——訊息佇列

        Linux/Unix系統IPC是各種程序間通訊方式的統稱,但是其中極少能在所有Linux/Unix系統實現中進行移植。隨著POSIX和Open Group(X/Open)標準化的推進

Linux程序通訊pipe

1、管道(PIPE)        從概念上說,管道是兩個程序之間的一個connection,因此一個程序的標準輸出就變成了另一個程序的標準輸入。在Unix作業系統中,管道用於程序間通訊(inter-process communication). (1

Linux程序通訊——管道(整理)

 程序間通訊 fork pipe pie_t 等用法(管道機制 通訊) 每個程序各自有不同的使用者地址空間,任 何一個程序的全域性變數在另一個程序中都看不到,所以程序之間要交換資料必須通過核心,在核心中開闢一塊緩衝 區,程序1把資料從使用者空間拷到核心緩衝區,程序2再從

linux 程序通訊訊號

Linux訊號(signal) 機制分析 轉載至:https://www.cnblogs.com/hoys/archive/2012/08/19/2646377.html 【摘要】本文分析了Linux核心對於訊號的實現機制和應

Linux -- 程序通訊管道

管道是Linux裡的一種檔案型別,同時也是Linux系統下程序間通訊的一種方式 建立一個管道檔案有兩種方式: 1.  Shell 下命令 mkfifo + filename,即建立一個有名管道 2.

程序通訊——管道,訊息佇列,共享記憶體

程序間通訊的本質是讓兩個不相干的程序看到同一份資源。這個資源是由作業系統提供的一個檔案。程序間通訊的目的:1.資料傳輸:一個程序需要將它 的資料傳送給另一個程序。2.資源共享:多個程序之間共享同樣的資源。3.通知事件:一個程序需要向另一個(組)程序傳送訊息,通知它們發生了

Linux 程序通訊管道

程序之間的通訊之管道 目錄 1 無名管道    管道是一種最基本的IPC機制,作用於父子程序之間,完成資料傳遞。 管道有以下特性: 1.其本質是一個偽檔案(實為核心緩衝區)其本質是一個偽檔案(實為核心緩衝區) 2.由兩

程序通訊---管道和訊息佇列

程序間通訊的目的:資料傳輸:一個程序需要將它的資料傳送給另一個程序資源共享:對個程序之間共享同樣的資源通知事件:一個程序需要向另一個或一組程序傳送訊息,通知它們發生了什麼事件程序控制:有些程序希望完全控制另一個程序的執行(如:Debug程序)程序間通訊的發展:管道:Syste

linux程序通訊訊號量(semaphore)

==================================================== 訊號量(semaphore)簡介 當我們在多使用者系統,多程序系統,或是兩者混合的系統中使用執行緒操作編寫程式時,我們經常會發現我們有段臨界程式碼,在此處我們需要

Linux程序通訊(IPC)程式設計實踐(十二)Posix訊息佇列--基本API的使用

posix訊息佇列與system v訊息佇列的差別: (1)對posix訊息佇列的讀總是返回最高優先順序的最早訊息,對system v訊息佇列的讀則可以返回任意指定優先順序的訊息。 (2)當往一個空佇列放置一個訊息時,posix訊息佇列允許產生一個訊號或啟動一個執行緒,

Linux程序通訊訊息佇列、訊號量和共享儲存

訊息佇列、訊號量和共享儲存是IPC(程序間通訊)的三種形式,它們功能不同,但有相似之處,下面先介紹它們的相似點,然後再逐一說明。 1、相似點 每個核心中的IPC結構(訊息佇列、訊號量和共享儲存)都用一個非負整數的識別符號加以引用,與檔案描述符不同,當一個

Linux---程序通訊IPC訊息佇列

**程序間通訊(IPC):**是指在不同程序之間傳播或交換資訊。 **IPC的方式:**通常有管道(無名管道、命名管道)、訊息佇列、訊號量、共享儲存、Socket、Streams等(Socket和Streams支援不同主機上的兩個程序IPC) 程序間通訊的目

Linux程序通訊訊息佇列

在上一篇部落格裡,我們學習了程序間通訊的一種方式,那就是管道,今天我們繼續學習另一種方式訊息佇列。 訊息佇列 一. 什麼是訊息佇列?   訊息佇列是訊息的連結串列,存放在核心中並由訊息佇列識別符號表示。   訊息佇列提供了一個從一個程序向另一個程