最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

內(nèi)存分配器memblock

2022-08-06 14:09 作者:補(bǔ)給站Linux內(nèi)核  | 我要投稿

背景

在Linux內(nèi)核開發(fā)過程中, 多少都會(huì)存在一個(gè)patch, 引入了遠(yuǎn)超預(yù)期的麻煩. 內(nèi)核2.6.34開發(fā)過程中, 這個(gè)獎(jiǎng)項(xiàng)非CONFIG_NO_BOOTMEM莫屬

bootmem本身是個(gè)簡(jiǎn)單的,低級(jí)的內(nèi)存分配器. 在引導(dǎo)程序的初期用來分配內(nèi)存. 有人可能會(huì)想, 沒有必要再增加一個(gè)內(nèi)存分配器, 但是由于內(nèi)存管理代碼在被調(diào)用前需要很多內(nèi)核功能都準(zhǔn)備好, 要想在啟動(dòng)初期使用內(nèi)存管理代碼會(huì)大大增加內(nèi)存管理的復(fù)雜性. 在x86架構(gòu)上, 會(huì)首先使用early_res機(jī)制接替BIOS e820的工作, 然后再交給架構(gòu)獨(dú)立的bootmem分配器, 最后才是全功能的buddy allocator

ingHai LU認(rèn)為可以把bootmem從這個(gè)過程中去掉, 簡(jiǎn)化這個(gè)過程. 結(jié)果就是, 增加了一堆patch來擴(kuò)展early_res機(jī)制, 把本該交給bootmem做的事情都做了, 然后直接到buddy分配器. 這些修改被合入了2.6.34, 老的基于bootmem的代碼仍然保留. CONFIG_NO_BOOTMEM用來控制使用哪個(gè)分配器, 缺省情況下并不使用bootmem

一切本該非常美好, 但是隨著kernel rc版本的發(fā)布, 新分配器測(cè)試陸續(xù)爆出很多問題, Linus發(fā)了一封郵件要求revert掉整個(gè)patch. 雖然簡(jiǎn)化代碼這個(gè)注意看起來不錯(cuò), 但是rc3仍然導(dǎo)致系統(tǒng)死機(jī), 以及大量使用ifdef, 以及缺省打開CONFIG_NO_BOOTMEM, 導(dǎo)致了社區(qū)的怨氣.

正常情況下, 新功能缺省情況下是不使能的. 新kernel應(yīng)該盡最大可能和之前的kernel保持一致. 而CONFIG_NO_BOOTMEM缺省打開導(dǎo)致了很大的改變和問題. Yinghai在2.6.35基礎(chǔ)上又提交了一組patch, 使用logical memory block分配器替代early_res代碼, 這組patch看起來比刪除bootmem引入了更大的風(fēng)險(xiǎn) --https://lwn.net/Articles/382559/

在Yinghai刪除bootmem patch的review過程中, 一些reviewers質(zhì)疑為什么x86不使用logical memory block(LMB)分配器替換early-res的代碼. 當(dāng)X86使用類似brk()形式的early-res時(shí) Microblaze, PowerPC, SuperH和SPARC架構(gòu)已經(jīng)使用LMB進(jìn)行系統(tǒng)啟動(dòng)初期的內(nèi)存分配, 所以LMB可以看做是一個(gè)generic的解決方案. 使用通用代碼的好處是顯而易見的: 更多的人review代碼, 總體維護(hù)代價(jià)更帶. 所以使用LMB明顯更合理.

因此Yinghai在2.6.35上又提交了一組patch來簡(jiǎn)化啟動(dòng)分配器代碼


【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個(gè)人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)? ??


Data structure

struct memblock { ? 
 ? ?bool bottom_up; ?/* is bottom up direction? */
 ? ?phys_addr_t current_limit;
 ? ?struct memblock_type memory;
 ? ?struct memblock_type reserved;
};

bootom_up 設(shè)置為true時(shí), 允許內(nèi)存分配使用bottom-up模式

current_limit memblock分配內(nèi)存時(shí)的上限

memory 描述了當(dāng)前內(nèi)存區(qū)包含的內(nèi)存區(qū)數(shù)目, 總大小, 以及每個(gè)內(nèi)存region

reserved 描述了當(dāng)前內(nèi)存塊已經(jīng)分配的內(nèi)存區(qū)數(shù)目, 總大小,以及每個(gè)內(nèi)存region. 在reserved中描述的地址范圍, 表示不可以再被memblock分配. memory用來描述memblock全部?jī)?nèi)存region(不區(qū)分分配和未分配), reserved用來描述memblock中已經(jīng)分配的內(nèi)存region

struct memblock_type {
 ? ?unsigned long cnt; ?/* number of regions */
 ? ?unsigned long max; ?/* size of the allocated array */
 ? ?phys_addr_t total_size; /* size of all regions */
 ? ?struct memblock_region *regions;
};

cnt regions數(shù)目

max 最大regions數(shù)目

total_size regions總尺寸

regions regions array


struct memblock_region {
 ? ?phys_addr_t base;
 ? ?phys_addr_t size;
 ? ?unsigned long flags;
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
 ? ?int nid;
#endif
};

base region 基地址

size region size

flags region

memblock initialization

static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;
static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;
 
struct memblock memblock __initdata_memblock = {
 ? ?.memory.regions ? ? = memblock_memory_init_regions,
 ? ?.memory.cnt ? ? = 1, ? ?/* empty dummy entry */
 ? ?.memory.max ? ? = INIT_MEMBLOCK_REGIONS,
 
 ? ?.reserved.regions ? = memblock_reserved_init_regions,
 ? ?.reserved.cnt ? ? ? = 1, ? ?/* empty dummy entry */
 ? ?.reserved.max ? ? ? = INIT_MEMBLOCK_REGIONS,
 
 ? ?.bottom_up ? ? ?= false,
 ? ?.current_limit ? ? ?= MEMBLOCK_ALLOC_ANYWHERE,
};

.memory.regions 和reserved.regions固定數(shù)組, 最多支持128個(gè)regions

memory.mx和reserved.max的最大值也為128

current_limit 也被定義為最大可能物理地址

memblock API 在include/linux/memblock.h中定義了memblock的API

int __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size)
{
 ? ?return memblock_reserve_region(base, size, MAX_NUMNODES, 0);
}

memblock_reserve API主要是為系統(tǒng)啟動(dòng)階段為kernel(text, data and initrd), swapper_pg_dir, reserved-memory, memreserve等預(yù)留內(nèi)存.

int __init_memblock memblock_remove(phys_addr_t base, phys_addr_t size)
{
 ? ?return __memblock_remove(&memblock.memory, base, size);
}

系統(tǒng)中有兩處會(huì)調(diào)用memblock_remove:

  1. early_init_dt_reserve_memory_arch中, 如果不希望這段內(nèi)存被映射, 那么就調(diào)用memblock_remove, 把這段內(nèi)存從memblock.memory中移除

  2. arm_memblock_steal中, 調(diào)用memblock_remove從memblock.memory中偷一段內(nèi)存空間, memblock.memory的regions中將不再包含這段內(nèi)存空間.


int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size)
{
 ? ?return memblock_add_region(&memblock.memory, base, size,
 ? ? ? ? ? ? ? ? ? MAX_NUMNODES, 0);
}

在memblock的memory type中增加一個(gè)region

base 是新增region的基地址

size 是新增region的尺寸

對(duì)于arm, 僅一處會(huì)調(diào)用memblock_add: arm_memblock_init 中根據(jù)meminfo向memblock.memory中增加region

memory和reserved region memblock有兩個(gè)memblock_type成員: memory和reserved

memblock.memory 描述memblock所有內(nèi)存區(qū)(已分配的+未分配的) memblock.reserved 描述已經(jīng)分配的內(nèi)存區(qū), 所有分配和釋放操作都是通過修改reserved來實(shí)現(xiàn)的. 分配操作在memblock.reserved上增加region, 釋放操作則memblock.reserved上釋放region. memblock_reserved

int __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size)
{
 ? ?return memblock_reserve_region(base, size, MAX_NUMNODES, 0);
}
 
static int __init_memblock memblock_reserve_region(phys_addr_t base,
 ? ? ? ? ? ? ? ? ? ? ? ? ? phys_addr_t size,
 ? ? ? ? ? ? ? ? ? ? ? ? ? int nid,
 ? ? ? ? ? ? ? ? ? ? ? ? ? unsigned long flags)
{
 ? ?struct memblock_type *_rgn = &memblock.reserved;
 ? ?return memblock_add_region(_rgn, base, size, nid, flags);
}

memblock_reserve -> memblock_reserve_region -> memblock_add_region

調(diào)用memblock_add_region時(shí)第一個(gè)參數(shù)為 memblock.reserved, 也就是說在memblock.reserved增加一個(gè)region memblock_add_region流程如下:

  1. 如果type->regions[0].size==0, 表示regions數(shù)組為空, 添加region[0], 設(shè)置region[0]相關(guān)成員,即可返回. 此時(shí)該memblock_type有一個(gè)region了

  2. 計(jì)算要插入的region數(shù)目, [base, base + size]可能會(huì)跨越多個(gè)已存在的region, 因此數(shù)目可能不為1.

  3. 由于要插入新region, 所以需要先擴(kuò)展regions array.

  4. 插入這些regions

  5. 執(zhí)行region merge操作

我們可以看到memblock_reserve()操作壓根就沒考慮memblock.memory, 因?yàn)閙emblock_reserve()只是告訴memblock, 這部分內(nèi)存被保留了, 不要再參與memblock分配操作

memblock_alloc

phys_addr_t __init memblock_alloc(phys_addr_t size, phys_addr_t align)
{
 ? ?return memblock_alloc_base(size, align, MEMBLOCK_ALLOC_ACCESSIBLE);
}
 
phys_addr_t __init memblock_alloc_try_nid(phys_addr_t size, phys_addr_t align, int nid)
{
 ? ?phys_addr_t res = memblock_alloc_nid(size, align, nid);
 
 ? ?if (res)
 ? ? ? ?return res;
 ? ?return memblock_alloc_base(size, align, MEMBLOCK_ALLOC_ACCESSIBLE);
}

memblock_alloc->memblock_alloc_base->memblock_alloc_base_nid

static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size,
 ? ? ? ? ? ? ? ? ? ?phys_addr_t align, phys_addr_t max_addr,
 ? ? ? ? ? ? ? ? ? ?int nid)
{
 ? ?phys_addr_t found;
 
 ? ?if (!align)
 ? ? ? ?align = SMP_CACHE_BYTES;
 
 ? ?found = memblock_find_in_range_node(size, align, 0, max_addr, nid);
 ? ?if (found && !memblock_reserve(found, size))
 ? ? ? ?return found;
 
 ? ?return 0;
}
  1. memblock_find_in_range_node查找符合條件的物理地址, 查找過程會(huì)涉及到查看memblock.reserve

  2. 如果找到了這個(gè)物理地址, 調(diào)用memblock_reserve進(jìn)行真正的分配(就是在memblock.reserve中添加region) memblock_free 釋放參數(shù)指定的內(nèi)存區(qū)間

int __init_memblock memblock_free(phys_addr_t base, phys_addr_t size)
{
 ? ?memblock_dbg(" ? memblock_free: [%#016llx-%#016llx] %pF\n",
 ? ? ? ? ? ? (unsigned long long)base,
 ? ? ? ? ? ? (unsigned long long)base + size - 1,
 ? ? ? ? ? ? (void *)_RET_IP_);
 
 ? ?return __memblock_remove(&memblock.reserved, base, size);
}

邏輯很簡(jiǎn)單, 從memblock.reserved中刪除[base, base+size]指定范圍的region. 這樣下次調(diào)用memblock_alloc時(shí)再檢查reserved type時(shí), 這段區(qū)域可以再次使用了











內(nèi)存分配器memblock的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
广昌县| 什邡市| 临漳县| 思南县| 甘洛县| 白河县| 曲松县| 钟山县| 黄大仙区| 勃利县| 涟源市| 华安县| 丹江口市| 文成县| 陆川县| 象山县| 西乌珠穆沁旗| 安达市| 蚌埠市| 遂川县| 沁阳市| 高阳县| 板桥市| 新疆| 碌曲县| 虹口区| 昌宁县| 咸阳市| 西峡县| 梧州市| 庆城县| 盱眙县| 澳门| 息烽县| 孝感市| 图木舒克市| 绥中县| 宜春市| 乌拉特中旗| 佛坪县| 清水县|