E-moss,程序員,愛好閱讀和擼狗,主要從事iOS開發工做,公衆號:知本集。
主要分享和編寫技術方面文章,不按期分享讀書筆記,亦可訪問「知本集」Git地址:https://github.com/knowtheroot/KnowTheRoot_iOS,歡迎提出問題和討論。
複製代碼
所謂內存映射,就是將文件的磁盤扇區映射到進程的虛擬內存空間的過程。node
1.進程發起一個讀文件請求;git
2.內核經過查找進程文件符表,定位到內核已打開的文件集上的文件信息,從而找到對應文件的inode;程序員
3.inode在地址空間(address_space)上查找要請求的文件是否已經緩存在內核頁的高速緩存中,若是存在,則直接放回該文件的內容;github
4.若是文件不存在高速緩存中,則經過inode定位到文件的磁盤地址,將數據從磁盤複製到內核頁高速緩存。以後再次範聖琦讀頁面的過程,將內核高速緩存中的數據發送給用戶進程;緩存
什麼是inode?
全稱爲index node,既存儲文件元信息的區域,中文譯名「索引節點」。
例如包含:文件權限、文件擁有者的UID、文件的大小等等。bash
1.系統在read/write的時候是很耗時的,例如在讀文件的時候,將文件內容從硬盤拷貝到內核空間的一個緩衝區,而後再將這些數據拷貝到用戶空間,實際上完成了兩次數據拷貝;
2.同理,寫入操做一樣耗時,待寫入的buffer在內核空間不能直接訪問,必需要先拷貝至內核空間對應的主存,再寫回磁盤中(延遲寫回),也是須要兩次數據拷貝;
3.若是兩個進程都對磁盤中的一個文件內容進行訪問,那麼這個內容在物理內存中有三份:進程A的地址空間 + 進程B的地址空間 + 內核頁高速緩衝空間;微信
此時咱們找到了文件讀取的痛點:兩次拷貝致使效率太低數據結構
「映射」這個詞,就和數學課上說的「一一映射」是一個意思,就是創建一種一一對應關係,在這裏主要是指硬盤上文件 的位置與進程邏輯地址空間 中一塊大小相同的區域之間的一一對應。微信開發
注意:這種對應關係純屬是邏輯上的概念,物理上是不存在的,緣由是進程的邏輯地址空間自己就是不存在的。函數
具體到代碼,就是創建並初始化了相關的數據結構(struct address_space),這個過程有系統調用mmap()實現,因此創建內存映射的效率很高。
1.mmap()會返回一個指針ptr,它指向進程邏輯地址空間中的一個地址,這樣之後,進程無需再調用read或write對文件進行讀寫,而只須要經過ptr就可以操做文件;
2.可是ptr所指向的是一個邏輯地址,要操做其中的數據,必須經過MMU將邏輯地址轉換成物理地址;
3.創建內存映射並無實際拷貝數據,這時,MMU在地址映射表中是沒法找到與ptr相對應的物理地址的,也就是MMU失敗,將產生一個缺頁中斷,缺頁中斷的中斷響應函數會在swap中尋找相對應的頁面,若是找不到(也就是該文件歷來沒有被讀入內存的狀況),則會經過mmap()創建的映射關係,從硬盤上將文件讀取到物理內存中;
4.若是在拷貝數據時,發現物理內存不夠用,則會經過虛擬內存機制(swap)將暫時不用的物理頁面交換到硬盤上;
MMU是Memory Management Unit的縮寫,中文名是內存管理單元,它是中央處理器(CPU)中用來管理虛擬存儲器、物理存儲器的控制線路,同時也負責虛擬地址映射爲物理地址,以及提供硬件機制的內存訪問受權,多用戶多進程操做系統。
mmap內存映射的實現過程,總的來講能夠分爲三個階段:
1.進程啓動映射過程,並在虛擬地址空間中爲映射建立虛擬映射區域;
2.調用內核空間的系統調用函數mmap(不一樣於用戶空間函數),實現文件物理地址和進程虛擬地址的一一映射關係;
3.進程發起對這片映射空間的訪問,引起缺頁異常,實現文件內容到物理內存(主存)的拷貝;
前兩個階段僅在於建立虛擬區間並完成地址映射,可是並無將任何文件數據的拷貝至主存。真正的文件讀取是當進程發起讀或寫操做時。
以前說過,常規文件操做爲了提升讀寫效率和保護磁盤,使用了頁緩存機制,因爲頁緩存處在內核空間,不能被用戶進程直接尋址,這樣就出現了兩次拷貝的過程,這也是常規文件操做的性能限制。
使用mmap操做文件中,建立新的虛擬內存區域和創建文件磁盤地址和虛擬內存區域映射這兩步,沒有任何文件拷貝操做。
以後訪問數據時發現內存中並沒有數據而發起的缺頁異常過程,能夠經過已經創建好的映射關係,只使用一次數據拷貝,就從磁盤中將數據傳入內存的用戶空間中,供進程使用。
對硬盤上一個名爲「mmap_test」的文件進行操做,文件中存有10000個整數,程序兩次使用不一樣的方法將它們讀出,加1,再寫回硬盤。
gettimeofday( &tv1, NULL );
fd = open( "mmap_test", O_RDWR );
array = mmap( NULL, sizeof(int)*MAX, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );
for( i=0; i<MAX; ++i )
++array[ i ];
munmap( array, sizeof(int)*MAX );
msync( array, sizeof(int)*MAX, MS_SYNC );
free( array );
close( fd );
gettimeofday( &tv2, NULL );
複製代碼
如下摘自《微信開發團隊guoling的技術分享》
什麼是MMKV? MMKV 是基於 mmap 內存映射的 key-value 組件,底層序列化/反序列化使用 protobuf 實現,性能高,穩定性強。 MMKV的實現 內存準備
經過 mmap 內存映射文件,提供一段可供隨時寫入的內存塊,App 只管往裏面寫數據,由 iOS 負責將內存回寫到文件,沒必要擔憂 crash 致使數據丟失。
數據處理
數據序列化方面咱們選用 protobuf 協議,pb 在性能和空間佔用上都有不錯的表現。考慮到咱們要提供的是通用 kv 組件,key 能夠限定是 string 字符串類型,value 則多種多樣(int/bool/double等)。要作到通用的話,考慮將 value 經過 protobuf 協議序列化成統一的內存塊(buffer),而後就能夠將這些 KV 對象序列化到內存中。
關於MMKV的原理以後將會專門新開一篇文章來詳解和應用。