linux 進程通信之 mmap

来源:https://www.cnblogs.com/xiaoshiwang/archive/2019/05/07/10823291.html
-Advertisement-
Play Games

三,mmap通信 創建記憶體映射區。 函數mmap:打開一個文件,指定一個文件的區域,作為一個區域,映射到記憶體中,以後就直接操作那個記憶體,就能夠實現進程間的通信。因為是記憶體操作,所以速度最快。 addr:固定NULL length:拿出文件中的多長的一段,映射到記憶體。 offset:從文件內容中的哪個 ...


一,管道PIPE

二,FIFO通信

三,mmap通信

創建記憶體映射區。

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *addr, size_t length);

函數mmap:打開一個文件,指定一個文件的區域,作為一個區域,映射到記憶體中,以後就直接操作那個記憶體,就能夠實現進程間的通信。因為是記憶體操作,所以速度最快。

  • addr:固定NULL
  • length:拿出文件中的多長的一段,映射到記憶體。
  • offset:從文件內容中的哪個位置開始拿。
  • prot
    • PROT_EXEC Pages may be executed
    • PROT_READ Pages may be read.
    • PROT_WRITE Pages may be written.
    • PROT_NONE Pages may not be accessed
  • flags
    • MAP_SHARED:對記憶體里的值進行修改,會反映到文件,也就是文件也被修改。
    • MAP_PRIVATE:對記憶體里的值進行修改,不會反映到文件,文件不會被修改。
  • offset:起始位置
  • 返回值
    • 成功:可用的記憶體的首地址
    • 失敗:MAP_FAILED (that is, (void *) -1)

釋放記憶體映射區。

#include <sys/mman.h>
int munmap(void *addr, size_t length);
  • addr:mmap的返回值
  • length:mmap創建的長度
  • 返回值:成功0;失敗-1.

例子:

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(){
  int fd = open("mem", O_RDWR);
  //char* buf = mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  char* buf = mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
  printf("%s\n", buf);
  strcpy(buf, "FFFFF");
  //釋放映射區
  munmap(buf, 8);
  close(fd);
}

mmap的七個問題:

  • 如果更改上面例子里變數buf的地址,釋放的時候munmap,還能成功嗎?

    不能成功。錯誤信息:【Invalid argument】

  • 對映射區的操作,越界了會怎麼樣。

    • open文件size > 要寫入的size > mmap參數的length:能夠全部寫入文件。
    • open文件size < 要寫入的size > mmap參數的length:不能全部寫入文件,能夠寫入的size是open文件的size
  • 偏移量隨便填個數字會怎麼樣。

    mmap函數出錯,錯誤信息:【Invalid argument】

    offset必須是4K的整數倍,【0,4*1024。。。】

    用【stat】命令查看文件,發現文件的size實際小於4096,但是【IO Block: 4096】

      File: pi2.c
      Size: 442           Blocks: 8          IO Block: 4096   regular file
    Device: 801h/2049d    Inode: 424247      Links: 1
    Access: (0664/-rw-rw-r--)  Uid: ( 1000/      ys)   Gid: ( 1000/      ys)
    Access: 2019-05-02 12:54:13.812282158 +0800
    Modify: 2019-04-29 13:49:42.489004001 +0800
    Change: 2019-04-29 13:49:42.489004001 +0800
  • 如果文件描述符先關閉,對mmap映射有沒有影響。

    沒有影響。

  • open的時候,可以用新創建一個文件的方式,來創建映射區嗎?

    錯誤:Bus error (core dumped)。

    錯誤理由是:創建的文件的size為0,所以出上面的錯誤。新創建一個文件後,馬上把文件大小擴展一下就不會發生錯誤了。

    int fd = open("mem", O_RDWR|O_CREAT, 0666);
    ftruncate(fd, 8);//把新創建的文件mem的大小擴展為8.
  • open文件時,選擇O_WRONLY,可以嗎

    mmap函數出錯,錯誤:【Permission denied】。

    因為要把文件的內容讀到記憶體,所以隱含一次讀取操作,所以沒有讀的許可權的話,就出這個錯誤。

  • 當選擇MAP_SHARED的時候,open文件選擇O_RDONLY,prot可以選擇【PROT_READ|PROT_WRITE】嗎

    mmap函數出錯,錯誤:【Permission denied】。

    MAP_SHARED的時候會去寫文件,但是open的時候只有讀許可權,所以許可權不夠。

用mmap實現父子進程間通信的例子:

註意:參數flags必須是MAP_SHARED,因為2個進程間通信,需要互相讀寫,所以必須是MAP_SHARED

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h>

int main(){
  int fd = open("mem", O_RDWR);
  int* mem = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  if(mem == MAP_FAILED){
    perror("mmap");
    return -1;
  }
  pid_t pid = fork();

  if(pid == 0){
    *mem = 100;
    printf("child:mem=%d\n", *mem);
    sleep(3);
    printf("child:mem=%d\n", *mem);
  }
  else if(pid > 0){
    sleep(1);
    printf("parent:mem=%d\n", *mem);
    *mem = 200;
    printf("parent:mem=%d\n", *mem);
    wait(NULL);
  }

  munmap(mem, 4);
  close(fd);
}

執行結果:

child:mem=100
parent:mem=100
parent:mem=200
child:mem=200

不知道讀者同學們發現了沒有,用mmap有個非常雞肋的地方,就是必須要使用一個文件,其實這個文件對程式沒有什麼作業。所以linux給我們提供了一個方法,叫做【匿名映射】。

匿名映射:在調用mmap函數時候,在flags參數那裡,設置【MAP_ANON】,併在fd參數那裡設置【-1】。

int* mem = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);

有個問題,在有些unix系統里是沒有【MAP_ANON】【MAP_ANONYMOUS】這2個巨集的,這2個巨集的作用是一樣的,其中一個是簡寫。那怎麼辦呢?

使用下麵2個文件去映射,因為要用文件,所以必須還得有open的調用,但好處是不用事先做出一個大小合適的文件了。

  • /dev/zero:可以隨意映射,size無限大,諢名叫【聚寶盆】
  • /dev/null:可以隨意映射,size無限大,但映射完後,文件里不會存有任何內容,所以也被叫成【無底洞】,一般錯誤日誌太多,而且不想保留的時候,會重定向到這個文件。

匿名映射的弱點:不能實現無學員關係進程間的通信。

用mmap實現無血緣關係的進程間通信的例子:

寫入端:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h>

typedef struct _student{
  int id;
  char name[20];
}Student;

int main(int argc, char* argv[]){

  int fd = open("aaa", O_RDWR|O_TRUNC|O_CREAT, 0666);
  int length = sizeof(Student);
  ftruncate(fd, length);
  Student* std = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  
  if(std == MAP_FAILED){
    perror("mmap");
    return -1;
  }

  int num = 0;
  while(1){
    std->id = num;
    sprintf(std->name, "xiaoming-%03d", num++);
    sleep(1);
  }

  munmap(std, length);
  close(fd);
}

讀入端:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h>

typedef struct _student{
  int id;
  char name[20];
}Student;

int main(int argc, char* argv[]){

  int fd = open("aaa", O_RDWR);
  int length = sizeof(Student);
  Student* std = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);


  if(std == MAP_FAILED){
    perror("mmap");
    return -1;
  }
  
  while(1){
    printf("id:%03d, name:%s\n", std->id, std->name);
    sleep(1);
  }
  

  munmap(std, length);
  close(fd);
}

利用mmap實現用多個進程拷貝一個文件的例子(代碼還沒寫完)

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

//argv[1]:process count
//argv[2]:src file
int main(int argc, char* argv[]){
  if(argc < 3){
    printf("bad argv,need 3 arg");
    return -1;
  }

  struct stat sbuf;
  int ret = stat(argv[2], &sbuf);
  if(ret < 0){
    perror("stat");
    return -1;
  }

  off_t sz = sbuf.st_size;

  off_t yu = sz % atoi(argv[1]);  
  off_t proSz = (sz - yu) / atoi(argv[1]);
  printf("proSz:%ld, yu:%ld\n", proSz, yu);

  //open src file
  int srcfd = open(argv[2], O_RDONLY);

  //create target file
  char wk[20] = {0};
  sprintf(wk, "%s.copy", argv[2]);
  int decfd = open(wk,O_RDWR|O_CREAT|O_TRUNC, 0666);
  ret = ftruncate(decfd, sz);
  if(ret < 0){
    perror("ftruncate");
    return -1;
  }

  void* vc[atoi(argv[1])];
  for(int i = 0; i < atoi(argv[1]); ++i){
    vc[i] = mmap(NULL, proSz, PROT_READ|PROT_WRITE, MAP_SHARED, decfd, 0);
    if(vc[i] == MAP_FAILED){
      perror("mmap die:");
      return -1;
    }
  }

  close(srcfd);
  close(decfd);
}

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

-Advertisement-
Play Games
更多相關文章
  • TimeSpan的屬性和方法: 下麵的列表涵蓋了其中的一部分: 屬性: Add:與另一個TimeSpan值相加。 Days: 返回用天數計算的TimeSpan值。Hours: 返回用小時計算的TimeSpan值Milliseconds: 返回用毫秒計算的TimeSpan值。Minutes: 返回用分 ...
  • 視頻與PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-02.md 作者是 Immo Landwerth(https://twitter.com/terrajobst),微軟 .NET 團隊的項目經理。 這一集的主要內容: ...
  • 前言我們之前研究過為什麼Unity的UI可以合批,是因為使用了相同的材質進行渲染,UI上不同圖片渲染是通過把圖片打成一張圖集後,使用Image組件對頂點填充了不同的UV值實現的。那麼有沒有什麼辦法可以讓3D的物體也像UI一樣,使用相同材質,但是可以表現出不一樣的樣子呢(比如顏色/位置等)?我們知道u ...
  • 一、 "Lambda 表達式"(lambda expression)是一個匿名函數,Lambda表達式基於數學中的λ演算得名,直接對應於其中的lambda抽象(lambda abstraction),是一個匿名函數,即沒有函數名的函數。Lambda表達式可以表示閉包(註意和數學傳統意義上的不同)。 ...
  • 自從.net core出現的時候,就知道c#的代碼居然能後運行到Linux上面,以前都沒想過居然這麼牛逼,所以很早就想學習怎樣部署上去,直到現在.net core都出現2.2了,才花時間去接觸,說實話,第一次接觸,太多的坑在這個上邊了,途中還走了很多彎路...現在把最後總結的流程簡單的記錄下來 一 ...
  • 最近做了一個查錯工具,運用了winform文件操作的知識,做了幾點總結,不全面,只總結了幾點項目里用過的知識(關於以下內容只是個人的理解和總結,不對的地方請多指教,有補充的可以評論留言大家一起討論學習)。 一:基礎知識 1.根據文件路徑得到文件夾對象:DirectoryInfo di = new D ...
  • 前文 本文只對筆者學習掌握的一般的拖動問題的實現方法進行整理和討論,包括視窗、控制項等內容的拖動。 希望本文能對一些尋找此問題的解決方法的人和一些剛入門的人一些幫助。筆者為WPF初學者,能得到各位的批評指正也是榮幸萬分。有更好更多的方法,勞煩與我分享,不勝感激。 本文的各種實現方法其他博客中也都有提及 ...
  • OSharpNS全稱OSharp Framework with .NetStandard2.0,是一個基於`.NetStandard2.0`開發的一個`.NetCore`快速開發框架。這個框架使用最新穩定版的`.NetCore SDK`(當前是.NET Core 2.2),對 AspNetCore... ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...