mmap内存映射学习笔记
1、進行虛擬內存結構體
前文《虛擬內存管理》我們講到每個進程的結構體task_struct中都有一個內存描述結構體mm_struct。 mm_struct此結構體描述了進程占用虛擬內存的情況,包含每個內存段的起始地址、結束地址。
如下圖,除了固定的代碼段、數據段、BSS段、堆、棧,還有一個MMAP 段,此段為程序員自己通過mmap()函數主動生成的一個段,用于將磁盤文檔映射到內存中以增加文件訪問效率(也可進行進程通信)的一種手段。
上述每個段都會生成一個VMA結構體。
2、mmap()的使用方法
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
start :? 指向欲映射的內存起始地址,通常設為 NULL,代表讓系統自動選定地址,映射成功后返回該地址。
? ?length:? 代表將文件中多大的部分映射到內存。
? ?prot? :? 映射區域的保護方式。可以為以下幾種方式的組合:
? ? ? ? PROT_EXEC 映射區域可被執行
? ? ? ? PROT_READ 映射區域可被讀取
? ? ? ? PROT_WRITE 映射區域可被寫入
? ? ? ? PROT_NONE 映射區域不能存取——需要小于等于文件打開的權限一致
? ?flags :? 影響映射區域的各種特性。在調用mmap()時必須要指定MAP_SHARED 或MAP_PRIVATE。
? ? ? ? MAP_FIXED 如果參數start所指的地址無法成功建立映射時,則放棄映射,不對地址做修正。通常不鼓勵用此旗標。
? ? ? ? MAP_SHARED 對映射區域的寫入數據會復制回文件內,而且允許其他映射該文件的進程共享。
? ? ? ? MAP_PRIVATE 對映射區域的寫入操作會產生一個映射文件的復制,即私人的“寫入時復制”(copy on write)對此區域作的任何修改都不會寫回原來的文件內容。
? ? ? ? MAP_ANONYMOUS建立匿名映射。此時會忽略參數fd,不涉及文件,而且映射區域無法和其他進程共享。
? ? ? ? MAP_DENYWRITE只允許對映射區域的寫入操作,其他對文件直接寫入的操作將會被拒絕。
? ? ? ? MAP_LOCKED 將映射區域鎖定住,這表示該區域不會被置換(swap)。
? ?fd? ? :? 要映射到內存中的文件描述符。如果使用匿名內存映射時,即flags中設置了MAP_ANONYMOUS,fd設為-1。有些系統不支持匿名內存映射,則可以使用fopen打開/dev/zero文件,然后對該文件進行映射,可以同樣達到匿名內存映射的效果。
? ?offset:文件映射的偏移量,通常設置為0,代表從文件最前方開始對應,offset必須是PAGE_SIZE的整數倍。
返回值:若映射成功則返回映射區的內存起始地址,否則返回MAP_FAILED(-1),錯誤原因存于errno 中。
int munmap(void *start, size_t length);——解除mmap的映射
int msync ( void * addr , size_t len, int flags);? ——文件回寫
3、mmap()的作用,將文件或者其他對象映射到內存中,使文件操作像通過指針操作內存一樣簡單快速
其主要有兩個用途,下面我們就從這兩個方面來講述:
- 內存映射
- 進程通信
4、內存映射,傳統文件訪問緩慢,內存映射后操作文件速度加快
傳統文件訪問的缺點:
- 效率低:應用程序通過read,write,ioctl來訪問硬件設備,它們都要經過兩次的數據拷貝,一次是用戶空間和內核空間的數據拷貝,另外一次是內核空間和硬件之間的數據拷貝。
- 浪費空間: 訪問文件的傳統方法是用open打開它們, 如果有多個進程訪問同一個文件, 則每一個進程在自己的地址空間都包含有該文件的副本,這不必要地浪費了存儲空間.??
下圖說明了兩個進程同時讀一個文件的同一頁的情形. 系統要將該頁從磁盤讀到高速緩沖區中, 每個進程再執行一個存儲器內的復制操作將數據從高速緩沖區讀到自己的地址空間.?
通過將磁盤文件映射到內存中,從而使文件訪問更加高效簡單:
眾多內存數據庫如MongoDB操作數據,就是把文件磁盤內容映射到內存中進行處理
5、共享內存實現進行通信
進程A和進程B都將該頁映射到自己的地址空間, 當進程A第一次訪問該頁中的數據時, 它生成一個缺頁中斷. 內核此時讀入這一頁到內存并更新頁表使之指向它.以后,
當進程B訪問同一頁面而出現缺頁中斷時, 該頁已經在內存, 內核只需要將進程B的頁表登記項指向次頁即可。
6、共享內存比管道的優勢:共享內存比管道和消息隊列效率高
共享內存和消息隊列,FIFO,管道傳遞消息的區別:
——消息隊列,FIFO,管道的消息傳遞方式一般為
1:服務器得到輸入
2:通過管道,消息隊列寫入數據,通常需要從進程拷貝到內核。
3:客戶從內核拷貝到進程
4:然后再從進程中拷貝到輸出文件
上述過程通常要經過4次拷貝,才能完成文件的傳遞。
——共享內存只需要
1:從輸入文件到共享內存區
2:從共享內存區輸出到文件
下面是舉例:
7、mmap注意事項
?
?最終被映射文件的內容的長度不會超過文件本身的初始大小,即映射不能改變文件的大小。文件被映射部分而不是整個文件決定了進程能夠訪問的空間大小
?8、mmap的實現
- 映射時,在虛擬地址空間中為你創建虛擬映射區域
進程在用戶空間調用庫函數mmap
在當前進程的虛擬地址空間中,尋找一段空閑的滿足要求的連續的虛擬地址
為此虛擬區分配一個vm_area_struct結構,接著對這個結構的各個域進行了初始化
- 調用內核空間的系統調用函數mmap,實現文件物理地址和進程虛擬地址的一一映射關系
完成以上兩部后,這片虛擬地址并沒有任何數據關聯到主存中。
- 進程發起對這片映射空間的訪問,引發缺頁異常,實現文件內容到物理內存(主存)的拷貝
進程的讀或寫操作訪問虛擬地址空間這一段映射地址,通過查詢頁表,
發現這一段地址并不在物理頁面上。因為目前只建立了地址映射,真正
的硬盤數據還沒有拷貝到內存中,因此引發缺頁異常。處理缺頁異常時
發現對于的數據還沒有寫入內存,于是會將文件數據換入內存。
調頁過程先在交換緩存空間(swap cache)中尋找需要訪問的內存頁,如
果沒有則調用nopage函數把所缺的頁從磁盤裝入到主存中
?
轉載于:https://www.cnblogs.com/tjyuanxi/p/9394387.html
總結
以上是生活随笔為你收集整理的mmap内存映射学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: js格式化货币金额
- 下一篇: 根据map键值对,生成update与se