日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

内存映射文件进行写文件和读文件有啥不同_Linux中的mmap映射 [二]

發布時間:2025/3/20 linux 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 内存映射文件进行写文件和读文件有啥不同_Linux中的mmap映射 [二] 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

來看下上文介紹的mmap()的函數原型是怎樣的:

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

看起來參數好像比較多,但對照著下面這張圖看,應該會清晰不少。

其中fd, offset和length都是用來描述要映射的文件區域的,"fd"是文件描述符,對于匿名映射,fd應該是-1(如果是通過打開/dev/zero這個特殊的文件來創建匿名映射,則它也是有不為-1的正常fd值的)。"offset"是文件中映射的起始位置,"length"是映射的長度。如果訪問了超出映射的區域,則有可能觸發SIGSEGV異常(segmentation fault)。

為什么說是有可能呢?因為只有訪問的地址沒有落在進程的任何VMAs(關于VMA請參考這篇文章)里,才應該觸發SIGSEGV,如果超出映射的這段區域正好落在另一個VMA里,那就可以僥幸逃過SIGSEGV。其實這樣更糟糕,觸發SIGSEGV可以讓你及時知道事故的第一現場,如果繼續運行,然后在某個不可預知的地方崩潰,那這bug排查起來就困難了。

圖片來源 - 《Linux環境編程:從應用到內核》

Linux是以page為單位管理內存的,mmap也是以page為單位建立映射的,因此offset必須是按page size對齊的(不對齊的話就會映射失敗)。文件是有長度的,offset和offset+length的值都應該小于被映射文件的長度。

然而,mmap()并不會對此作出檢查,所以有可能建立的映射區域并不完全在文件的長度范圍內。比如一個文件的長度是4KB,而你映射了8KB,那么在訪問后面的這4KB內存的時候,就會觸發SIGBUS異常,表明你訪問的這段內存區域,沒有對應的文件。

如果用戶給定的length不是按page size對齊的,那么內核會填充一部分長度以保證對齊(可見,offset對齊是用戶自己保證的,length對齊是靠內核來保證的)。比如文件長度是10KB,你映射了5KB,那么內核會將其擴充到8KB。如果你訪問下圖中5120字節到8191字節的這段內存,因為既沒有超出映射的內存區域,也沒有超出文件的長度,所以是不會觸發任何異常的。

你以為只要保證映射的區域完全落在文件的長度范圍內就可以高枕無憂了么?事務都是變化的,被映射的文件也不例外,比如它可以通過truncate()/ftruncate()截斷,截斷之后文件的長度如果減小了(truncate也是可以增大的),然后你剛好訪問了被截斷的這段區域,依然會觸發SIGBUS。

映射區域的大小可以通過mremap()動態調整,事實上,glibc中的realloc()就是調用mremap()實現的。

"prot"是protection的意思,表示的是對內存映射區域的保護,包括PROT_READ(可讀),PROT_WRITE(可寫)和PROT_EXEC(可執行)。還有一個很特殊的PROT_NONE,就是既不可讀也不可寫更不可執行,啥操作都不可以,那映射出來干嗎?

普通場景下沒有用,不代表特殊場景下沒用,PROT_NONE可以用于實現防范攻擊的guard page。如果攻擊者訪問了某個guard page,就會觸發SIGSEV,作用和地雷是差不多的。在布了地雷的土地上,你不能種田,不能蓋房,但是可以防范敵人的入侵,只是,你得記住地雷放的位置,別自己踩著了。

prot屬性是可以通過mprotect()動態修改的(mprotect并不局限于操作由mmap映射的內存區域,它可以操作任意區域的內存),這篇文章介紹的JIT實現就是對此一個頗有意思的應用。

"flags"用于指定映射是基于文件的還是匿名(MAP_ANONYMOUS)的,是共享的(MAP_SHARED)還是私有的(MAP_PRIVATE)。上文介紹過,共享映射和私有映射在寫操作上是有區別的,前者是直接寫,后者是COW,先copy再寫。

addr用于指定映射到的VMA的起始地址,這個地址也必須按page size對齊。映射是由內核完成的,但進程可以通過addr參數建議一個它認為的最佳地址(沒有這種要求就設置addr為NULL),畢竟進程最了解它自身的應用場景嘛。

但這對內核來說不是強制的,如果addr和addr+length之間的虛擬內存空間恰好是可用的,那么內核會滿足進程的這一要求。如果flags中加上MAP_FIXED,那就是進程要求必須映射到這個addr起始的區域,當然,這會增加映射失敗的概率。

實際映射到的VMA起始地址保存在mmap()的返回值中。

同read()和write()一樣,要獲得mmap()中的參數fd,自然是要先open()一下,而調用open()的時候是需要設置對文件的讀寫權限的,比如O_RDONLY(只讀),O_RDWD(可讀寫),mmap()中prot和flags的設置需要和進程打開文件時的屬性設置保持一致。

上文提到,mmap()的實現主要是在進程虛擬地址空間創建了一個VMA,而一個VMA是有VM_READ, VM_WRITE, VM_EXEC, VM_SHARED等諸多屬性的。如果是通過mmap創建的VMA,則這個VMA的屬性就是由mmap()中prot和flags參數傳遞進去的。

參考:

《Linux環境編程:從應用到內核》第11.4節

原創文章,轉載請注明出處。

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的内存映射文件进行写文件和读文件有啥不同_Linux中的mmap映射 [二]的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。