進程間通信(二)—消息隊列

来源:http://www.cnblogs.com/lenomirei/archive/2016/07/05/5642575.html
-Advertisement-
Play Games

我會用幾篇博客總結一下在Linux中進程之間通信的幾種方法,我會把這個開頭的摘要部分在這個系列的每篇博客中都打出來 進程之間通信的方式 管道 消息隊列 信號 信號量 共用存儲區 套接字(socket) 進程間通信(一)—管道傳送門:http://www.cnblogs.com/lenomirei/p ...


我會用幾篇博客總結一下在Linux中進程之間通信的幾種方法,我會把這個開頭的摘要部分在這個系列的每篇博客中都打出來

進程之間通信的方式

  • 管道
  • 消息隊列
  • 信號
  • 信號量
  • 共用存儲區
  • 套接字(socket)

進程間通信(一)—管道傳送門:http://www.cnblogs.com/lenomirei/p/5636339.html

進程間通信(三)—信號量傳送門:http://www.cnblogs.com/lenomirei/p/5649792.html

進程間通信(四)—共用存儲區傳送門:http://www.cnblogs.com/lenomirei/p/5651995.html

這次主要寫的是消息隊列,之前講過的管道和消息隊列在本質上就有很大的區別,管道是一個文件,而消息隊列是一個數據結構(類似於鏈表)。這說明瞭,管道文件是存放在磁碟上的,關機也會存在(尤其是命名管道更為顯而易見,你不刪除他他就擱那呆著),而消息隊列是存在於內核中的記憶體,顯而易見,關機就沒了。

更關鍵的是,記憶體他快呀,比磁碟I/O快多了,為啥要用那麼慢的管道。而且消息隊列是可以直接完成沒有親緣關係的進程之間的通信的。但是結構比起管道要複雜,用到了很多結構體。內容有些多。先寫一下和管道的主要區別,可以更直觀的進行對比

  • 匿名管道是跟隨進程的,消息隊列是跟隨內核的,也就是說進程結束之後,匿名管道就死了,但是消息隊列還會存在(除非顯示調用函數銷毀)
  • 管道是文件,存放在磁碟上,訪問速度慢,消息隊列是數據結構,存放在記憶體,訪問速度快
  • 管道是數據流式存取,消息隊列是數據塊式存取

那麼從頭開始吧:

 

  • 如何創建一個消息隊列

在C庫函數中有一個系統調用可以創建一個消息隊列,那就是msgget,跟這個函數有關的其他函數也會一併給出來

  • 函數原型: int msgget(key_t key, int msgflg)
  • 頭文件:#include <sys/types.h> #include <sys/ipc.h>  #include <sys/msg.h>
  • 參數解析
    • 第一個參數是一個標識數據結構唯一的key鍵值,可以給IPC_PRIVATE讓內核自動給,也可以自己調用ftok函數綁定一個
    • 第二個參數是創建消息隊列的參數,有IPC_CREAT 和 IPC_EXCL
      • 單獨使用IPC_CREAT,如果該消息隊列已經存在(就是該key_t對象已經拿去被創建過一個隊列了),打開該隊列並返回,如果不存在,就創建一個返回
      • 單獨使用IPC_EXCL沒有意義
      • 兩個參數一起使用(IPC_CREAT | IPC_EXCL),如果該隊列存在,出錯返回,如果不存在創建一個返回,也就是說這樣使用一定會獲得一個新隊列
  • 返回值,成功返回標誌消息隊列的唯一的一個int,失敗返回-1

 

  • 函數原型:key_t ftok(const char *pathname,int proj_id)
  • 頭文件:#include <sys/types.h>  #include <sys/ipc.h>
  • 參數解析
    • 沒啥好解析的,第一個參數就是給個路徑(目錄)就成了
    • 第二個就是給個int值,沒啥特殊要求,ftok本質就是把這個proj_id和pathname綁定在一起罷了

相關函數就這麼多,到這裡就可以開心的創建一個新的消息隊列了,那麼怎麼看我們創建出來的消息隊列呢?怎麼銷毀他呢?

先用命令看一下,使用ipcs -q就可查看消息隊列的狀態,這裡我創建了一個消息隊列,下麵來銷毀它

使用ipcrm -q msqid就可以銷毀一個消息隊列,我已經把剛纔創建的消息隊列銷毀了

那麼如何通過C函數銷毀一個消息隊列?使用msgctl函數

  • 函數原型: int msgctl(int msgid ,int cmd ,struct msgid_ds *buf)
  • 頭文件:#include <sys/types.h>  #include <sys/ipc.h>  #include <sys/msg.h>
  • 參數解析
    • 第一個參數就是創建好的標識msg的int變數
    • 第二個參數有很多,這裡介紹兩個,先說主要的銷毀,IPC_RMID,設置了這個之後第三個參數就沒必要設置了給個0就成
    • 第二個參數設置成IPC_SET的時候表示對消息隊列進行初始化,這時第三個參數就有用了,但是我這裡沒用到就不話費篇幅詳細解釋了,貼出結構看看就成

這裡提一下,消息隊列的一個缺點,這裡說到IPC_SET對消息隊列的初始化,就是說消息隊列的創建和初始化是分開的,這樣設計不是很好,因為有線程安全的問題,當一個線程創建了消息隊列還沒初始化,另一個線程直接開始執行訪問的操作就很尷尬了

這裡說錯了!我把信號量的缺點說成是消息隊列的了!現在取消了,真是對不起!!!

 1 struct msginfo {
 2                       int msgpool; /* Size in kibibytes of buffer pool
 3                                       used to hold message data;
 4                                       unused within kernel */
 5                       int msgmap;  /* Maximum number of entries in message
 6                                       map; unused within kernel */
 7                       int msgmax;  /* Maximum number of bytes that can be
 8                                       written in a single message */
 9                       int msgmnb;  /* Maximum number of bytes that can be
10                                       written to queue; used to initialize
11                                       msg_qbytes during queue creation
12                                       (msgget(2)) */
13                       int msgmni;  /* Maximum number of message queues */
14                       int msgssz;  /* Message segment size;
15                                       unused within kernel */
16                       int msgtql;  /* Maximum number of messages on all queues
17                                       in system; unused within kernel */
18                       unsigned short int msgseg;
19                                    /* Maximum number of segments;
20                                       unused within kernel */
21                   };

 

  • 使用消息隊列

提到使用消息隊列無非就是消息的寫入和消息的讀出,這涉及兩個函數和一個結構體

先說發送函數

  • 函數原型:int msgsnd(int msgid,const void *msgp,size_t msgsz,int msgflg)
  • 頭文件:#include <sys/types.h>  #include <sys/ipc.h>   #include <sys/msg.h>
  • 參數解析
    • 第一個還是消息隊列號
    • 第二個是一個結構體的指針,這個結構體叫做msgbuf在內核中有(應該是有的。。。沒有就自己寫一個放程式里),必要的,這個就是傳輸數據的數據塊,沒了它就沒得傳了,可不能隨便設置為0
    • 第三個就是傳輸的消息長度(一般你的數據有多長就寫多長,不是msgbuf對象的總大小)
    • 第四個是傳送方式(無阻塞啊什麼的)的參數,我的程式沒有使用所以我設置為0了

在列接受函數之前必須把結構體放出來,不然下麵就懵逼了

1 struct msgbuf {
2                long mtype;       /* message type, must be > 0 */
3                char mtext[1];    /* message data */
4            };

mtype講起,因為隊列是好多進程(或者說是線程)都拿來用的,比如4個進程共用一個消息隊列,兩兩之間進行通信,就有必要標識哪兩個進程是一組的,哪塊數據是屬於你們組的,不能亂拿是不是,這個mtype就是用來標識的,但是一定要大於0,我一開始設置為0一直報參數錯誤,查了文檔才發現自己蠢了。。。

mtext存放的就是你要傳輸的數據了,怎麼大小隻有1?當然不能只有1,我自己寫的msgbuf就有1024長度。。。所以還是自己寫爽啊

 可以說接受函數了

  • 函數原型:ssize_t msgrcv(int msgid,const void *msgp,size_t msgsz,long msgtyp,int msgflg)
  • 頭文件同msgsnd函數
  • 參數解析
    • 只用看標紅部分就成了,其他的和msgsnd一樣
    • msgtyp就是用來表示我(當前進程)拿數據的時候,只拿(msgbuf對象中的mtype)和我傳入的msgtyp一樣的數據塊(msgbuf對象)
  •  成功返回長度,失敗返回-1

 

事已至此,基本操作就說完了,廢話少說,show me the code

我的程式分為comm.h(公共頭文件)  comm.c(封裝基本函數) server.c(簡易伺服器端) client.c(簡易客戶端)一共4個文件

完成了伺服器端和客戶端的簡單通信(回合制聊天(誤))

comm.h

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <sys/msg.h>
 4 #include <sys/types.h>
 5 #include <sys/ipc.h>
 6 #include <string.h>
 7 #include <stdlib.h>
 8 #include <errno.h>
 9 #include <memory.h>
10 
11 
12 #define _PATH_NAME_ "/tmp"
13 #define _PROJ_ID_ 0x666
14 #define _SIZE_ 1024
15 
16 static int comm_create_msg_set(int flags);
17 int create_msg_set();
18 int get_msg_set();
19 void destory_msg_set(int msg_id);
20 void send_msg(int msg_id,long msgtype,char * buf);
21 void receive_msg(int msg_id,long msgtype,char *buf);
22 
23 
24 
25 struct msgbuf
26 {
27   long mtype;
28   char mtext[_SIZE_];
29 };

comm.c

 1 #include "comm.h"
 2 static int comm_create_msg_set(int flags)
 3 {
 4   key_t _key=ftok(_PATH_NAME_,_PROJ_ID_);
 5   if(_key<0)
 6   {
 7     printf("%d:%s",errno,strerror(errno));
 8   }
 9   int msg_id=msgget(_key,flags);
10   if(msg_id<0)
11   {
12     printf("%d:%s",errno,strerror(errno));
13   }
14   return msg_id;
15 }
16 
17 
18 int get_msg_set()
19 {
20   key_t _key=ftok(_PATH_NAME_,_PROJ_ID_);
21   int flags=IPC_CREAT;
22   return comm_create_msg_set(flags);
23 }
24 
25 
26 int create_msg_set()
27 {
28   key_t _key=ftok(_PATH_NAME_,_PROJ_ID_);
29   int flags=IPC_CREAT | IPC_EXCL;
30   return comm_create_msg_set(flags);
31 }
32 
33 
34 void send_msg(int msg_id,long msgtype,char *buf)
35 {
36   memset(buf,'\0',strlen(buf)+1);
37   ssize_t _size=read(0,buf,_SIZE_);
38   if(_size>0)
39   {
40     buf[_size-1]='\0';
41   }
42 
43   struct msgbuf _mbuf;
44   memset(&_mbuf,'\0',sizeof(struct msgbuf));
45   _mbuf.mtype=msgtype;
46   strcpy(_mbuf.mtext,buf);
47   if(msgsnd(msg_id,&_mbuf,_size,0)<0)
48   {
49     printf("send error,%d:%s",errno,strerror(errno));
50   }
51 }
52 
53 void receive_msg(int msg_id , long msgtype ,char *buf)
54 {
55   struct msgbuf _mbuf;
56   memset(&_mbuf,'\0',sizeof(struct msgbuf));
57   _mbuf.mtype=0;
58   if(msgrcv(msg_id,&_mbuf,_SIZE_,msgtype,0)<0)
59   {
60     printf("recv error %d:%s",errno,strerror(errno));
61   }
62   strcpy(buf,_mbuf.mtext);
63 }
64 
65 void destory_msg_set(int msg_id)
66 {
67   if(msgctl(msg_id,IPC_RMID,0)<0)
68   {
69     printf("%d:%s",errno,strerror(errno));
70   }
71 }

server.c

 1 #include "comm.h"
 2 long c_type=1;
 3 long s_type=22;
 4 int main()
 5 {
 6   int msg_id=create_msg_set();
 7   char buf[_SIZE_];
 8 
 9   while(1)
10   {
11     memset(buf,'\0',sizeof(buf));
12     receive_msg(msg_id,c_type,buf);
13     if(strcasecmp(buf,"quit")==0)
14     {
15       break;
16     }
17     printf("client # %s\n",buf);
18     printf("clent say done ! Please Input:");
19     fflush(stdout);
20     memset(buf,'\0',sizeof(buf));
21     send_msg(msg_id,c_type,buf);
22   }
23   destory_msg_set(msg_id);
24   return 0;
25 }

client.c

 1 #include "comm.h"
 2 
 3 
 4 long c_type=1;
 5 long s_type=2;
 6 
 7 int main()
 8 {
 9   int msg_id = get_msg_set();
10 
11   char buf[_SIZE_];
12 
13   while(1)
14   {
15     memset(buf,'\0',sizeof(buf));
16     send_msg(msg_id,c_type,buf);
17     if(strcasecmp(buf,"quit")==0)
18     {
19       break;
20     }
21     memset(buf,'\0',sizeof(buf));
22     receive_msg(msg_id,c_type,buf);
23     printf("server # %s\n",buf);
24     printf("server say done ! Please Input:");
25     fflush(stdout);
26   }
27 
28   return 0;
29 }

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • FreeBSD用戶手冊學習筆記 freeBSD安裝:http://my.oschina.net/lsgx/blog/540980 第一章 介紹 1.1 FreeBSD歷史和簡介 FreeBSD 自身的源代碼是完全公開的,所以可以對系統進行最大程度的定製。 FreeBSD 項目的目標是無附加條件地提供 ...
  • 安裝openssh-serversudo apt-get install openssh-server 查看server是否啟動: ps -ef |grep ssh 如果看到/usr/sbin/sshd -D,說明服務已經啟動,否則服務尚未啟動,那麼需要啟動server: /etc/init.d/s ...
  • ROP的全稱為Return-oriented programming(返回導向編程),這是一種高級的記憶體攻擊技術可以用來繞過現代操作系統的各種通用防禦(比如記憶體不可執行和代碼簽名等)。雖然現在大家都在用64位的操作系統,但是想要扎實的學好ROP還是得從基礎的x86系統開始,但看官請不要著急。 ...
  • 第一部分:安裝redis 現在我們將redis安裝到此目錄 /usr/local/redis 希望將安裝包下載到此目錄 /usr/local/src 那麼安裝過程指令如下: (註:redis官網地址:http://www.redis.io/ 最新版本:3.0.6) 註意上面的最後一行,我們通過PRE ...
  • 1,伺服器系統的安裝會出現錯誤的地方一般都是在Raid 卡驅動 略過Raid 卡配置, 具體 http://jingyan.baidu.com/article/ca41422fddfd201eae99ed30.html 2.準備好2008R2 系統光碟 以下所舉例的是由"用安裝光碟引導啟動安裝"的方 ...
  • 在ASP.NET 2.0 站點根目錄下,只要存在 App_Offline.htm 文件,那麼所有對.aspx的請求都將轉向App_Offline.htm 。而且瀏覽器的地址欄顯示的是所請求的.aspx的URL。這樣當我們的站點需要維護時,只要把App_Offline.htm 拷貝到站點根目錄下即可。 ...
  • 3. 記憶體數據 前面我們知道了,記憶體是按位元組編址,每個地址的存儲單元可以存放8bit的數據。我們也知道CPU通過記憶體地址獲取一條指令和數據,而他們存在存儲單元中。現在就有一個問題。我們的數據和指令不可能剛好是8bit,如果小於8位,沒什麼問題,頂多是浪費幾位(或許按位元組編址是為了節省記憶體空間考慮)。 ...
  • 隨著Linux程式的增多,軟體的安裝過程中經常出現許多令人頭疼的問題,比如,重覆機械的勞動,今天來分享一些解決方法.. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...