UNIX環境下如何應用消息隊列

火星人 @ 2014-03-12 , reply:0


  一、 引 言

---- 進 入 九 十 年 代 后, 隨 著 計 算 機 和 網 絡 技 術 的 發 展, 很 多 數 據 處 理 系 統 都 采 用 開 放 系 統 結 構 的 客 戶 機/ 服 務 器 網 絡 模 式。 即 客 戶 機 提 出 任 務 請 求, 由 服 務 器 做 相 應 處 理, 執 行 被 請 求 的 任 務, 然 后 將 結 果 返 回 給 客 戶 機。 例 如: 銀 行ATM 的 前 置 機 和 數 據 處 理 的 主 機 之 間 即 構 成 客 戶 機/ 服 務 器 模 式; 電 話 銀 行 的 前 置 機 和 銀 行 數 據 處 理 主 機 之 間 也 構 成 這 種 模 式 結 構。 還 有POS 等。 這 樣, 各 種 應 用 的 請 求 是 很 頻 繁 的, 數 據 主 機 在 處 理 通 存 通 兌, ATM, 電 話 銀 行, POS 等 各 種 請 求 時, 如 果 沒 有 相 應 機 制 的 控 制, 數 據 將 出 現 混 亂, 有 可 能 產 生 透 支, 也 有 可 能 處 理 密 碼 已 改 變 的 帳 戶.。 數 據 的 完 整 性, 安 全 性 無 法 控 制。 而 消 息 隊 列 正 是 解 決 這 一 問 題 的 有 力 工 具。 它 使 主 機 在 處 理 各 種 請 求 時, 按 照 先 后 順 序 有 條 不 紊 地 進 行, 保 證 了 數 據 的 一 致 性 和 安 全 性。

二、 基 本 概 念

---- 1. 隊 列

---- 隊 列 是 信 息 的 線 性 表, 它 的 訪 問 次 序 是 先 進 先 出(FIFO)。 也 就 是 說, 置 入 隊 列 中 的 第 一 個 數 據 項 將 是 從 隊 列 中 第 一 次 讀 出 的 數 據 項, 置 入 的 第 二 項 將 是 讀 出 的 第 二 項, 依 此 類 推。 這 是 隊 列 允 許 的 唯 一 存 取 操 作, 其 它 隨 機 訪 問 是 不 允 許 的。 這 種 數 據 結 構 保 證 對 數 據 資 源 的 請 求 將 嚴 格 按 照 先 后 順 序 進 行, 因 而 可 用 於 對 事 件 的 調 度 並 起 到I/O 緩 沖 的 作 用。

---- 2. 報 文

---- 發 送 進 程 和 接 收 進 程 進 行 信 息 的 交 換, 一 般 是 通 過 將 信 息 划 分 為 若 干 段 放 入 數 據 交 換 緩 沖 器 中, 進 程 間 通 過 對 該 緩 沖 器 的 存 取 來 實 現 通 信。 因 此, 數 據 是 以 不 連 續 的 形 式 在 進 程 間 傳 送, 這 些 不 連 續 的 部 分 就 叫 報 文。

---- 3. 消 息 隊 列

---- 將 報 文 按 隊 列 的 結 構 進 行 組 織 就 叫 消 息 隊 列。 該 隊 列 用 於 存 放 正 被 發 送 或 接 收 的 每 一 個 報 文 的 標 題 信 息。 每 一 個 消 息 隊 列 還 對 應 有 一 個 數 據 結 構, 它 含 有 消 息 隊 列 的 存 取 權 限, 和 消 息 隊 列 的 當 前 狀 態 信 息 等 信 息。 消 息 隊 列 可 進 行" 發 送" 和" 接 收" 操 作。

三、 消 息 隊 列 的 編 程 要 點 及 運 作 過 程

---- 1. 消 息 隊 列 的 創 建

---- 在 報 文 能 夠 發 送 和 接 收 之 前, 必 須 創 建 一 個 能 夠 唯 一 被 識 別 出 的 消 息 隊 列 和 數 據 結 構, 這 個 被 創 建 的 唯 一 標 識 符 叫 做 消 息 隊 列 描 述 符(msqid), 用 來 識 別 或 引 用 相 關 的 消 息 隊 列 和 數 據 結 構。 用msgget(long key, int msgflg) 系 統 調 用 來 創 建 消 息 隊 列, 其 中 key 是 一 個 長 整 型, 可 由 用 戶 設 定 也 可 通 過ftok() 獲 得。msgflg 的 值 是 八 進 制 的 消 息 隊 列 操 作 權 和 控 制 命 令 的 組 合。 操 作 權 定 義 為:

操作允許權 八進位整數

用戶可讀 0400

用戶可寫 0200

同組可讀 0040

同組可寫 0020

其它可讀 0004

其它可寫 0002


---- 操 作 權 可 相 加 而 派 生, 如 用 戶 可" 讀"、" 寫" 的 權 限 為0400|0200=0600。 控 制 命 令 可 取IPC_CREAT 或IPC_EXCL。 如 果 要 創 建 一 個key=888 且 屬 主 和 同 組 可 讀 寫 的 消 息 隊 列, 執 行 以 下 系 統 調 用msgget(0x888,0660|IPC_CREAT)。 創 建 后 可 用ipcs 命 令 看 到 以 下 信 息:


IPC status from /dev/mem as of Sun

Jan 25 06:49:52 1970

T ID KEY MODE OWNER GROUP

Message Queues:

. q 7 0x00000888 --rw-rw----

root system

...


---- 它 的 消 息 隊 列 描 述 符 是7, 屬 主 是root , 同 組 是system, 存 取 權 是 屬 主、 用 戶 可 讀 寫。 如 果 執 行msgget(0x888,0660|IPC_CREAT) 時, 與0x888 對 應 的 消 息 隊 列 已 存 在, 則 返 回 該 消 息 隊 列 的 描 述 符msqid。
---- 2. 消 息 的 發 送

---- 消 息 隊 列 一 經 創 建 即 可 用msgsnd(int msqid, void *msgp, size_t msgsz, int msgflg) 發 送 消 息。msgqid 是 經msgget 創 建 的 消 息 隊 列 描 述 符,msgp 是 指 向 消 息 段 的 指 針, 該 指 針 所 指 結 構 含 有 報 文 類 型 和 要 發 送 或 接 收 的 報 文:

struct msgbuf {

long mtype;/*消息類型*/

char mtext[512];

/* 消息正文,512暫定為消息段的大小*/

}


---- msgsz 是msgp 參 量 指 向 的 數 據 結 構 中 字 符 數 組 的 長 度, 即 報 文 長 度, 最 大 值 由MSGMAX 確 定。msgflg 是 當 消 息 隊 列 滿 時( 隊 列 中 無 空 閑 空 間), 系 統 要 采 取 的 行 動. 如 果msgflg&IPC_NOWAIT= 真, 調 用 進 程 立 即 返 回, 不 發 送 該 消 息。 如 果msgflg&IPC_NOWAIT= 假, 調 用 進 程 暫 停 執 行, 處 於" 掛 起" 狀 態, 且 不 發 送 該 消 息。 直 到 下 列 情 況 之 一 出 現:
---- - 引 起 暫 停 的 條 件 不 再 存 在, 如 隊 列 出 現 空 閑, 即 可 發 送
---- - 該 消 系 隊 列 被 從 系 統 中 刪 去
---- - 調 用 進 程 接 收 到 一 個 要 捕 捉 的 信 號, 如 中 斷 信 號, 此 時 不 發 送 消 息, 調 用 進 程 按signal 中 描 述 的 方 式 執 行。

---- 如 果msgsnd 返 回0 則 發 送 成 功。 返 回-1 則 表 示 發 送 失 敗, 錯 誤 類 型 可 具 體 查 看errno。

---- 3. 消 息 的 接 收

---- 用 msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) 系 統 調 用 從 msqid 消 息 隊 列 中 讀 取 一 條 信 息 並 將 其 放 入 消 息 段 指 針msgp 指 向 的 結 構。msgsz 給 出mtext 的 字 節 數, 如 果 所 接 收 的 消 息 比msgsz 大 且msgflg&MSG_NOERROR 為 真, 則 按msgsz 的 大 小 截 斷 而 不 通 知 調 用 進 程。msgtyp 指 定 要 求 的 消 息 類 型:

---- msgtyp=0 接 收 消 息 隊 列 中 的 第 一 個 報 文
---- msgtyp >0 接 收 消 息 隊 列 中 的 類 型 為msgtyp 的 第 一 個 報 文
---- msgtyp< 0 接 收 消 息 隊 列 中 小 於 等 於msgtyp 絕 對 值 的 最 低 類 型 的 第 一 個 報 文

---- 當 隊 列 上 沒 有 所 期 望 類 型 的 消 息 或 消 息 隊 列 為 空 時msgflg 指 出 調 用 進 程 要 采 取 的 行 動: 如 果msgflg&IPC_NOWAIT 為 真, 則 調 用 進 程 立 即 結 束 並 返 回-1。 如msgflg&IPC_NOWAIT 為 假, 則 調 用 進 程 暫 停 執 行 直 至 出 現:

---- - 隊 列 中 放 入 所 需 類 型 的 消 息, 調 用 進 程 接 收 該 消 息
---- -msqid 消 息 隊 列 從 系 統 中 刪 除
---- - 調 用 進 程 接 收 到 捕 獲 的 信 號, 此 時 不 接 收 消 息, 調 用 進 程 按signal 描 述 的 方 式 執 行。

---- 如 果msgrev 執 行 成 功, 則 返 回 放 入mtext 中 的 字 節 數, 失 敗 返 回-1 , 錯 誤 類 型 可 查errno。

---- 4. 消 息 隊 列 的 控 制 和 撤 銷

---- 用 msgctl(int msqid, int cmd, struct msqid_ds *buf) 系 統 調 用 實 現 對 消 息 隊 列 的 控 制。msgqid 必 須 是 用msgget 創 建 的 消 息 隊 列 描 述 符。cmd 可 以 是:

---- IPC_STAT 查 看 消 息 隊 列 的 狀 態, 結 果 放 入buf 指 針 指 向 的 結 構
---- IPC_SET 為 消 息 隊 列 設 置 屬 主 標 識, 同 組 標 識, 操 作 允 許 權, 最 大 字 節 數
---- IPC_RMID 刪 除 指 定 的msqid 以 及 相 關 的 消 息 隊 列 和 結 構

四、 編 程 示 例

---- 下 面 給 出 一 個 運 用 消 息 隊 列, 實 現 進 程 通 信 的 實 例。 以 下 程 序 在 IBM RS/6000 小 型 機(AIX 操 作 系 統) 上 和IBM PC(UNIX 操 作 系 統) 上 分 別 調 試 通 過。 該 程 序 主 要 模 擬 根 據 帳 號 查 詢 余 額 的 過 程。 包 括 三 方 面:

請 求 進 程 從 標 准 輸 入 讀 入 帳 號, 並 將 該 帳 號 通 過 消 息 隊 列 發 送 給 服 務 進 程;

服 務 進 程 接 收 該 帳 號 后, 按 照 請 求 的 先 后 順 序 在 標 准 輸 入 上 輸 入 該 帳 戶 的 姓 名 和 余 額, 並 將 結 果 返 回 給 客 戶 進 程;

請 求 進 程 接 收 返 回 的 信 息, 並 將 結 果 輸 出 在 標 准 輸 出 上。
---- 服 務 進 程(msgcenter) 先 於 請 求 進 程(msgreq) 啟 動. 客 戶 進 程 啟 動 時 要 攜 帶 請 求 編 號, 可 同 時 起 動 多 個 請 求 進 程。


/*請求方程序msgreq.c*/

#include

#include

#include

#include

#include

static struct msgbuf1{

long mtype;

char mtext[100];

} sndbuf, rcvbuf, *msgp ;

extern int errno;

main(int argc, char **argv)

{ int rtrn, msqid ;

char name[10];

double balance;

if (argc!=2){ fprintf(stderr,

"msgreq [01-99]\n"); exit(-1); }

if ( (msqid = msgget(0x888, IPC_CREAT|0660)) == -1 ){

fprintf(stderr, "msgget 888 failed !\n"); exit(-1);

}

msgp=&sndbuf;

sprintf(sndbuf.mtext,"%2.2s",argv[1]);

printf("輸入4位帳號:");

scanf("%s",&sndbuf.mtext[2]);

sndbuf.mtext[6]=0;

msgp->mtype=666;

rtrn=msgsnd(msqid,msgp, strlen(sndbuf.mtext), 0);

if (rtrn==-1){

perror("msgsnd"); exit(-1);

}

msgp=&rcvbuf;

fprintf(stderr,"等待後台數據處理進程的回答....");

rtrn=msgrcv(msqid,msgp, 100, atoi(argv[1]), 0);

if(rtrn==-1){ perror("msgrcv"); exit(-1); }

sscanf(rcvbuf.mtext,"%[^|]|%lf",name,&balance);

printf("\n姓名=%s\n",name);

printf("餘額=%lf\n",balance);

}

/*服務方程序msgcenter.c*/

#include

#include

#include

#include

#include

static struct msgbuf1{

long mtype;

char mtext[100];

} sndbuf, rcvbuf , *msgp;

extern int errno;

main()

{ int rtrn, msgqid ;

char strbuf[100];

if ( (msqid = msgget(0x888, IPC_CREAT|0600)) == -1 ){

fprintf(stderr, "msgget 888 failed !\n"); exit(-1);

}

while(1) {

msgp=&rcvbuf;

fprintf(stderr,"等待前台進程的請求....");

rtrn=msgrcv(msqid, msgp, 100, 666 ,MSG_NOERROR);

if(rtrn==-1){ perror("msgrcv");exit(-1); }

msgp=&sndbuf;

sprintf(strbuf,"%2.2s\0",rcvbuf.mtext);

msgp->mtype=atoi(strbuf);

printf("\n輸入帳號=%4.4s的帳戶姓名:",&rcvbuf.mtext[2]);

scanf("%s",sndbuf.mtext);

strcat(sndbuf.mtext,"|");

printf("輸入該帳戶餘額:");

scanf("%s",strbuf);

strcat(sndbuf.mtext,strbuf);

rtrn=msgsnd(msqid,msgp, strlen(sndbuf.mtext), 0);

if (rtrn==-1){ perror("msgsnd"); exit(-1); }

}

}





[火星人 via ] UNIX環境下如何應用消息隊列已經有393次圍觀

http://www.coctec.com/docs/unix/show-post-74002.html