linux 消息隊列機制

現在我們來討論第三種也是最后一種System V IPV工具:消息隊列。在許多方面看來,消息隊列類似于有名管道,但是卻沒有與打開與關閉管道的復雜關聯。然而,使用消息隊列并沒有解決我們使用有名管道所遇到的問題,例如管道上的阻塞。

?

消息隊列提供了一種在兩個不相關的進程之間傳遞數據的簡單高效的方法。與有名管道比較起來,消息隊列的優點在獨立于發送與接收進程,這減少了在打開與關閉有名管道之間同步的困難。

?

消息隊列提供了一種由一個進程向另一個進程發送塊數據的方法。另外,每一個數據塊被看作有一個類型,而接收進程可以獨立接收具有不同類型的數據塊。消息隊列的好處在于我們幾乎可以完全避免同步問題,并且可以通過發送消息屏蔽有名管道的問題。更好的是,我們可以使用某些緊急方式發送消息。壞處在于,與管道類似,在每一個數據塊上有一個最大尺寸限制,同時在系統中所有消息隊列上的塊尺寸上也有一個最大尺寸限制。

?

盡管有這些限制,但是X/Open規范并沒有定義這些限制的具體值,除了指出超過這些尺寸是某些消息隊列功能失敗的原因。Linux系統有兩個定義,MSGMAXMSGMNB,這分別定義單個消息與一個隊列的最大尺寸。這些宏定義在其他系統上也許并不相同,甚至也許就不存在。

?

消息隊列函數定義如下:

?

#include <sys/msg.h>

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

int msgget(key_t key, int msgflg);

int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);

int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);

?

與信息號和共享內存一樣,頭文件sys/types.hsys/ipc.h通常也是需要的。

?

msgget

?

我們可以使用msgget函數創建與訪問一個消息隊列:

?

int msgget(key_t key, int msgflg);

?

與其他IPC工具類似,程序必須提供一個指定一個特定消息隊列的key值。特殊值IPC_PRIVATE創建一個私有隊列,這在理論上只可以為當前進程所訪問。與信息量和共享內存一樣,在某些Linux系統上,消息隊列并不是私有的。因為私有隊列用處較少,因而這并不是一個嚴重問題。與前面一樣,第二個參數,msgflg,由9個權限標記組成。要創建一個新的消息隊列,由IPC_CREAT特殊位必須與其他的權限位進行或操作。設置IPC_CREAT標記與指定一個已存在的消息隊列并不是錯誤。如果消息隊列已經存在,IPC_CREAT標記只是簡單的被忽略。

?

如果成功,msgget函數會返回一個正數作為隊列標識符,如果失敗則會返回-1

?

msgsnd

?

msgsnd函數允許我們將消息添加到消息隊列:

?

int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);

?

消息結構由兩種方式來限定。第一,他必須小于系統限制,第二,必須以long int開始,這在接收函數中會用作一個消息類型。當我們在使用消息時,最好是以如下形式來定義我們的消息結構:

?

struct my_message {

????long int message_type;

????/* The data you wish to transfer */

}

?

因為message_type用于消息接收,所以我們不能簡單的忽略他。我們必須定義我們自己的數據結構來包含并對其進行初始化,從而他可以包含一個可知的值。

?

第一個參數,msgid,是由msgget函數所返回的消息隊列標識符。

?

第二個參數,msg_ptr,是一個指向要發送消息的指針,正如前面所描述的,這個消息必須以long int類型開始。

?

第三個參數,msg_sz,是由msg_ptr所指向的消息的尺寸。這個尺寸必須不包含long int消息類型。

?

第四個參數,msgflg,控制如果當前消息隊列已滿或是達到了隊列消息的系統限制時如何處理。如果msgflg標記設置了IPC_NOWAIT,函數就會立即返回而不發送消息,并且返回值為-1。如果msgflg標記清除了IPC_NOWAIT標記,發送進程就會被掛起,等待隊列中有可用的空間。

?

如果成功,函數會返回0,如果失敗,則會返回-1。如果調用成功,系統就會復制一份消息數據并將其放入消息隊列中。

?

msgrcv

?

msgrcv函數由一個消息隊列中收取消息:

?

int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);

?

第一個參數,msqid,是由msgget函數所返回的消息隊列標記符。

?

第二個參數,msg_ptr,是一個指向將要接收消息的指針,正如在msgsnd函數中所描述的,這個消息必須以long int類型開始。

?

第三個參數,msg_sz,是由msg_ptr所指向的消息的尺寸,并不包含long int消息類型。

?

第四個參數,msgtype,是一個long int類型,允許一個接收優先級形式的實現。如果msgtype的值為0,隊列中第一個可用的消息就會被接收。如果其值大于0,具有相同消息類型的第一個消息就會被接收。如果其值小于0,第一個具有相同類型或是小于msgtype絕對值的消息就會被接收。

?

這聽起來要比實際操作復雜得多。如果我們只是簡單的希望以其發送的順序來接收消息,我們可以將msgtype設置為0。如果我們希望接收特殊消息類型的消息,我們可以將msgtype設置為等于這個值。如果我們希望接收消息類型為n或是小于n的值,我們可以將msgtype設置為-n

?

第五個參數,msgflg,控制當沒有合適類型的消息正在等待被接收時如何處理。如果在msgflg中設置了IPC_NOWAIT位,調用就會立即返回,而返回值為-1。如果msgflg標記中消除了IPC_NOWAIT位,進程就會被掛起,等待一個合適類型的消息到來。

?

如果成功,msgrcv會返回放入接收緩沖區中的字節數,消息會被拷貝到由msg_ptr所指向的用戶分配緩沖區中,而數據就會由消息隊列中刪除。如果失敗則會返回-1

?

msgctl

?

最后一個消息隊列函數是msgctl,這與共享內存中的控制函數十分類似。

?

int msgctl(int msqid, int command, struct msqid_ds *buf);

?

msqid_ds結構至少包含下列成員:

?

struct msqid_ds {

????uid_t msg_perm.uid;

????uid_t msg_perm.gid

????mode_t msg_perm.mode;

}

?

第一個參數,msqid,是由msgget函數所返回的標記符。

?

第二個參數,command,是要執行的動作。他可以取下面三個值:

?

命令 ???????描述

IPC_STAT ???設置msqid_ds結構中的數據來反射與消息隊列相關聯的值。

IPC_SET ???????如果進程有權限這樣做,這個命令會設置與msqid_ds數據結構中所提供的消息隊列相關聯的值。

IPC_RMID ???刪除消息隊列。

?

如果成功則會返回0,如果失敗則會返回-1。當進程正在msgsnd或是msgrcv函數中等待時如果消息隊列被刪除,發送或接收函數就會失敗。

?

試驗--消息隊列

?

現在我們已經了解了消息隊列的定義,我們可以來看一下他們是如何實際工作的。與前面一樣,我們將會編寫兩個程序:msg1.c來接收,msg2.c來發送。我們會允許任意一個程序創建消息隊列,但是使用接收者在接收到最后一條消息后刪除消息隊列。

?

1 下面是接收程序:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <errno.h>

#include <unistd.h>

?

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

?

struct my_msg_st

{

????long int my_msg_type;

????char some_text[BUFSIZ];

};

?

int main()

{

????int running = 1;

????int msgid;

????struct my_msg_st some_data;

????long int msg_to_receive = 0;

?

2 首先,我們設置消息隊列:

?

????msgid = msgget((key_t)1234,0666|IPC_CREAT);

?

????if(msgid == -1)

????{

????????fprintf(stderr,"msgget failed with error: %d\n", errno);

????????exit(EXIT_FAILURE);

????}

?

3 然后,接收消息隊列中的消息直到遇到一個end消息。最后,消息隊列被刪除:

?

????while(running)

????{

????????if(msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1)

????????{

????????????fprintf(stderr, "msgrcv failed with errno: %d\n", errno);

????????????exit(EXIT_FAILURE);

????????}

?

????????printf("You wrote: %s", some_data.some_text);

????????if(strncmp(some_data.some_text, "end", 3)==0)

????????{

????????????running = 0;

????????}

????}

?

????if(msgctl(msgid, IPC_RMID, 0)==-1)

????{

????????fprintf(stderr, "msgctl(IPC_RMID) failed\n");

????????exit(EXIT_FAILURE);

????}

?

????exit(EXIT_SUCCESS);

}

?

4 發送程序與msg1.c類似。在main函數中,刪除msg_to_receive聲明,代之以buffer[BUFSIZ]。移除消息隊列刪除代碼,并且在running循環中做出如下更改。現在我們調用msgsnd來將輸入的文本發送到隊列中。

?

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <errno.h>

?

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

?

#define MAX_TEXT 512

?

struct my_msg_st

{

????long int my_msg_type;

????char some_text[MAX_TEXT];

};

?

int main()

{

????int running = 1;

????struct my_msg_st some_data;

????int msgid;

????char buffer[BUFSIZ];

?

????msgid = msgget((key_t)1234, 0666|IPC_CREAT);

?

????if(msgid==-1)

????{

????????fprintf(stderr,"msgget failed with errno: %d\n", errno);

????????exit(EXIT_FAILURE);

????}

?

????while(running)

????{

????????printf("Enter some text: ");

????????fgets(buffer, BUFSIZ, stdin);

????????some_data.my_msg_type = 1;

????????strcpy(some_data.some_text, buffer);

?

????????if(msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0)==-1)

????????{

????????????fprintf(stderr, "msgsnd failed\n");

????????????exit(EXIT_FAILURE);

????????}

?

????????if(strncmp(buffer, "end", 3) == 0)

????????{

????????????running = 0;

????????}

????}

?

????exit(EXIT_SUCCESS);

}

?

與管道中的例子不同,進程并沒有必要提供自己的同步機制。這是消息隊列比起管道的一個巨大優點。

?

假設消息隊列有空間,發送者可以創建隊列,在隊列中放入一些數據,并且甚至可以在接收者啟動之前退出。我們會首先運行發送者。如下面的例子輸出:

?

$ ./msg2

Enter some text: hello

Enter some text: How are you today?

Enter some text: end

$ ./msg1

You wrote: hello

You wrote: How are you today?

You wrote: end

$

?

工作原理

?

發送者程序使用msgget創建一個消息隊列;然后使用msgsnd函數向隊列中添加消息。接收者使用msgget來獲得消息隊列標識符,并且接收消息,直到接收到特殊消息end。然后他會使用msgctl刪除消息隊列進行一些清理工作。

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

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

相關文章

堆(概念,數據結構中堆與內存堆區的區別 ,堆的基本操作)

堆的特性&#xff1a; 必須是完全二叉樹 用數組實現 任一結點的值是其子樹所有結點的最大值或最小值 最大值時&#xff0c;稱為“最大堆”&#xff0c;也稱大根堆&#xff1b; 在完全二叉樹中&#xff0c;任何一個子樹的最大值都在這個子樹的根結點。最小值時&#xff0c;稱為…

makefile中的shell調用---注意事項

在之前一次編寫makfile時候&#xff0c;有看到相關的makefile中使用$$來引用變量&#xff0c;而且嘗試后發現$$使用居然和${}有類似的功能。當時也沒具體追究相關的用法&#xff0c;當然剛才所說的都是錯誤的觀念 $$&#xff1a;在makefile中會被替換成一個$。 相關資料是這么描…

網絡基礎2(分層模型,通信過程,以太網,ARP協議格式和具體功能詳解)

分層模型 OSI七層模型 OSI模型 1 物理層&#xff1a;主要定義物理設備標準&#xff0c;如網線的接口類型、光纖的接口類型、各種傳輸介質的傳輸速率等。它的主要作用是傳輸比特流&#xff08;就是由1、0轉化為電流強弱來進行傳輸&#xff0c;到達目的地后再轉化為1、0&#…

為github帳號添加SSH keys

使用git clone命令從github上同步github上的代碼庫時&#xff0c;如果使用SSH鏈接&#xff08;如我自己的beagleOS項目&#xff1a;gitgithub.com:DamonDeng/beagleOS.git&#xff09;&#xff0c;而你的SSH key沒有添加到github帳號設置中&#xff0c;系統會報下面的錯誤&…

網絡基礎3(IP段格式,UDP數據報格式,TCP數據報格式)

IP段格式 IP數據報的首部長度和數據長度都是可變長的&#xff0c;但總是4字節的整數倍。 對于IPv4&#xff0c;4位版本字段是4。4位首部長度的數值是以4字節為單位的&#xff0c;最小值為5&#xff0c;也就是說首部長度最小是4x520字節&#xff0c;也就是不帶任何選項的IP首部…

Linux 開發路線

Linux 開發路線&#xff1a; 使用 linux—〉linxu 系統編程開發---〉驅動開發和分析 linux 內核 開始學 linux 內核:最好有三件寶物:《深入理解 linux 內核》《LINUX內核源代碼情景分析》和源代碼。 《深》是綱,《情》是目。最后深入代碼 Linux 內核原理&#xff1a;比較淺顯…

堆的應用(堆排序,TopK問題)

堆的應用 1&#xff09;排序 堆排序 選擇排序 既可以找到最大的放在最后 也可以找到最小的方最前 但是&#xff0c;堆排序不能找最小的放在最前 因為把最小數放在最前&#xff0c;會破壞掉堆的原來的順序&#xff0c;除非重新建堆 1&#xff0c; 2&#xff0c;9&#xff0c…

有名管道和無名管道的區別

1&#xff09;無名管道:管道是半雙工的&#xff0c;數據只能向一個方向流動&#xff1b;需要雙方通信時&#xff0c;需要建立起兩個管道&#xff1b;只能用于父子進程或者兄弟進程之間&#xff08;具有親緣關系的進程&#xff09;。 單獨構成一種獨立的文件系統&#xff1a;管道…

網絡基礎4(TCP三次握手,四次握手,TCP流量控制,TCP狀態轉換 , TCP異常斷開,設置TCP屬性,端口復用)

TCP協議 TCP通信時序 下圖是一次TCP通訊的時序圖。TCP連接建立斷開。包含大家熟知的三次握手和四次握手。 TCP通訊時序 在這個例子中&#xff0c;首先客戶端主動發起連接、發送請求&#xff0c;然后服務器端響應請求&#xff0c;然后客戶端主動關閉連接。 兩條豎線表示通訊的…

linux編程手冊讀書筆記第一章(20140329)

&#xff08;2&#xff09;管道、FIFO、套接字、設備&#xff08;比如終端、偽終端&#xff09;都支持非阻塞模式。&#xff08;因為無法通過open&#xff08;&#xff09;來獲取管道和套接字的文件描述符。所以要啟用非阻塞標志&#xff0c;就必須使用fcntl&#xff08;&#…

排序(基本概念及分類,直接插入排序和希爾排序)

排序的概念 排序&#xff1a;所謂排序&#xff0c;就是使一串記錄&#xff0c;按照其中的某個或某些關鍵字的大小&#xff0c;遞增或遞減的排列起來的操作。 穩定性&#xff1a;假定在待排序的記錄序列中&#xff0c;存在多個具有相同的關鍵字的記錄&#xff0c;若經過排序&a…

Linux編程手冊讀書筆記第二章(20140330)

內核&#xff1a;管理和分配計算機資源&#xff08;即CPU、RAM和設備&#xff09;的核心軟件層Linux內核可執行文件采用&#xff0f;boot&#xff0f;vmlinuz或類似的路徑名&#xff0c;“z”表明內核是經過壓縮的可執行文件。內核主要任務&#xff1a; &#xff08;1&#xff…

直接交換排序

直接交換排序 缺點&#xff1a;進行一些重復性比較&#xff0c;解決放法&#xff1a;堆排序 選擇排序優化 //如果當前的數大于假定最大的數 //改變下標 //如果當前的數小于假定最小的數 //改變下標 //遍歷數組跳到下一個元素 //如果最大的數沒有在它的位置上 //交換 //交換…

Linux編程手冊讀書筆記第三章(20140407)

外殼函數執行一條中斷機器指令&#xff08;int 0x80&#xff09;&#xff0c;引發處理器從用戶態切換到核心態&#xff0c;并執行系統中斷0x80的中斷矢量所指向的代碼。&#xff08;在2.6內核及glib 2.3.2之后的版本都支持sysenter指令&#xff0c;進入內核的速度更快&#xff…

Linux編程手冊讀書筆記第四章(20140407)

標準文件描述符定義在<unistd.h>中&#xff0c;STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO打開一個文件&#xff1a;open&#xff08;&#xff09; &#xff03;include<sys/stat.h> #include<fcntl.h> int open(const char *pathname, int flags, …/* …

快速排序概念及實現

快速排序 快速排序是Hoare于1962年提出的一種二叉樹結構的交換排序方法&#xff0c; 其基本思想為&#xff1a; 任取待排序元素序列中的某元素作為基準值&#xff0c;按照該排序碼將待排序集合分割成兩子序列&#xff0c;左子序列中所有元素均小于基準值&#xff0c;右子序列…

Linux編程手冊讀書筆記第五章(20140408)

改變已打開文件性質&#xff1a;fcntl&#xff08;&#xff09; #include<fcntl.h> int fcntl(int fd, int cmd, …); (1) 調用失敗返回&#xff0d;1 &#xff08;2&#xff09;fcntl函數有5種功能&#xff1a; a. 復制一個現有的描述符&#xff08;cmd&#xff1d;F_D…

歸并排序概念及其實現

基本思想&#xff1a; 歸并排序&#xff08;MERGE-SORT&#xff09;是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一個非常典型的應用。將已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每個…

##連接符和#符的使用

C語言中如何使用宏C&#xff08;和C&#xff09;中的宏&#xff08;Macro&#xff09;屬于編譯器預處理的范疇&#xff0c;屬于編譯期概念&#xff08;而非運行期概念&#xff09;。下面對常遇到的宏的使用問題做了簡單總結。 關于#和## 在C語言的宏中&#xff0c;#的功能是將其…

計數排序和基數排序

適用于數據集中在某個范圍中&#xff0c; //統計每個數據出現的次數 計數排序&#xff1a;鴿巢原理 1找范圍 2給空間 3記次數 4回收 for(int i 0;i<size; i) {temp[array[i]]; }for(int i0;i<range;i&#xff09;{while(temp[i])array[index]i;}代碼實現 時間復雜度&…