Linux消息隊列原理與應用

消息隊列 (也叫做報文隊列)Unix系統V版本中3種進程間通信機制之一。另外兩種是信號燈共享內存。這些IPC機制使用共同的授權方法。只有通過系統調用將標誌符傳遞給核心之後,進程才能存取這些資源。這種系統IPC對象使用的控制方法和文件系統非常類似。使用對象的引用標誌符作爲資源表中的索引。

消息隊列就是一個消息的鏈表。就是把消息看作一個記錄,並且這個記錄具有特定的格式以及特定的優先級。對消息隊列有寫權限的進程可以按照一定的規則添加新消息;對消息隊列有讀權限的進程則可以從消息隊列中讀出消息。

Linux採用消息隊列的方式來實現消息傳遞。這種消息的發送方式是:發送方不必等待接收方檢查它所收到的消息就可以繼續工作下去,而接收方如果沒有收到消息也不需等待。這種通信機制相對簡單,但是應用程序使用起來就需要使用相對複雜的方式來應付了。新的消息總是放在隊列的末尾,接收的時候並不總是從頭來接收,可以從中間來接收。

消息隊列是kernel persistence並和進程相關,只有在內核重起或者顯示刪除一個消息隊列時,該消息隊列纔會真正被刪除。因此系統中記錄消息隊列的數據結構 (struct ipc_ids msg_ids)位於內核中,系統中的所有消息隊列都可以在結構msg_ids中中找到訪問入口。

IPC標識符:每一個I P C目標都有一個唯一的I P C標識符。這裏所指的I P C目標是指一個單獨的消息隊列、一個信號量集或者一個共享的內存段。系統內核使用此標識符在系統內核中指明 I P C目標。

IPC 關鍵字:想要獲得唯一的標識符,則必須使用一個 I P C關鍵字。客戶端進程和服務器端進程必須雙方都同意此關鍵字。這是建立一個客戶機/服務器框架的第一步。在System V IPC機制中,建立兩端聯繫的路由方法是和I P C關鍵字直接相關的。通過在應用程序中設置關鍵字值,每一次使用的關鍵字都可以是相同的。一般情況下,可以使用f t o k ( )函數爲客戶端和服務器端產生關鍵字值。

二、ipcs 命令

命令ipcs用於讀取System V IPC目標的狀態。
ipcs -q: 只顯示消息隊列。
ipcs -s: 只顯示信號量。
ipcs -m: 只顯示共享內存。
ipcs –help: 其他的參數。

下面是ipcs命令輸出的例子:

[root@wanglong wanglong]# ipcs

—— Shared Memory Segments ——–

key        shmid      owner      perms      bytes      nattch     status      

0×00000000 0          root      644        40         2                       

0×00000000 32769      root      644        16384      2                       

0×00000000 65538      root      644        268        2                       

—— Semaphore Arrays ——–

key        semid      owner      perms      nsems     

0×000000a7 0          root      600        1         

0×00000000 98305      apache    600        1         

0×00000000 65538      apache    600        1         

0×00000000 131075     apache    600        1         

0×00000000 163844     apache    600        1         

0×00000000 196613     apache    600        1         

0×00000000 229382     apache    600        1         

0×00000000 262151     apache    600        1         

0×00000000 294920     apache    600        1         

—— Message Queues ——–

key        msqid      owner      perms      used-bytes   messages 

 

三、消息隊列的主要調用

內核中實現消息傳遞機制的代碼基本上都在文件ipc/msg.c,消息隊列的主要調用有下面4個,這裏只作簡單介紹:

(1)msgget:調用者提供一個消息隊列的鍵標 (用於表示個消息隊列的唯一名字),當這個消息隊列存在的時候, 這個消息調用負責返回這個隊列的標識號;如果這個隊列不存在,就創建一個消息隊列,然後返回這個消息隊列的標識號 ,主要由sys_msgget執行。

(2)msgsnd:向一個消息隊列發送一個消息,主要由sys_msgsnd執行。

(3)msgrcv:從一個消息隊列中收到一個消息,主要由sys_msgrcv執行。

(4)msgctl:在消息隊列上執行指定的操作。根據參數的不同和權限的不同,可以執行檢索、刪除等的操作,主要由sys_msgctl執行。

四、消息隊列的應用例子

下面的例子很好的演示了創建、發送、讀取、改變權限以及刪除消息隊列各種操作:

消息隊列 (也叫做報文隊列)是Unix系統V版本中3種進程間通信機制之一。另外兩種是信號燈和共享內存。這些IPC機制使用共同的授權方法。只有通過系統調用將標誌符傳遞給核心之後,進程才能存取這些資源。這種系統IPC對象使用的控制方法和文件系統非常類似。使用對象的引用標誌符作爲資源表中的索引。

消息隊列就是一個消息的鏈表。就是把消息看作一個記錄,並且這個記錄具有特定的格式以及特定的優先級。對消息隊列有寫權限的進程可以按照一定的規則添加新消息;對消息隊列有讀權限的進程則可以從消息隊列中讀出消息。

Linux採用消息隊列的方式來實現消息傳遞。這種消息的發送方式是:發送方不必等待接收方檢查它所收到的消息就可以繼續工作下去,而接收方如果沒有收到消息也不需等待。這種通信機制相對簡單,但是應用程序使用起來就需要使用相對複雜的方式來應付了。新的消息總是放在隊列的末尾,接收的時候並不總是從頭來接收,可以從中間來接收。

消息隊列是隨內核持續的並和進程相關,只有在內核重起或者顯示刪除一個消息隊列時,該消息隊列纔會真正被刪除。因此係統中記錄消息隊列的數據結構 (struct ipc_ids msg_ids)位於內核中,系統中的所有消息隊列都可以在結構msg_ids中中找到訪問入口。

IPC標識符:每一個I P C目標都有一個唯一的I P C標識符。這裏所指的I P C目標是指一個單獨的消息隊列、一個信號量集或者一個共享的內存段。系統內核使用此標識符在系統內核中指明 I P C目標。

IPC 關鍵字:想要獲得唯一的標識符,則必須使用一個 I P C關鍵字。客戶端進程和服務器端進程必須雙方都同意此關鍵字。這是建立一個客戶機/服務器框架的第一步。在System V IPC機制中,建立兩端聯繫的路由方法是和I P C關鍵字直接相關的。通過在應用程序中設置關鍵字值,每一次使用的關鍵字都可以是相同的。一般情況下,可以使用f t o k ( )函數爲客戶端和服務器端產生關鍵字值。

二、ipcs 命令 
命令ipcs用於讀取System V IPC目標的狀態。
ipcs -q: 只顯示消息隊列。
ipcs -s: 只顯示信號量。
ipcs -m: 只顯示共享內存。
ipcs –help: 其他的參數。

下面是ipcs命令輸出的例子:

[root@wanglong wanglong]# ipcs

—— Shared Memory Segments ——–

key        shmid      owner      perms      bytes      nattch     status      

0×00000000 0          root      644        40         2                       

0×00000000 32769      root      644        16384      2                       

0×00000000 65538      root      644        268        2                       

—— Semaphore Arrays ——–

key        semid      owner      perms      nsems     

0×000000a7 0          root      600        1         

0×00000000 98305      apache    600        1         

0×00000000 65538      apache    600        1         

0×00000000 131075     apache    600        1         

0×00000000 163844     apache    600        1         

0×00000000 196613     apache    600        1         

0×00000000 229382     apache    600        1         

0×00000000 262151     apache    600        1         

0×00000000 294920     apache    600        1         

—— Message Queues ——–

key        msqid      owner      perms      used-bytes   messages 

 

三、消息隊列的主要調用 
內核中實現消息傳遞機制的代碼基本上都在文件ipc/msg.c中,消息隊列的主要調用有下面4個,這裏只作簡單介紹:

(1)msgget:調用者提供一個消息隊列的鍵標 (用於表示個消息隊列的唯一名字),當這個消息隊列存在的時候, 這個消息調用負責返回這個隊列的標識號;如果這個隊列不存在,就創建一個消息隊列,然後返回這個消息隊列的標識號 ,主要由sys_msgget執行。

(2)msgsnd:向一個消息隊列發送一個消息,主要由sys_msgsnd執行。

(3)msgrcv:從一個消息隊列中收到一個消息,主要由sys_msgrcv執行。

(4)msgctl:在消息隊列上執行指定的操作。根據參數的不同和權限的不同,可以執行檢索、刪除等的操作,主要由sys_msgctl執行。

四、消息隊列的應用例子 
下面的例子很好的演示了創建、發送、讀取、改變權限以及刪除消息隊列各種操作:


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_SEND_SIZE 80

struct mymsgbuf {
long mtype;
char mtext[MAX_SEND_SIZE];
};

void send_message(int qid, struct mymsgbuf *qbuf, long type, char *text);
void read_message(int qid, struct mymsgbuf *qbuf, long type);
void remove_queue(int qid);
void change_queue_mode(int qid, char *mode);
void usage(void);
int main(int argc, char *argv[])
{
key_t key;
int msgqueue_id;
struct mymsgbuf qbuf;

if(argc == 1)
usage();

/* Create unique key via call to ftok() */
key = ftok(“.”, ’m');

/* Open the queue - create if necessary */
if((msgqueue_id = msgget(key, IPC_CREAT|0660)) == -1)

 {
perror(“msgget”);
exit(1);
}

switch(tolower(argv[1][0]))
{
case ’s’: send_message(msgqueue_id, (struct mymsgbuf *)&qbuf,atol(argv[2]), argv[3]);
break;
case ’r': read_message(msgqueue_id, &qbuf, atol(argv[2]));
break;
case ’d': remove_queue(msgqueue_id);
break;
case ’m': change_queue_mode(msgqueue_id, argv[2]);
break;

default: usage();

}

return(0);
}

void send_message(int qid, struct mymsgbuf *qbuf, long type, char *text)
{
/* Send a message to the queue */
printf(“Sending a message …n”);
qbuf->mtype = type;
strcpy(qbuf->mtext, text);

if((msgsnd(qid, (struct msgbuf *)qbuf,
strlen(qbuf->mtext)+1, 0)) ==-1)
{
perror(“msgsnd”);
exit(1);
}
}

void read_message(int qid, struct mymsgbuf *qbuf, long type)
{
/* Read a message from the queue */
printf(“Reading a message …n”);
qbuf->mtype = type;
msgrcv(qid, (struct msgbuf *)qbuf, MAX_SEND_SIZE, type, 0);

printf(“Type: %ld Text: %sn”, qbuf->mtype, qbuf->mtext);
}

void remove_queue(int qid)
{
/* Remove the queue */
msgctl(qid, IPC_RMID, 0);
}

void change_queue_mode(int qid, char *mode)
{
struct msqid_ds myqueue_ds;

/* Get current info */
msgctl(qid, IPC_STAT, &myqueue_ds);

/* Convert and load the mode */
sscanf(mode, ”%ho”, &myqueue_ds.msg_perm.mode);

/* Update the mode */
msgctl(qid, IPC_SET, &myqueue_ds);
}

void usage(void)
{
fprintf(stderr, ”msgtool - A utility for tinkering with msg queuesn”);
fprintf(stderr, ”nUSAGE: msgtool (s)end n”);
fprintf(stderr, ” (r)ecv n”);
fprintf(stderr, ” (d)eleten”);
fprintf(stderr, ” (m)ode n”);
exit(1);
}

 

程序保存爲 ipcs.c

編譯:gcc -o ipcs ipcs.c

程序運行結果解釋:

[root@wanglong wanglong]# ./ipcs  s  001  hello!

Sending a message …

[root@wanglong wanglong]# ./ipcs  s  001  world!

Sending a message …

[root@wanglong wanglong]# ./ipcs  s  001  you

Sending a message …

[root@wanglong wanglong]# ./ipcs  s  001  and

Sending a message …

[root@wanglong wanglong]# ./ipcs  s  001  me!

Sending a message …

[root@wanglong wanglong]# ./ipcs  r  001 

Reading a message …

Type: 001 Text:hello!

[root@wanglong wanglong]# ./ipcs  r  001 

Reading a message …

Type: 001 Text:world!

[root@wanglong wanglong]# ./ipcs  r  001  

Reading a message …

Type: 001 Text:you

[root@wanglong wanglong]# ./ipcs  d  001    /*刪除了消息隊列001*/ 

[root@wanglong wanglong]# ./ipcs  r  001 

Reading a message ..                        ./* 因爲刪除了,所以讀不出消息了*/



程序保存爲 ipcsvmsg.c

編譯:gcc -o ipcsvmsg ipcsvmsg.c

程序運行結果解釋:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章