共享內存簡介和mmap 函數

一、共享內存簡介

共享內存區是最快的IPC形式,這些進程間數據傳遞不再涉及到內核,換句話說是進程不再通過執行進入內核的系統調用來傳遞彼此的數據。


即每個進程地址空間都有一個共享存儲器的映射區,當這塊區域都映射到相同的真正的物理地址空間時,可以通過這塊區域進行數據交換,例如共享庫就是這么實現的,很多進程都會使用同一個函數如printf,也許在真正的物理地址空間中只存在一份printf.o ,然后所有進程都映射到這一份printf.o 就實現了共享。


用管道或者消息隊列傳遞數據:


用共享內存傳遞數據:


即使用共享內存傳遞數據比用消息隊列和管道來說,減少了進入內核的次數,提高了效率。


二、mmap 函數

#include <sys/mman.h>

功能:將文件或者設備空間映射到共享內存區。
原型?void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
參數
addr: 要映射的起始地址,通常指定為NULL,讓內核自動選擇
len:映射到進程地址空間的字節數
prot:映射區保護方式
flags:標志
fd:文件描述符
offset:從文件頭開始的偏移量,必須是頁大小的整數倍(在32位體系統結構上通常是4K)
返回值:成功返回映射到的內存區的起始地址;失敗返回-1


prot 參數取值:

PROT_EXEC 表示映射的這一段可執行,例如映射共享庫

PROT_READ 表示映射的這一段可讀

PROT_WRITE 表示映射的這一段可寫

PROT_NONE 表示映射的這一段不可訪問


flag參數有很多種取值,這里只講兩種,其它取值可查看mmap(2)

MAP_SHARED 多個進程對同一個文件的映射是共享的,一個進程對映射的內存做了修改,另一個進程也會看到這種變化。

MAP_PRIVATE 多個進程對同一個文件的映射不是共享的,一個進程對映射的內存做了修改,另一個進程并不會看到這種變化,也不會真的寫到文件中去。


內存映射文件示意圖:


如果mmap成功則返回映射首地址,如果出錯則返回常數MAP_FAILED。當進程終止時,該進程的映射內存會自動解除,也可以調用munmap解除映射:

功能:取消mmap函數建立的映射
原型?int munmap(void *addr, size_t len);
參數
addr: 映射的內存起始地址
len:映射到進程地址空間的字節數
返回值:成功返回0;失敗返回-1


下面寫兩個程序測試一下:

mmap_write.c

?

C++ Code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
?
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/mman.h>

#define?ERR_EXIT(m)?\
????do?{?\
????????perror(m);?\
????????exit(EXIT_FAILURE);?\
????}?while(0)

typedef?struct?stu
{
????char?name[4];
????int?age;
}?STU;

int?main(int?argc,?char?*argv[])
{
????if?(argc?!=?2)
????{
????????fprintf(stderr,?"Usage:?%s?<file>\n",?argv[0]);
????????exit(EXIT_FAILURE);
????}

????int?fd;
????fd?=?open(argv[1],?O_CREAT?|?O_RDWR?|?O_TRUNC,?0666);
????if?(fd?==?-1)
????????ERR_EXIT("open");

????lseek(fd,?sizeof(STU)?*?5?-?1,?SEEK_SET);
????write(fd,?"",?1);

????STU?*p;
????p?=?(STU?*)mmap(NULL,?sizeof(STU)?*?5,?PROT_READ?|?PROT_WRITE,
????????????????????MAP_SHARED,?fd,?0);

????if?(p?==?-1)
????????ERR_EXIT("mmap");

????char?ch?=?'a';
????int?i;
????for?(i?=?0;?i?<?5;?i++)
????{
????????memcpy((p?+?i)->name,?&ch,?1);
????????(p?+?i)->age?=?20?+?i;
????????ch++;
????}

????printf("initialize?over\n");

????munmap(p,?sizeof(STU)?*?5);
????printf("exit...\n");
????return?0;
}

?

mmap_read.c

?

C++ Code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
?
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/mman.h>

#define?ERR_EXIT(m)?\
????do?{?\
????????perror(m);?\
????????exit(EXIT_FAILURE);?\
????}?while(0)

typedef?struct?stu
{
????char?name[4];
????int?age;
}?STU;

int?main(int?argc,?char?*argv[])
{
????if?(argc?!=?2)
????{
????????fprintf(stderr,?"Usage:?%s?<file>\n",?argv[0]);
????????exit(EXIT_FAILURE);
????}

????int?fd;
????fd?=?open(argv[1],?O_RDWR);
????if?(fd?==?-1)
????????ERR_EXIT("open");


????STU?*p;
????p?=?(STU?*)mmap(NULL,?sizeof(STU)?*?5,?PROT_READ?|?PROT_WRITE,
????????????????????MAP_SHARED,?fd,?0);

????if?(p?==?-1)
????????ERR_EXIT("mmap");

????int?i;
????for?(i?=?0;?i?<?5;?i++)
????{
????????printf("name?=?%s?age?=?%d\n",?(p?+?i)->name,?(p?+?i)->age);
????}
????munmap(p,?sizeof(STU)?*?5);
????printf("exit...\n");
????return?0;
}

?

先運行mmap_write ,然后用od -c 查看文件內容:

?

simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./mmap_write test?
initialize over
exit...
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ od -c test?
0000000 ? a ?\0 ?\0 ?\0 024 ?\0 ?\0 ?\0 ? b ?\0 ?\0 ?\0 025 ?\0 ?\0 ?\0
0000020 ? c ?\0 ?\0 ?\0 026 ?\0 ?\0 ?\0 ? d ?\0 ?\0 ?\0 027 ?\0 ?\0 ?\0
0000040 ? e ?\0 ?\0 ?\0 030 ?\0 ?\0 ?\0
0000050
注意od -c 輸出的是八進制,024即20,即對內存的操作寫入了文件。

再嘗試運行mmap_read,輸出如下:
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./mmap_read test?
name = a age = 20
name = b age = 21
name = c age = 22
name = d age = 23
name = e age = 24
exit...
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$?

再次將文件test 映射到內存,然后從內存讀取到了文件的內容。


mmap 編程注意點:

1、映射不能改變文件的大小;
2、可用于進程間通信的有效地址空間不完全受限于被映射文件的大小;
3、文件一旦被映射后,所有對映射區域的訪問實際上是對內存區域的訪問。映射區域內容寫回文件時,所寫內容不能超過文件的大小;


對于1,3點,將mmap_write.c 中40行以后的代碼中的5改成10,即映射的內存大于文件的大小,這樣寫入是不會出錯的,因為是向內存寫入,但用od 查看時發現文件還是40 個字節,即只有前5個STU才被真正寫入到了文件。

對于第2點,將mmap_write.c 和 mmap_read.c 都按上面說的更改成10,然后在mmap_write.c 中munmap 函數之前sleep(10); 先運行mmap_write,再在另一終端運行mmap_read,觀察結果:

simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./mmap_read test?
name = a age = 20
name = b age = 21
name = c age = 22
name = d age = 23
name = e age = 24
name = f age = 25
name = g age = 26
name = h age = 27
name = i age = 28
name = j age = 29
exit...

即在mmap_write 對映射內存區域寫入之后尚未取消映射時,mmap_read 也映射了test 文件,兩個虛擬進程地址空間的映射區域都指向了同一塊物理內存,所以也能讀到write 進程對內存的修改,但進程結束后查看test 文件,還是40個字節而已。內存的映射是以頁面為單位的,一般為4k,所以才有第2條的說法,其實這才是真正體現共享內存可以進程間通信的所在。


最后一點,與write 類似,將文件映射到內存后對內存進行寫入,不一定會馬上寫回文件,有可能內核也會產生一個緩沖區,找個適當的時間內核再寫回設備文件,write 之后可以調用fsync 進行同步,同樣地,mmap 可以調用msync 進行同步。


?

參考:

《linux c 編程一站式學習》

《UNP》

轉載于:https://www.cnblogs.com/alantu2018/p/8473111.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/255404.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/255404.shtml
英文地址,請注明出處:http://en.pswp.cn/news/255404.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【pyqt5學習】——QToolTip,QLabel控件,伙伴關系

目錄 0、學習資源 1、給控件設置提示信息——QToolTip 2、QLabel控件&#xff08;顯示圖像、設置超鏈接、信號綁定&#xff09; 3、QLabel伙伴關系 1&#xff09;代碼 2&#xff09;效果 3&#xff09;知識點addWidget 0、學習資源 PyQt5教程&#xff0c;來自網易云課堂…

Hello IT

從高中的計算機課開始學習如何按下開機鍵&#xff0c;如何上網&#xff0c;如何背誦五筆字根&#xff0c;再到大學中如何使用office,利用C編碼做算法&#xff0c;到現在IT中的偽一員&#xff0c;時間算起來也不短&#xff0c;然正在入門是在2010年8月杭州參加嵌入式培訓&#x…

ARM指令集2

ARM指令集2 ARM微處理器支持加載/存儲指令用于在寄存器和存儲器之間傳送數據&#xff0c;加載指令用于將存儲器中的數據傳送到寄存器&#xff0c;存儲指令則完成相反的操作。 LDR指令&#xff08;與MOV有區別&#xff0c;MOV只能操作通用寄存器&#xff09; LDR指令格式為&…

SylixOS中select原理及使用分析

2019獨角獸企業重金招聘Python工程師標準>>> 1. select接口簡介 1.1 select接口使用用例 select是操作系統多路I/O復用技術實現的方式之一。 多路I/O復用技術大致使用場景為&#xff1a;構造一張感興趣的文件描述符列表&#xff0c;然后調用多路復用的IO接口&#x…

【pyqt5學習】——QLineEdit學習(回顯模式)

目錄 1、回顯模式 2、成果顯示 3、知識點 1&#xff09;FormLayout布局添加addRow方法 2&#xff09;在輸入框顯示灰色提示字體&#xff0c;輸入內容時消失setPlaceholderText 3&#xff09;設置回顯模式setEchoMode 4、完整代碼 1、回顯模式 QLineEdit控件的主要功能是輸…

有關機械手臂控制中的兩個重要輸入參數

1.在機械手臂中有兩個重要參數。一個是編碼器的值&#xff0c;另外一個是馬達的電流值。根據這兩個可以獲得機械手臂的運動學&#xff0c;動力學的一些數據。第一重要特征參數 是DH參數&#xff0c;另外一個就是每個軸的質心參數。

MySQL的權限分配

MySQL 賦予用戶權限命令的簡單格式可概括為&#xff1a;grant 權限 on 數據庫對象 to 用戶&#xff0c;如 GRANT PRIVILEGES ON datebase.* to user% IDENTIFIED by passwd;一、給表數據賦權 grant 普通數據用戶&#xff0c;查詢、插入、更新、刪除 數據庫中所有表數據的權利。…

用HttpURLConnection發送http請求

//發送http請求try {//1.使用網址構造一個URL對象 URL url new URL(path);//2.獲取連接對象 HttpURLConnection conn (HttpURLConnection) url.openConnection();//3.設置一些屬性 //設置請求方式&#xff0c;注意大寫conn.setRequestMethod("GET");//設置請求超時…

【pyqt5學習】——QLineEdit控件輸入校驗器Validator、掩碼setInputMask限制輸入、textChanged信號

目錄 1、輸入校驗器——限制輸入框輸入的內容 1&#xff09;校驗器類型——整數、浮點數、數字字母結合&#xff08;正則&#xff09; 2&#xff09;步驟 3&#xff09;結果 ?編輯 ?編輯 4&#xff09;完整代碼 2、利用掩碼進行輸入的限制 0&#xff09;掩碼對照表 1…

Call requires API level 3 (current min is 1)

結果出現“Call requires API level 3 (current min is 1): 解決方法&#xff1a; 在工程上點擊右鍵 -> Android Tools -> Clear Lint Markers&#xff0c;即可。轉載于:https://www.cnblogs.com/qianyukun/p/5458331.html

Product文本格式說明

使用txt進行產品信息的說明。 Product文本格式說明 //**************************************************** //產品信息 //固定標識符全部大寫&#xff0c;全部在等號&#xff08;&#xff09;前面 //****************************************************** PRODUCTTest //…

PyOpenCL圖像處理:Box模糊

為什么80%的碼農都做不了架構師&#xff1f;>>> # -*- coding: utf-8 -*-from __future__ import absolute_import, print_function import numpy as np import pyopencl as cl import cv2 from PIL import Imagedef RoundUp(groupSize, globalSize): r globalSi…

【python bug修復】——Script file ‘D:\softwares_install\Anaconda3\envs\PartTimes\Scripts\pip-scrip

目錄 1、問題描述 2、問題解決 1&#xff09;下載pip安裝腳本 2&#xff09; 運行安裝pip腳本 3&#xff09; 下載庫 1、問題描述 利用pip命令進行庫的安裝時&#xff0c;突然出現這個問題&#xff0c;之前使用還好好的 Script file D:\softwares_install\Anaconda3\envs\P…

項目中的那些事---下載pdf文件

最近做了一個下載pdf文檔的需求&#xff0c;本以為使用HTML5中<a>標簽的屬性download就能簡單搞定&#xff0c;不料IE竟然不支持這一簡單粗暴的H5新特性&#xff0c;而是直接在網頁中打開&#xff0c; 于是各種搜索之后得出以下結論&#xff1a;IE中下載文檔時&#xff0…

MySQL日志分類及性能分析你應該知道的知識

為什么80%的碼農都做不了架構師&#xff1f;>>> MySQL日志記錄了MySQL數據庫日常操作和錯誤信息&#xff0c;MySQL總共有四種類型的日志&#xff0c;通過分析這些日志可以查詢到MySQL的運行情況、用戶操作、錯誤信息等&#xff0c;可以為MySQL的管理和優化提供必要…

利用三個點(trsf)來實現各種規則圖形的實現

在Val3,是使用trsf(x,y,z,rx,ry,rz)來實現三維空間點的位置與方向。 其中第一點和第二點位置很重要&#xff0c;第三點是用來確定方向。根據這三個點先確定一個用戶坐標系。 在這個坐標系中&#xff0c;實現圓&#xff0c;三角形&#xff0c;矩形&#xff0c;腰圓&#xff0c;正…

android基礎組件----Button的使用

按鈕由文本或圖標&#xff08;或文本和一個圖標&#xff09;組成&#xff0c;當用戶觸摸到它時&#xff0c;會發生一些動作。今天我們開始Button的學習。少年的愛情永遠不夠用&#xff0c;一杯酒足以了卻一件心事。 Button的簡要說明 根據你是否想要一個帶有文本的按鈕&#xf…

@Repository、@Service、@Controller 和 @Component

Repository 、Service 、 Controller 、Component 這四個Spring注解 ,用于把加了注解的 類 加入到Spring 容器中管理&#xff0c;節省了xml 的繁重的配置&#xff0c;盡管如此xml 同樣可以實現&#xff08;一般建議先搞懂xml&#xff09;。 Repository Repository注解便屬于最先…

【pyqt5學習】——QTextEdit控件學習:獲取文本、添加文本

目錄 1、QTextEdit控件介紹 2、QTextEdit控件添加文本、添加HTML格式 3、QTextEdit控件獲取文本、獲取HTML格式文本 4、案例 1&#xff09;完整代碼 2&#xff09;效果 1、QTextEdit控件介紹 QTextEdit控件是一個支持多行輸入的輸入框&#xff0c;支持HTML進行格式的設置 2…

空間變化

空間變化 第一種&#xff1a;圓 第二種: 矩形 第三種&#xff1a;正五邊形 第四種:正六邊形 第五種&#xff1a;腰圓 算法&#xff1a; 在規則圖形&#xff08;不包括圓心-直徑法&#xff09;中&#xff0c;等于或者超過三個點。一般的&#xff0c;利用p1,p2,p3來建立一個用戶…