1. 程式人生 > >簡述記憶體管理的原理和其簡單實現

簡述記憶體管理的原理和其簡單實現

我們平時在linux或者其他的嵌入式系統中經常會用到兩個函式,malloc和free。因為有這種動態記憶體分配的方法,所以給我們日常的程式設計帶來了很多的便捷之處。那麼,這兩個函式其工作機理是怎麼樣的呢?在這裡,我不想太過複雜地去闡述作業系統中其真正的執行過程,只是把這種動態記憶體分配的一些思路給說明一下,因為真正的動態記憶體分配是十分複雜的,我這裡只能簡單地描述其中的一種,並用C來把這種動態記憶體分配的方法給模擬出來。
malloc就是從一塊空間中開闢出有一部分能用的記憶體塊,返回給使用者的是這塊分配好的空間的首地址。而free正好相反,其作用是把分配好的空間重新歸還於原來的地方,就是我們常說的“釋放記憶體”。
相對free而言,malloc還是比較好理解,而free中的釋放又是怎麼一回事呢?簡單的來說,比如我們在一塊記憶體中佔用了其間的一塊空間,那麼此時作業系統中的某個機制就會把這個事件記錄下來,給已經分配給使用者的這塊空間作好標誌,防止下次再次分配的時候會涉及到這部分空間,造成指標越界。
而free的實現和其工作機理,要比malloc要複雜的多了。其要做的工作,不僅僅是把分配過的空間的標誌位給擦除掉,而且還要把記憶體中的碎片進行整合,這是非常必要的。記憶體中的碎片存在的越多,本來可分配的記憶體會變得越來越少,最後變得不可分割,從而導致malloc失敗,導致程式異常中斷。
可以用C來對上述的動態記憶體分配進行模擬實現。思路是,取一塊陣列,一部分可分配空間,一部分是用於記錄當前分配空間狀態的目錄,通過查詢目錄來獲取此時的記憶體分配情況,管理可分配空間,從而實現動態分配方法。實現程式碼如下:

#include<stdio.h>
#include<time.h>
#include<string.h>

#define MEMSIZE 10000   //總記憶體塊的大小
#define CATALOG_COUNT_MAX 100   //目錄的個數

char memory[MEMSIZE];
typedef struct mumm
{
   void *start; /*指向當前分配空間的首地址*/
   int used;    /*分配的標誌位,1表示已分配,0表示空閒*/
   int size;    /*該目錄指向的空間的大小*/
}catalog;

catalog *memu
; //目錄指標 void init() { /* 定義好一串目錄連結串列,用於查詢分配,個數為100個。同時給目錄進行初始化 ,其中,第一個目錄指向的就是未分配的總記憶體大小*/ memu = (catalog *)(memory + MEMSIZE - sizeof(catalog)*CATALOG_COUNT_MAX); memu[0].start = memory; memu[0].used = 0; memu[0].size = MEMSIZE - sizeof(catalog)*CATALOG_COUNT_MAX; int i; for(i = 1;i < CATALOG_COUNT_MAX; i++) { memu[i].start = NULL; memu[i].used = 0
; memu[i].size = 0; } } /* malloc 的實現方法 */ void *mymalloc(int size) { int i,j; /*查詢所有目錄,看是否有空閒的資料塊*/ for(i = 0;i < CATALOG_COUNT_MAX; i++) { /*找到了一塊資料塊*/ if(memu[i].size >= size && memu[i].used == 0) { /*標記為使用中*/ memu[i].used = 1; for(j = 0;j < CATALOG_COUNT_MAX; j++) { /*查詢目錄,尋找未有分配狀態的目錄*/ if(memu[j].used == 0 && memu[j].size == 0) { /*若找到,把該目錄指標置於已分配的空間的後面,並分配餘下空間大小,並返回*/ memu[j].start = memu[i].start + size; memu[j].size = memu[i].size - size; memu[i].size = size; return memu[i].start; } } break; } } return memu[i].start; } /*free 的實現方法*/ void *myfree(void *p) { int i, j, k; for(i = 0;i < CATALOG_COUNT_MAX; i++) { /*從目錄中尋找要釋放的記憶體,通過其指向的首地址和使用狀態來尋找*/ if(p == memu[i].start && memu[i].used == 1) { /*找到了,把標誌位清0。但此時工作並未結束,需要整理記憶體碎片*/ memu[i].used = 0; for(j = 0; j < CATALOG_COUNT_MAX; j++) { /*尋找目錄,找到指向要釋放的空間的後一塊記憶體,如果該記憶體仍在使用中,則放棄合併,如果未使用,此時把後一塊空間合併到要釋放空間的記憶體中來,組成一塊新的大的記憶體塊*/ if((memu[i].start + memu[i].size == memu[j].start) && memu[j].used == 0) { memu[j].start = NULL; memu[i].size = memu[j].size + memu[i].size; memu[j].size = 0; break; } } for(k = 0;k < CATALOG_COUNT_MAX;k++) { /*同上,尋找目錄,找到指向要釋放的空間的前一塊記憶體,如果該記憶體仍在使用中,則放棄合併,如果未使用,此時把要釋放的空間合併到前一塊空間的記憶體中來,組成一塊新的大的記憶體塊*/ if((memu[k].start + memu[k].size == memu[i].start) && memu[k].used == 0 ) { memu[i].start = NULL; memu[k].size = memu[i].size + memu[k].size; memu[i].size = 0; break; } } break; } } } /* 該主函式為壓力測試,如果夠完成9999次malloc和free,那麼表示該動態分配方法沒有問題 */ int main(int argc, char *argv[]) { init(); int i; char *pp[80]; int n; char ch; for(i = 0;i < 80;i++) pp[i] = "-1"; srand(time(NULL)); for(i = 0;i < 10000;i++) { printf("i = %d\n",i); n = random()%80; if(strcmp(pp[n], "-1") == 0) { pp[n] = (char *)mymalloc(random()%100 + 1); if(pp[n] == NULL) break; strcpy(pp[n], "a"); printf("malloc %d\t%x\n", n, pp[n]); } else { printf("free %d\t%x\n", n, pp[n]); myfree(pp[n]); pp[n] = "-1"; } }/* int *p = mymalloc(50); int *a = mymalloc(100); int *b = mymalloc(100); int *c = mymalloc(100); int *d = mymalloc(1000); int *f = mymalloc(7450); printf("%p\n",p); printf("%p\n",a); printf("%p\n",b); printf("%p\n",c); printf("%p\n",d); myfree(a); myfree(b); int *e = mymalloc(150); printf("%p\n",e); int *g = mymalloc(100); printf("%p\n",g); int *h = mymalloc(100); printf("%p\n",h); int *i = mymalloc(100); printf("%p\n",i); */ return 0; }

其結果如下:

i表示malloc和free的次數,如malloc 60 8049339 表示malloc 60byte大小空間地址為0x8049339。