memset 结构体内指针_数据结构之线性表应用——内存管理
大家好,我是隔壁小王,前面我給大家講了數(shù)據(jù)結(jié)構(gòu)中的線性表,因?yàn)槭堑谝淮螌懽?#xff0c;可能很多地方描述得不清楚,真是慚愧至極啊!這兩天我花了不少時(shí)間來學(xué)習(xí)如何寫作,也看了不少公眾號文章,今天我來講講線性表的應(yīng)用,加深大家對線性表的應(yīng)用。
不知道大家在電腦卡頓時(shí)會不會有這樣的情況,突然變得很卡,一般解決辦法就是關(guān)閉一些不必要的軟件或者更換更大的內(nèi)存條來改善。關(guān)閉軟件我們不討論,我們來討論下這個(gè)內(nèi)存。大家有沒有想過內(nèi)存插上之后,OS(Operatin System)是怎么管理內(nèi)存的,要知道,內(nèi)存條就是一個(gè)大倉庫,要是沒有倉庫管理人員,以及管理方法倉庫會有多么的混亂。
由此引出我們今天的主題——“內(nèi)存管理”,什么是內(nèi)存管理,即內(nèi)存管理是指軟件運(yùn)行時(shí)對計(jì)算機(jī)內(nèi)存資源的分配和使用的技術(shù)。內(nèi)存管理有很多方法,我們討論比較簡單的也適合移植到單片機(jī)系統(tǒng)上的分塊式內(nèi)存管理。
不難發(fā)現(xiàn),分塊式內(nèi)存管理由兩部分組成,內(nèi)存池——儲存數(shù)據(jù)的地方,內(nèi)存池被分成若干個(gè)塊。內(nèi)存管理表——用于管理內(nèi)存的分配釋放,管理表也被分成若干項(xiàng),每一項(xiàng)指像分好塊的內(nèi)存塊的首地址,講到這里,有沒有發(fā)現(xiàn)和我們上次講的線性表很相識啦,內(nèi)存塊就是線性表中的數(shù)據(jù)項(xiàng),管理表就是線性表中的索引,唯一的區(qū)別就在于內(nèi)存管理的數(shù)據(jù)是指針,指向內(nèi)存池。我們通過管理表來訪問內(nèi)存池。
假設(shè)內(nèi)存池大小為32Byte,現(xiàn)在我們需要65KiloByte的內(nèi)存,首先我們用65 / 32 = 2余1,需要兩塊內(nèi)存,但是余數(shù)也是需要內(nèi)存的,所以還需要多分配一塊內(nèi)存,所以總共需要3塊內(nèi)存,然后算法會從末尾往前面找3塊連續(xù)的內(nèi)存出來,接下來,是重要的一步,總得讓其他應(yīng)用知道,這3塊內(nèi)存是我的,誰也不能搶對吧!
所以我們要對這3塊內(nèi)存對應(yīng)的管理表做標(biāo)記,并且每一塊都要標(biāo)記
所以,就變成了下面這個(gè)亞子
這樣,應(yīng)用就分配到了3塊內(nèi)存,如果這個(gè)應(yīng)用不用了,要釋放內(nèi)存怎么辦,簡單!!直接修改管理表就可以了,真是渣男!在內(nèi)存池上留下這么多足跡,結(jié)果改一下管理表就不管不顧了!
現(xiàn)在原理基本講完了,大家應(yīng)該很清楚了吧!下面就是code time!(代碼時(shí)間)
#define MEM_BLOCK_SIZE 32 //內(nèi)存塊大小(Byte)#define MEM_MAX_SIZE 1 * 1024 //內(nèi)存池大小(KiloByte)#define MEM_TABLE_SIZE MEM_MAX_SIZE / MEM_BLOCK_SIZE //內(nèi)存管理表大小(Byte)static uint8_t membase[MEM_MAX_SIZE];static uint16_t memmap[MEM_TABLE_SIZE];typedef struct {void (*init)(void); //內(nèi)存初始化uint32_t (*perused)(void); //內(nèi)存使用量uint8_t* base; //內(nèi)存池uint16_t* map; //內(nèi)存管理表uint8_t ready; //內(nèi)存就緒}malloc_t;先是對定義內(nèi)存池的相關(guān)參數(shù),為了方便后面觀察,這里我們管理1kb的內(nèi)存,大家不要小看這1kb,在單片機(jī)系統(tǒng)里面1kb能儲存1024b數(shù)據(jù)呢,哈哈哈!
我們定義了一個(gè)內(nèi)存池和內(nèi)存管理表,并且定義了內(nèi)存管理的結(jié)構(gòu)體。
void mem_init(void); void Memset(void* dst, uint8_t c, uint32_t size); void* Memcpy(void* dst, void* src, uint32_t size); uint8_t mem_free(uint32_t ptr); uint32_t get_mem_perused(void); uint32_t mem_malloc(uint32_t count);void Free(void* ptr); void* Malloc(uint32_t size); void* Realloc(void* ptr, uint32_t size)然后是相關(guān)的函數(shù),其實(shí)用戶可以用到的就只有3個(gè)Malloc,Free,Realloc。
在使用內(nèi)存之前我們要先初始化內(nèi)存池和管理表
void mem_init(void) { Memset(membase, 0, MEM_MAX_SIZE); Memset(memmap, 0, MEM_TABLE_SIZE * 2); malloc_dev.ready = 1;}這樣,內(nèi)存池被打掃得干干凈凈,管理表也準(zhǔn)備開始工作
當(dāng)我們調(diào)用Malloc的時(shí)候,會放生什么呢?
程序會去訪問內(nèi)存管理表,并且找出連續(xù)可用的內(nèi)存塊出來,這里我并沒有直接返回對應(yīng)內(nèi)存池的地址,而是返回一個(gè)相對地址偏移量,這是為什么呢?
因?yàn)?#xff0c;這個(gè)偏移量是相對于地址0x00000000開始的,但是我們的內(nèi)存是的起始地址可能不是0x00000000,有可能是0x05201314,所以真實(shí)地址還得加上內(nèi)存池的起始地址,從程序的模塊化來說,我把這兩個(gè)操作分開了。
uint32_t mem_malloc(uint32_t size){ uint32_t offset; //地址偏移量 uint32_t mem_block; //需要的內(nèi)存塊數(shù) uint32_t mem_find = 0,i; //以找到的內(nèi)存塊數(shù) if (size == 0) return 0xFFFFFFFF; mem_block = size / MEM_BLOCK_SIZE; if (size % MEM_BLOCK_SIZE) mem_block++; for (offset = MEM_BLOCK_SIZE - 1; offset > 0; offset--) { if(!malloc_dev.map[offset]) mem_find++; else mem_find = 0; if (mem_find == mem_block){ for (i = 0; i < mem_find; i++){ malloc_dev.map[offset + i] = (uint16_t)mem_find; //標(biāo)記內(nèi)存 } return offset * MEM_BLOCK_SIZE; }}return 0xFFFFFFFF;}當(dāng)我們獲取到了相對偏移地址之后,咱們加上起始地址,是不是就得到了對應(yīng)在內(nèi)存池中的地址啦?很簡單吧!
void* Malloc(uint32_t size) { uint32_t offset; offset = mem_malloc(size); if (offset == 0xFFFFFFFF) return NULL; return (void*)(malloc_dev.base + offset);}申請內(nèi)存是標(biāo)記內(nèi)存管理表,那釋放內(nèi)存不就是申請的逆過程嘛,所以我們只要找到需要釋放的內(nèi)存對應(yīng)的管理表,將標(biāo)記取消,拍拍屁股走人,就對了,因?yàn)闆]有清空內(nèi)存池中的數(shù)據(jù),所以,大家在使用指針申請內(nèi)存的時(shí)候有沒有發(fā)現(xiàn),如果打印的話,會有隨機(jī)值,雖然在一開始我們初始化了內(nèi)存池,它特別干凈,但是經(jīng)過一段時(shí)間得運(yùn)行,應(yīng)用A使用一點(diǎn),應(yīng)用B也使用一點(diǎn),釋放的時(shí)候也沒有去清空內(nèi)存池,導(dǎo)致內(nèi)存池中的數(shù)據(jù)是隨機(jī)的,這就是上一個(gè)應(yīng)用不負(fù)責(zé)任的后果,讓下一個(gè)應(yīng)用當(dāng)老實(shí)人,要是不注意的話,這盤,哼哼哼哼。。。。
uint8_t mem_free(uint32_t ptr) { uint32_t i; if (!malloc_dev.ready) { malloc_dev.init(); return 1; } if (ptr < MEM_MAX_SIZE) { uint32_t index = ptr / MEM_BLOCK_SIZE; uint32_t mem_block = malloc_dev.map[index]; for (i = 0; i < mem_block; i++) { malloc_dev.map[index + i] = 0; } return 0;}return 2;}代碼很簡單,就是從管理表找到內(nèi)存塊的數(shù)量,然后依次清零,它的父級函數(shù)
void Free(void* ptr) {uint32_t offset;if (ptr == NULL) return; offset = (uint32_t)ptr - (uint32_t)malloc_dev.base; mem_free(offset);}我們從當(dāng)前地址去找到管理表的偏移量,然后在調(diào)用子級函數(shù)做其他處理
好啦,現(xiàn)在內(nèi)存管理的重要函數(shù)我們已經(jīng)講完了,接下來我們就來測試下吧
int main () { malloc_dev.init(); char* a = (char*)Malloc(sizeof(char)*1); printf("a address:0x%p", a); int i; printf("Memory manahement table(dir--->)(size:%d):", MEM_TABLE_SIZE); for (i = 0; i < 32; i++) { printf("%d ", malloc_dev.map[i]); } printf(""); printf("a content:"); strcpy(a, "I Love You"); printf("%s", a); printf("used:%.2f", malloc_dev.perused()/1000.0); Free(a); printf("used:%.2f", malloc_dev.perused()/1000.0); return 0;}下面是運(yùn)行結(jié)果
從圖中可以看到,變量a獲取到的內(nèi)存地址,與我們在malloc里面通過相對偏移加上起始地址之后的地址是一致的,并且通過內(nèi)存管理表可以看到占用的內(nèi)存塊的個(gè)數(shù),以及使用率。
這里不知道大家有沒有發(fā)現(xiàn)一個(gè)問題,我們申請一個(gè)char類型的內(nèi)存,也就是一個(gè)字節(jié),但是系統(tǒng)卻返回給我們的是一個(gè)內(nèi)存塊!而這里我們內(nèi)存塊的單位是32字節(jié),也就是說我們還有31字節(jié)沒有使用到,資源浪費(fèi)太多,但是如果我們縮小內(nèi)存塊的單位,勢必管理表的數(shù)量又會增加,在每次分配或者釋放的時(shí)候,照成時(shí)間延時(shí)比較多,所以如何權(quán)衡內(nèi)存塊的最小值,是一個(gè)難題。
但是我們還有其他更優(yōu)秀的算法來彌補(bǔ)這個(gè)分塊管理的缺陷,這里我就不做過多的討論了,在單片機(jī)系統(tǒng)里面,我們對內(nèi)存沒有很嚴(yán)格的要求,所以這個(gè)算法也就被經(jīng)常使用了,因?yàn)閱纹瑱C(jī)系統(tǒng)往往主頻小,運(yùn)行速度比不上PC,像紅黑樹那些算法跑起來需要較多的時(shí)間。
同時(shí),我也在公眾號:GeEes
關(guān)注并回復(fù):002
或者關(guān)注頭條號并私信:002
都可以獲得本文的參考資料和全部內(nèi)容!
非常感謝你的關(guān)注!
總結(jié)
以上是生活随笔為你收集整理的memset 结构体内指针_数据结构之线性表应用——内存管理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringMVC和SpringBoot
- 下一篇: 拦截器中addInterceptor和e