【進程間通信】常用方式彙總

来源:https://www.cnblogs.com/-Donge/archive/2022/10/19/16804858.html
-Advertisement-
Play Games

【進程間通信】常用方式彙總 隨著我們的進程越來越多,難免不同進程之間要互相傳輸一些數據,那麼這個時候該怎麼辦呢? 下麵主要簡單瞭解一下,**進程間通信(InterProcess Communication,IPC)**的幾種實現方式! 1、管道模型 管道模型與軟體生命周期模型——瀑布模型(Water ...


【進程間通信】常用方式彙總

隨著我們的進程越來越多,難免不同進程之間要互相傳輸一些數據,那麼這個時候該怎麼辦呢?

下麵主要簡單瞭解一下,進程間通信(InterProcess Communication,IPC)的幾種實現方式!

 

1、管道模型

管道模型與軟體生命周期模型——瀑布模型(Waterfall Model)很相似。

所謂的瀑布模型,其實就是將整個軟體開發過程分成多個階段,往往是上一個階段完全做完,才將輸出結果交給下一個階段。

image-20220922134815245

還記得咱們最初學 Linux 命令的時候,有下麵這樣一行命令:

ps -ef | grep 關鍵字 | awk '{print $2}' | xargs kill -9

這裡面的豎線“|”就是一個管道。它會將前一個命令的輸出,作為後一個命令的輸入。

從管道的這個名稱可以看出來,管道是一種單向傳輸數據的機制,它其實是一段緩存,裡面的數據只能從一端寫入,從另一端讀出。如果想互相通信,我們需要創建兩個管道才行。

管道又可以分為匿名管道和命名管道!

 

1.1 匿名管道

如上命令:

ps -ef | grep 關鍵字 | awk '{print $2}' | xargs kill -9

匿名管道:用"|” 表示的管道,意思就是這個類型的管道沒有名字,用完了就銷毀了。豎線代表的管道隨著命令的執行自動創建、自動銷毀。用戶甚至都不知道自己在用管道這種技術,就已經解決了問題。

 

1.2 命名管道

命名管道,這個類型的管道需要通過 mkfifo 命令顯式地創建。

mkfifo donge		#建立一個管道

donge就是這個管道的名稱。管道以文件的形式存在,這也符合 Linux 裡面一切皆文件的原則。

 

下麵我們看一下文件類型

ls -l
prw-rw-r-- 1 dong dong     0 Sep 28 17:09 donge

可以看到,這個文件的類型是 p,就是 pipe 的意思。

 

往管道中寫入數據

echo "hello world" > donge

這個時候,管道裡面的內容沒有被讀出,這個命令就是停在這裡的,即進程被堵塞。

這說明當一個項目組要把它的輸出交接給另一個項目組做輸入,當沒有交接完畢的時候,前一個項目組是不能撒手不管的。

重新打開一個終端,讀出管道數據

cat < hello 
hello world

一方面,我們能夠看到,管道裡面的內容被讀取出來,列印到了終端上;

另一方面,echo 那個命令正常退出了,也即交接完畢,前一個項目組就完成了使命,可以解散了。

 

管道通信,我們可以看出,瀑布模型的開發流程效率比較低下,因為團隊之間無法頻繁地溝通。而且,管道的使用模式,也不適合進程間頻繁的交換數據

 

2、消息隊列

image-20220922135603302

消息隊列可以理解為發郵件,每一封郵件都視為一個獨立的數據單元,也就是消息體,每個消息體都是固定大小的存儲塊,在位元組流上不連續

這個消息結構的定義我寫在下麵了。這裡面的類型 type 和正文 text 沒有強制規定,只要消息的發送方和接收方約定好即可。

struct msg_buffer {
    long mtype;
    char mtext[1024];
};

 

2.1 創建消息隊列

消息隊列的創建,需要用到msgget函數

int msgget(key_t key, int msgflg);
  • key:該參數是消息隊列的唯一標識,由ftok生成。

  • msgflg:取值有以下幾個選擇:IPC_CREATIPC_EXCL ,這兩個參數詳細的作用可以man msgflg看詳細介紹。

  • 返回值:返回一個近乎唯一的Message queue id

 

那麼,key是如何由ftok生成的呢?

我們可以指定一個文件,調用ftok ,它會根據這個文件的 inode,生成一個近乎唯一的 key

key_t ftok(const char *pathname, int proj_id);
  • pathname:文件信息,必須指定在一個存在的,可訪問的文件
  • proj_id8bit的數據,0-255隨意設定

這樣就可以獲得一個近乎唯一的key了!

 

只要在這個消息隊列的生命周期內,這個文件不要被刪除就可以了。只要不刪除,無論什麼時刻,再調用 ftok,也會得到同樣的 key。

 

綜上,創建一個消息隊列只需兩步:

①:ftok生成一個key

②:msgget生成一個消息隊列的ID

如下:

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
 
 
int main() {
  int messagequeueid;
  key_t key;
 
 
  if((key = ftok("/root/messagequeue/messagequeuekey", 1)) < 0)
  {
      perror("ftok error");
      exit(1);
  }
 
 
  printf("Message Queue key: %d.\n", key);
 
 
  if ((messagequeueid = msgget(key, IPC_CREAT|0777)) == -1)
  {
      perror("msgget error");
      exit(1);
  }
 
 
  printf("Message queue id: %d.\n", messagequeueid);
}

ftok要指定一個存在的文件,所以我們在執行之前,需要創建該文件。

 

查看消息隊列

System V IPC 體系有一個統一的命令行工具:ipcmkipcsipcrm 用於創建、查看和刪除 IPC 對象。

查看創建的IPC對象:ipcs -q

dong@ubuntu:~//Interprocess_Communication$ ipcs 

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x01110005 0          dong       777        0            0           

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      

------ Semaphore Arrays --------
key        semid      owner      perms      nsems

 

2.2 發送消息

消息隊列發送消息,主要調用msgsnd 函數

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
  • msqid:該參數是msgget所得到的message queueid
  • msgp:消息結構體
struct msg_buffer {
    long mtype;
    char mtext[1024];
};
  • msgsz:表示消息結構體中,mtext最大長度
  • msgflg:一位掩碼,可取值有:IPC_NOWAITMSG_COPYMSG_EXCEPTMSG_NOERROR,取值說明可見man msgsnd

 

2.3 接收消息

消息隊列接收消息,主要調用msgrcv 函數

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);
  • msqid:該參數是msgget所得到的message queueid
  • msgp:消息結構體
  • msgsz:可接收數據最大長度
  • msgflg:一位掩碼,可取值有:IPC_NOWAITMSG_COPYMSG_EXCEPTMSG_NOERROR,取值說明可見man msgsnd

 

有了消息這種模型,兩個進程之間的通信就像咱們平時發郵件一樣,你來一封,我回一封,可以頻繁溝通了。

 

3、共用記憶體

image-20220924172026172

怎麼理解共用記憶體呢?

我們知道每個進程都有自己獨立的虛擬記憶體空間不同的進程的虛擬記憶體空間映射到不同的物理記憶體中去。這個進程訪問 A 地址和另一個進程訪問 A 地址,其實訪問的是不同的物理記憶體地址,對於數據的增刪查改互不影響。

 

但是,咱們是不是可以變通一下,拿出一塊虛擬地址空間來,映射到相同的物理記憶體中。這樣這個進程寫入的東西,另外一個進程馬上就能看到了,都不需要拷貝來拷貝去,傳來傳去。

 

相比於消息隊列,共用記憶體的優勢在哪裡呢?

  • 大數據傳輸:如果批量的大數據進行傳輸,使用郵件的方式,來去發送不及時,並且大小也有限制
  • 實時性:用共用記憶體,其可以大大節省通信時間

 

3.1 創建共用記憶體

我們可以創建一個共用記憶體,調用 shmget

int shmget(key_t key, size_t size, int shmflg);
  • key:和 msgget 裡面的 key 一樣,都是唯一定位一個共用記憶體的對象
  • size:共用記憶體的大小
  • shmflg:其值可以取:IPC_CREATIPC_EXCLSHM_HUGETLBSHM_HUGE_2MB

返回值:共用記憶體的唯一ID

 

創建完畢之後,我們可以通過 ipcs 命令查看這個共用記憶體。

#ipcs --shmems
 
------ Shared Memory Segments ------ ­­­­­­­­
key        shmid    owner perms    bytes nattch status
0x00000000 19398656 marc  600    1048576 2      dest

 

3.2 訪問共用記憶體

接下來,如果一個進程想要訪問這一段共用記憶體,需要將這個記憶體載入到自己的虛擬地址空間的某個位置,通過 shmat 函數,就是 attach 的意思。

void *shmat(int shmid, const void *shmaddr, int shmflg);
  • shmid:標識一個共用記憶體段的唯一ID
  • shmaddr:就是要指定 attach 到這個地方。但是這個地址的設定難度比較大,除非對於記憶體佈局非常熟悉,否則可能會 attach到一個非法地址。所以,通常的做法是將 shmaddr設為 NULL,讓內核選一個合適的地址。
  • shmflg:一位掩碼,可取值:SHM_EXECSHM_RDONLYSHM_REMAP

返回值:為所連接的實際地址

 

3.3 關閉共用記憶體

如果共用記憶體使用完畢,可以通過 shmdt 解除綁定,然後通過 shmctl,將 cmd 設置為 IPC_RMID,從而刪除這個共用記憶體對象。

int shmdt(void *addr); 
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmdt的參數addr:為shmat的返回值,表示卸載一片共用記憶體

shmctl的參數:

  • shm_idshmget的返回值,為共用記憶體的唯一ID
  • cmd:取值有:IPC_STATIPC_RMID等,見:man shmctl
  • buf:共用記憶體管理結構體。

 

3.4 信號量

這裡你是不是有一個疑問,如果兩個進程 attach 同一個共用記憶體,大家都往裡面寫東西,很有可能就衝突了。例如兩個進程都同時寫一個地址,那先寫的那個進程會發現內容被別人覆蓋了。

 

所以,這裡就需要一種保護機制,使得同一個共用的資源,同時只能被一個進程訪問。在 System V IPC 進程間通信機制體系中,早就想好了應對辦法,就是信號量(Semaphore。因此,信號量和共用記憶體往往要配合使用。

 

信號量和共用記憶體都比較複雜,兩者還要結合起來用,就更加複雜,它們內核的機制就更加複雜。這一節我們先不講。

 

4、信號

上面講的進程間通信的方式,都是常規狀態下的工作模式,對應到咱們平時的工作交接,收發郵件、聯合開發等,其實還有一種異常情況下的工作模式

例如出現線上系統故障,這個時候,什麼流程都來不及了,不可能發郵件,也來不及開會,所有的架構師、開發、運維都要被通知緊急出動。所以,7 乘 24 小時不間斷執行的系統都需要有告警系統,一旦出事情,就要通知到人,哪怕是半夜,也要電話叫起來,處理故障。

信號可以在任何時候發送給某一進程,進程需要為這個信號配置信號處理函數。

Linux所支持的異常信號如下:

 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

當某個信號發生的時候,就預設執行這個函數就可以了。這就相當於咱們運維一個系統應急手冊,當遇到什麼情況,做什麼事情,都事先準備好,出了事情照著做就可以了。

有點類似於異常中斷……

OK,這一篇,我們整體講解了一下進程間通信的幾種方式,現在我們來回顧一下:

  • 類似瀑布開發模型的管道
  • 類似郵件模式的消息隊列
  • 類似會議室聯合開發的共用記憶體加信號量
  • 類似應急預案的信號

Peek 2022-09-25 22-04


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

-Advertisement-
Play Games
更多相關文章
  • 我們都知道通過servlet跳轉到具體的視圖有兩種方式,一種方式是重定向,一種方式請求轉發。那麼在SpringMVC中視圖解析後跳轉是哪種方式實現的呢?很明顯在我們發起請求時地址欄內的地址是沒有發生變化的,所以SpringMVC預設是採用請求轉發的方式進行跳轉,那麼我們是否可以通過重定向的方式到自己 ...
  • Java 硬核學習指南 從零基礎進階大廠 | Java2Top by 小龍coding。 原創不易,請勿抄襲,違者必究! 背景故事 空暇之餘,經常有很多粉絲、學弟學妹問我 "如何學習 Java,什麼時候學? 學什麼? 有沒有相關資料資源推薦? 網上各自路線太多太雜,不知道究竟學什麼? 哪裡該學哪裡可 ...
  • 現在我們來學習一下RequestMapping註解。 RequestMapping註解用來將url映射到一個控制類(Controller類)或一個特定處理的方法上。 一、RequestMapping使用的位置 1.方法上 直接上實例: package com.jms.controller; impo ...
  • 序列化類高級用法之source 使用source,欄位參數,可以修改序列化欄位名字 原本序列化器中欄位名,必須和表中的欄位名一樣,不一樣會報錯 我們可以通過source欄位來改變序列化器中的欄位名,使得前端在展示的時候也修改一下欄位名!! source也可以做跨表查詢,通過外鍵欄位,表名點外鍵出去的 ...
  • 最近真的是運氣不好,國慶前一天,隔壁小區有人中招了,結果國慶出不了門,好不容易國慶結束了,準備上班,結果小區又有個叼毛中招了,搞得我直接國慶放了半個月,還只能在家過,沒事幹只能這裡寫寫,那裡弄弄,於是今天就把存貨分享給大家了~ 一、什麼是PyQt6? 簡單介紹一下PyQt6 1、基礎簡介 PyQt6 ...
  • 什麼是代理模式 代理模式是常用的java設計模式,在Java中我們通常會通過new一個對象再調用其對應的方法來訪問我們需要的服務。代理模式則是通過創建代理類(proxy)的方式間接地來訪問我們需要的服務。 舉一個生活中的例子:像我們在網上通過中介租到其背後房東的房子,因為房東也嫌麻煩想省事,此時中介 ...
  • 簡介 上一篇文章講解瞭如何使用Azure DevOps持續部署應用到Azure Kubernetes上。但是部署是否成功?會不會遇到什麼問題?項目運行中是否會出現問題?我們該怎麼樣查看這些問題,並且對問題進行針對性解決?這就是今天要講的。 Aks_Core部署流程 在講遇到的問題之前我們可以先看看如 ...
  • 一:背景 1.講故事 最近遇到了好幾起和 COM 相關的Dump,由於對 COM 整體運作不是很瞭解,所以分析此類dump還是比較頭疼的,比如下麵這個經典的 COM 調用棧。 0:044> ~~[138c]s win32u!NtUserMessageCall+0x14: 00007ffc`5c891 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...