c++ mmap写入速度_Linux系统编程_用mmap+数组的方式修改数据文件
正文開始前,先聊點非技術的東西,推薦2本生動有趣的書:
- 《經濟學原理 宏觀經濟學》,曼昆,豆瓣評分9.3,4945人評價
- 《經濟學原理 微觀經濟學》,曼昆,豆瓣評分9.6,1879人評價
我想看這兩本書原因是:Joel on Software 在一篇 給計算機系學生 的忠告文章里說,畢業之前你一定要修一門經濟學課程,Joel( stackoverflow 的創始人 ) 的話在我心里有很重的分量,我愿意接受他的建議,甚至認為自己 應該在高中時 就閱讀上面2本書。
這兩本書在豆瓣上的評價極高:
一看到《經濟學原理》這樣的書名,我會立即把它歸為異次元一類,碰都不會去碰,別說讀它了,自從大學畢業后,對于任何此類教科書,我會毫不客氣地說,滾!
這種情緒的對立既包含了對中國教育制度的控訴,又體現了對死讀書無法改變命運的殘酷現實的無奈。
工作約7到8年后,自我體會到和社會的逐漸脫節,這里不止是因為工作環境的隔離性和社交圈的狹窄,還有自身知識量的乏饋,光是義務教育和本科教育所建立的知識結構顯然已經無法支撐起我的求知欲所對應的飛速發展的社會萬物,可是學習從何開始呢?
話說到這里,志同道合的人應該知道怎么做了,我就不再講廢話了,下面回歸到技術上。
以下是正文:
一、背景知識
mmap()系統調用在調用進程的虛擬地址空間中創建一個新內存映射( memory mappings ),映射分為兩種:
文件映射( File mapping ):文件映射將一個文件的一部分直接映射到調用進程的虛擬內存中。一旦一個文件被映射之后就可以通過在相應的內存區域中操作字節來訪問文件內容了。映射的分頁會在需要的時候從文件中(自動)加載。這種映射也被稱為基于文件的映射( file-based mapping )或內存映射文件( memory-mapped file )。
匿名映射( Anonymous mapping ):匿名映射沒有對應的文件,這種映射的分頁會被初始化為0。
我們可以使某個磁盤文件的內容看起來就像是內存中的一個數組。如果文件的內容是由讀多條 C 語言結構體描述的記錄構成的話,你就可以通過訪問結構體數組來更新文件的內容了。
1. 創建映射: mmap()
mmap()系統調用在調用進程的虛擬地址空間中創建一個新映射,如果是文件映射的話,是通過文件描述符和文件關聯在一起:
#include?void?*mmap(void?*addr,?size_t?length,?int?prot,?int?flags,?int?fd,?off_t?offset);
成功時 mmap() 會返回新映射的起始地址。發生錯誤時 mmap( )會返回 MAP_FAILED。
參數:
addr,用去指定內存區域的起始地址,為了可移植性,一般設為 NULL, 表示由系統自動決定內存區域的起始地址。不管采用何種方式,內核會選擇一個不與任何既有映射沖突的地址。
len, 映射區域的長度。
prot,映射區域的訪問權限,可選項包括 READ/WRITE/EXEC/NONE。
flags, 映射區域的可見性:
- 私有映射(MAP_PRIVATE):在映射內容上發生的變更對其他進程不可見,對于文件映射來講,變更將不會在底層文件上發生。
- 共享映射(MAP_SHARED):在映射內容上發生的變更對所有共享同一個映射的其他進程都可見,對于文件映射來講,變更將會發生在底層的文件上。
fd, 被映射的文件的文件描述符。
offset, 從該偏移位置開始映射。
更多知識點:
在打開描述符fd引用的文件時必須要具備與prot和flags參數值匹配的權限。
offset參數指定了從文件區域中的哪個字節開始映射,它必須是系統分頁大小的倍數。
一旦mmap()被調用之后就能夠關閉文件描述符了,而不會對映射產生任何影響。
在Linux上,一個文件映射的分頁會在首次被訪問時被映射進內存。
除了普通的磁盤文件,使用 mmap() 還能夠映射各種真實和虛擬設備的內容,如硬盤、光盤以及/dev/mem。
2. 同步映射區域:msync()
msync() 系統調用讓應用程序能夠顯式地 在某種程度上 控制何時完成共享映射與映射文件之間的同步。
$?man?msyncSYNOPSIS
????#include?
???????int?msync(void?*addr,?size_t?length,?int?flags);
DESCRIPTION
???????msync()??flushes??changes?made?to?the?in-core?copy?of?a?file?that?was?mapped?into?memory?using?mmap(2)?back?to?the?filesystem.
參數:
addr和length參數指定了需同步的內存區域的起始地址和大小。
flags 的可取值:
- MS_SYNC,執行一個同步的文件寫入。會阻塞直到內存區域中所有被修改過的分頁被寫入到磁盤為止。
- MS_ASYNC,執行一個異步的文件寫入,會立即返回。內存區域中被修改過的分頁會在后面某個時刻被寫入磁盤并立即對在相應文件區域中執行 read() 的其他進程可見。
- MS_INVALIDATE,使映射數據的緩存副本失效。其結果是其他進程對文件做出的所有更新將會在內存區域中可見。
更多知識點:
如果映射是私有的,那么不修改被映射的文件。
與其他內存映射函數一樣,地址必須與頁邊界對齊。
MS_ASYNC 不保證何時寫入,因為在后臺運行的 IO elevator 算法試圖通過合并和排序寫入來最大程度地提高效率,以最大程度地提高 IO 的吞吐量。
二、演示 demo
1. 分解代碼
定義數據類型:
typedef?struct?{????int?integer;
????char?string[24];
}?RECORD;
構造數據文件 records.dat:
fp?=?fopen("records.dat","w+");for(i=0;?i????record.integer?=?i;
????sprintf(record.string,"RECORD-%d",i);
????fwrite(&record,sizeof(record),1,fp);
}
映射文件 records.dat:
f?=?open("records.dat",O_RDWR);mapped?=?(RECORD?*)mmap(0,?NRECORDS*sizeof(record),?
????????????????????????PROT_READ|PROT_WRITE,?MAP_SHARED,?f,?0);
通過結構體數組修改數據( RECORD-43->RECORD-243 ):
mapped[43].integer?=?243;sprintf(mapped[43].string,"RECORD-%d",mapped[43].integer);
msync((void?*)mapped,?NRECORDS*sizeof(record),?MS_ASYNC);
清理現場:
munmap((void?*)mapped,?NRECORDS*sizeof(record));close(f);
2. 運行效果
$?gcc?mmap.c?-o?mmap$?./mmap
$?ls
mmap??mmap.c??records.dat
$?strings?records.dat
RECORD-0
RECORD-1
RECORD-2
...
RECORD-42
RECORD-243
RECORD-44
...
可以看到:RECORD-43 被成功修改為 RECORD-243。
演示的 demo 就這么簡單,卻需要如此多的背景知識,甚至還有大量的細節沒在文中披露,有需要時再深入講解吧。
三、相關參考
- 《Linux/Unix 系統編程手冊-第49章 內存映射》
- 《Linux程序設計-第3章 文件操作》
- 《Unix 環境高級編程-第14章節 14.8-存儲映射I/O》
- LWN: R.I.P. pdflush
- Stack Overflow: msync(MS_ASYNC) flush order
你和我各有一個蘋果,如果我們交換蘋果的話,我們還是只有一個蘋果。但當你和我各有一個想法,我們交換想法的話,我們就都有兩個想法了。如果你也對 嵌入式系統和開源軟件 感興趣,并且想和更多人互相交流學習的話,請關注我的公眾號:嵌入式系統磚家,一起來學習吧,關注或轉發都是是對作者最大的支持,謝謝大家,祝工作順利~
總結
以上是生活随笔為你收集整理的c++ mmap写入速度_Linux系统编程_用mmap+数组的方式修改数据文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: access考试素材_NCRE考试当天常
- 下一篇: linux中_在 Linux 桌面中开始