zephyr 系统--- 内存池使用方法
內(nèi)存池(memory pool) 是一個內(nèi)核對象,它允許從指定的內(nèi)存區(qū)域上動態(tài)地分配內(nèi)存塊(memory block)。同一個內(nèi)存池中的內(nèi)存塊的大小是不固定的,這樣可以減小由于不同的應(yīng)用程序需要為大小不同的數(shù)據(jù)結(jié)構(gòu)分配不同的存儲空間所造成的浪費。內(nèi)存池使用“伙伴(buddy)內(nèi)存分配”算法,它可以高效地將大塊內(nèi)存分割為小塊內(nèi)存。此外,它還可以在最大限度減小內(nèi)存碎片的前提下,高效地分配和釋放不小不同的內(nèi)存塊。
概念
可以定義任意數(shù)量的內(nèi)存池。每個內(nèi)存池通過其內(nèi)存地址進(jìn)行引用。
內(nèi)存池的關(guān)鍵屬性包括:
- 塊最小尺寸:以字節(jié)為單位。大于等于 4X 字節(jié),其 0。
- 塊最大尺寸:以字節(jié)為單位。等于 “塊最小尺寸”*4^Y,其中 Y 大于等于 0。
- 最大尺寸塊的數(shù)量:大于 0。
- buffer:內(nèi)存池的塊的實際內(nèi)存區(qū)域。它必須大于等于 “塊最大尺寸”*“最大尺寸塊的數(shù)量”。
內(nèi)存片的 buffer 必須 N 字節(jié)對齊,其中 N 是大于 2 的 2 的整數(shù)次冪(例如 4,8,16…)。為了保證 buffer 中的所有內(nèi)存塊都對齊到這個邊界,塊的大小必須是 N 的整數(shù)倍。
當(dāng)線程需要內(nèi)存塊時,它只需要從一個內(nèi)存池中申請。申請成功后,由線程提供的塊描述符的 data 字段表示該內(nèi)存塊的起始地址。當(dāng)線程使用完內(nèi)存塊后,它必須將其釋放給內(nèi)存池,讓其可以重復(fù)利用。
如果沒有找到所期望的內(nèi)存塊,線程可以等待,直到某個塊可用。多個線程可以同時等待某個空的內(nèi)存池;當(dāng)某個內(nèi)存塊可用時,它會被分配給優(yōu)先級最高的、等待時間最久的線程使用。
與堆不同的是,如果有需要,可以定義多個內(nèi)存片。例如不同的應(yīng)用程序可以利用不同的內(nèi)存池。這樣可以阻止某個應(yīng)用程序“綁架”所有資源。
內(nèi)部操作
內(nèi)存池的 buffer 是一個數(shù)組,數(shù)組的元素的大小是塊的最大尺寸,這樣能保證塊與塊之間沒有空間被浪費。每個“第 0 級”的塊是一個 quad-block,(如果有需要)可以被分為四個小的大小相等的“第 1 級”塊。類似地,每個第 1 級塊也是 quad-block,也可以被分為四個小的大小相等的“第 2 級”塊。依次類推。因此,每個內(nèi)存塊都可以遞歸地分為四分之一的小塊,知道小塊的尺寸不滿足塊最小尺寸。
內(nèi)存池通過一個叫做 塊集(block set) 的數(shù)據(jù)結(jié)構(gòu)來跟蹤它的 buffer 空間的分區(qū)情況。內(nèi)存池為所支持的每一個劃分等級或者每一個塊尺寸都維持了一個塊集。每個塊集使用一個叫做 quad-block 狀態(tài) 的數(shù)據(jù)結(jié)構(gòu)的數(shù)組來跟蹤它所關(guān)聯(lián)的尺寸的所有空閑塊。
當(dāng)應(yīng)用程序請求一個內(nèi)存塊時,內(nèi)存池首先會判斷最小塊的尺寸是否滿足請求,并檢查其相應(yīng)的塊集。如果塊集包含有一個空閑塊,它會將該塊標(biāo)記為以使用,然后分配過程就結(jié)束了。如果該塊集不包含空閑塊,內(nèi)存池將嘗試將一個更大尺寸的空閑塊分類成小的塊,或者將小塊合并為大的塊。如果不能創(chuàng)建這樣的塊,則分配失敗。
內(nèi)存池的塊合并和分裂過程是非常高效的,但是它采用的是遞歸算法,因此很容易產(chǎn)生顯著的開銷。此外,合并算法不能將大小不同的相鄰塊結(jié)合在一起,也不能合并不屬于同一個父 quad-block 的尺寸相同的相鄰塊。因此,使用內(nèi)存池時依然會遇到碎片問題。
當(dāng)應(yīng)用程序釋放一個已分配的內(nèi)存塊時,僅僅會在該內(nèi)存塊所關(guān)聯(lián)塊集中將其標(biāo)記為空閑。內(nèi)存池不會嘗試合并最近釋放的塊,這樣的好處是可以很方便地在其已存在的組織上進(jìn)行重分配。
實現(xiàn)
定義內(nèi)存池
使用類型為 struct k_mem_pool 的變量可以定義一個內(nèi)存池。不過,由于內(nèi)存池也需要大量的尺寸可變的數(shù)據(jù)結(jié)構(gòu)來代表它的塊集合和它的 quad-block 的狀態(tài),內(nèi)核不支持在運行時動態(tài)地定義內(nèi)存池。內(nèi)存池只能使用 K_MEM_POOL_DEFINE 在編譯時進(jìn)行定義和初始化。
下面的代碼定義并初始化了一個內(nèi)存池,這個內(nèi)存池有三個大小為 4096 字節(jié)的塊。這些塊也可以被劃分為最小為 64 字節(jié)的 4 字節(jié)對齊的子塊。(也就是說,內(nèi)存池支持的塊大小是 4096、1024、256 和 64 字節(jié)。)注意,該宏定義了內(nèi)存池的所有數(shù)據(jù)結(jié)構(gòu)和它的 buffer。
| K_MEM_POOL_DEFINE(my_pool, 64, 4096, 3, 4); |
分配內(nèi)存塊
函數(shù) k_mem_pool_alloc() 用于分配內(nèi)存塊。
下面的代碼會先等待 100 毫秒,以拿到一個 200 字節(jié)的可以內(nèi)存塊,然后將其填充為零。如果沒有獲得合適的內(nèi)存塊,代碼會打印一個警告信息。
注意,應(yīng)用程序?qū)嶋H會接收到一個大小為 256 字節(jié)的內(nèi)存塊,因為這是內(nèi)存池所支持的最接近的尺寸。
| struct k_mem_block block;if (k_mem_pool_alloc(&my_pool, &block, 200, 100) == 0)) { memset(block.data, 0, 200);...} else { printf("Memory allocation time-out");} |
釋放內(nèi)存塊
函數(shù) k_mem_pool_free() 用于釋放內(nèi)存塊。
下面的代碼基于上面的例程之上,它申請了 75 字節(jié)的內(nèi)存塊,并在不再使用時釋放。(基于安全考慮,實際上會從堆內(nèi)存池使用 256 字節(jié)的內(nèi)存塊。)
| struct k_mem_block block;k_mem_pool_alloc(&my_pool, &block, 75, K_FOREVER);... /* use memory block */k_mem_pool_free(&block); |
建議的用法
當(dāng)需要分配大小不固定的內(nèi)存時,可以使用內(nèi)存池。
當(dāng)一個線程需要給另一個線程發(fā)送大量的數(shù)據(jù)時,可以使用內(nèi)存池,這樣可以避免不必要的數(shù)據(jù)拷貝。
API
頭文件 kernel.h 中提供了如下的內(nèi)存池 API:
- K_MEM_POOL_DEFINE
- k_mem_pool_alloc()
- k_mem_pool_free()
- http://zephyrproject.cn/zh-cn/docs/latest/k_memory_pool.html
總結(jié)
以上是生活随笔為你收集整理的zephyr 系统--- 内存池使用方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 说说FATFS文件系统(转)
- 下一篇: fatfs源码阅读