因為最近項目中需要實現一個臨時資料高速儲存,所以最近對記憶體對應檔做了一下瞭解,寫出來與大家分享一下,因為個人水平有限也許會有這樣那樣的問題也懇請大家指正。
mmap是linux記憶體對應檔,是將檔案對應成為記憶體位址空間的一種方式,其實,方法很簡單。
memfd = open(MEMFILE, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR);memd = mmap(NULL, (sizeof(Type)) * size, PROT_WRITE | PROT_READ, MAP_SHARED, memfd, 0);
我們的程式現在擁有了一定的地址空間,代碼中也獲得了一個指向首地址的指標,那我們該怎麼使用他呢?其實,大家想怎麼用就怎麼用,不過我們還是採用一定的方法將這些地址空間管理起來,定義一下結構體來對記憶體進行結構化管理:
/** * \struct NodeHeader * \brief 節點頭 */struct NodeHeader { unsigned int size; /**< 記憶體尺寸 */ bool isActive; /**< 是否使用 */ unsigned int refCount; /**< 引用數量 */ NodeHeader *next;};/** * \struct Node * \brief 節點 */struct Node { NodeHeader header; /**< 節點頭 */ char *data; /**< 資料 */};/** * \struct NodeList * \brief 節點列表 */struct NodeList { NodeList *next; unsigned int size; /**< 列表中節點的尺寸 */ NodeHeader *header; /**< 節點 */};/** * \struct NodeContext * \brief 節點列表目錄 */struct NodeContext { bool isInit; /**< 是否已經初始化*/ unsigned int refCount; /**<引用計數器 */ NodeList *list; /**< 節點列表頭 */};
注意,以上結構體本身沒有定義實際儲存資料的空間,而是通過Node的data指標來指向資料的空間。我一般會在Node之後緊接著根據size大小的資料空間,在分配時直接將指標向下移動相應的數值就可以。這是malloc函數實現中brk ()函數要做的事情,我們自己來做來類比實現其分配過程。
初始化節點很簡單:
Node* allocNode(NodeList *list){ Node *node = (Node *)nodeMem; node->header.size = list->size; if(!nodeContext->isInit) { node->header.refCount = 0; node->header.isActive = false; } node->data = nodeMem + sizeof(Node); nodeMem = nodeMem + sizeof(Node) + list->size; //注意,這裡直接將指標移動過去一定的值來為實際資料保留相應的空間 return node;}
我們經過一系列初始化過程將這些連續的地址空間進行了分割,這樣我們就可以通過自訂的介面來訪問這些記憶體塊了。不過提醒大家的是由於進程間映射的地址值不同,所以我們把地址一個放到了對應檔中,會造成地址訪問的越界。所以以上基於指標的實現只能夠用於進程內,不過大家完全可以只在對應檔中儲存資料,通過不同進程中相同的記憶體結構來訪問。還有一種方式就是類比cpu變址定址和Linux底層虛擬記憶體來實現,這樣既可以擁有了結構化的資料,又擁有了指標訪問的靈活性。
那我們如何擷取資料呢?大家注意到我上面使用的都是POD資料,也就是說他們都是不會經過C++編譯器最佳化的(如果用在C中就更不存在這個問題了),這裡我們直接offset來實現由資料獲得節點:
Node* getNode(char *data){ return (Node *)(&(*data) - sizeof(NodeHeader));}
上面的所有的過程都是簡單的對記憶體地區的移動,記得有人說過“編程不過就是做一些加法或者將記憶體中的資料從一個地方搬到另一個地方”。