memblockアルゴリズムは比較的単純な達成するために、(元bootmemアルゴリズムを置き換える)メモリアロケータのLinuxカーネルの初期化段階です。ページアロケータの初期化前のメモリ管理と割り当て要求を担当。
Memblock解析アルゴリズム、我々はいくつかのポイントから開始することができます:
- memblockアルゴリズムの初期化。
- アプリケーションとmemblockメモリ管理アルゴリズムをリリース。
memblockアルゴリズム前の準備:
既に初期化中のLinuxシステムメモリの分布を分析し、使用データ内部boot_params.e820_mapに格納されているマシン(E820図)、15ディスカバリーをINT割り込み、何照合データは、すべての後、解体、存在しませんBIOSのメモリの統合は何かをする一切の責任なので、システムによって達成されるもののこの部分を持っていません。その後のLinuxの機能のいくつかは、これを実現する方法を見て、機能のこの部分は、内側setup_memory_mapに実装されています。
この機能は、初期設定の位置に呼び出されます。
start_kernel()
└->setup_arch()
└->setup_memory_map();
達成するために機能:
【file:/arch/x86/kernel/e820.c】
void __init setup_memory_map(void)
{
char *who;
who = x86_init.resources.memory_setup();
memcpy(&e820_saved, &e820, sizeof(struct e820map));
printk(KERN_INFO "e820: BIOS-provided physical RAM map:\n");
e820_print_map(who);
}
我々は最初のフック関数を呼び出して、達成するのは非常に簡単で参照して、内部のe820_savedするE820を保存し、さらに下の印刷機能があることができます。もちろん、あなたがx86_init x86_init.cにおける構造体変数内の定義を見つけることができ、重要なポイントは、このフック関数を達成することである見ることができます。
【file:/arch/x86/kernel/x86_init.c】
struct x86_init_ops x86_init __initdata = {
.resources = {
.probe_roms = probe_roms,
.reserve_resources = reserve_standard_io_resources,
.memory_setup = default_machine_specific_memory_setup,
},
.mpparse = {
.mpc_record = x86_init_uint_noop,
.setup_ioapic_ids = x86_init_noop,
.mpc_apic_id = default_mpc_apic_id,
.smp_read_mpc_oem = default_smp_read_mpc_oem,
.mpc_oem_bus_info = default_mpc_oem_bus_info,
.find_smp_config = default_find_smp_config,
.get_smp_config = default_get_smp_config,
},
.irqs = {
.pre_vector_init = init_ISA_irqs,
.intr_init = native_init_IRQ,
.trap_init = x86_init_noop,
},
.oem = {
.arch_setup = x86_init_noop,
.banner = default_banner,
},
.paging = {
.pagetable_init = native_pagetable_init,
},
.timers = {
.setup_percpu_clockev = setup_boot_APIC_clock,
.tsc_pre_init = x86_init_noop,
.timer_init = hpet_time_init,
.wallclock_init = x86_init_noop,
},
.iommu = {
.iommu_init = iommu_init_noop,
},
.pci = {
.init = x86_default_pci_init,
.init_irq = x86_default_pci_init_irq,
.fixup_irqs = x86_default_pci_fixup_irqs,
},
};
)(吊り下げフック関数がdefault_machine_specific_memory_setupされていることが分かります。
また、関数の実装を見てみましょう。
【file:/arch/x86/kernel/e820.c】
char *__init default_machine_specific_memory_setup(void)
{
char *who = "BIOS-e820";
u32 new_nr;
/*
* Try to copy the BIOS-supplied E820-map.
*
* Otherwise fake a memory map; one section from 0k->640k,
* the next section from 1mb->appropriate_mem_k
*/
new_nr = boot_params.e820_entries;
sanitize_e820_map(boot_params.e820_map,
ARRAY_SIZE(boot_params.e820_map),
&new_nr);
boot_params.e820_entries = new_nr;
if (append_e820_map(boot_params.e820_map, boot_params.e820_entries)
< 0) {
u64 mem_size;
/* compare results from other methods and take the greater */
if (boot_params.alt_mem_k
< boot_params.screen_info.ext_mem_k) {
mem_size = boot_params.screen_info.ext_mem_k;
who = "BIOS-88";
} else {
mem_size = boot_params.alt_mem_k;
who = "BIOS-e801";
}
e820.nr_map = 0;
e820_add_region(0, LOWMEMSIZE(), E820_RAM);
e820_add_region(HIGH_MEMORY, mem_size << 10, E820_RAM);
}
/* In case someone cares... */
return who;
}
検出boot_params.e820_mapメモリレイアウト情報の正面に見ることができるこの機能では、それがここで使用されます。
まず、達成するための機能を処理するための方法E820 sanitize_e820_map情報の機能を分析します:
【file:/arch/x86/kernel/e820.c】
int __init sanitize_e820_map(struct e820entry *biosmap, int max_nr_map,
u32 *pnr_map)
{
static struct change_member change_point_list[2*E820_X_MAX] __initdata;
static struct change_member *change_point[2*E820_X_MAX] __initdata;
static struct e820entry *overlap_list[E820_X_MAX] __initdata;
static struct e820entry new_bios[E820_X_MAX] __initdata;
unsigned long current_type, last_type;
unsigned long long last_addr;
int chgidx;
int overlap_entries;
int new_bios_entry;
int old_nr, new_nr, chg_nr;
int i;
/* if there's only one memory region, don't bother */
if (*pnr_map < 2)
return -1;
old_nr = *pnr_map;
BUG_ON(old_nr > max_nr_map);
/* bail out if we find any unreasonable addresses in bios map */
for (i = 0; i < old_nr; i++)
if (biosmap[i].addr + biosmap[i].size < biosmap[i].addr)
return -1;
/* create pointers for initial change-point information (for sorting) */
for (i = 0; i < 2 * old_nr; i++)
change_point[i] = &change_point_list[i];
/* record all known change-points (starting and ending addresses),
omitting those that are for empty memory regions */
chgidx = 0;
for (i = 0; i < old_nr; i++) {
if (biosmap[i].size != 0) {
change_point[chgidx]->addr = biosmap[i].addr;
change_point[chgidx++]->pbios = &biosmap[i];
change_point[chgidx]->addr = biosmap[i].addr +
biosmap[i].size;
change_point[chgidx++]->pbios = &biosmap[i];
}
}
chg_nr = chgidx;
/* sort change-point list by memory addresses (low -> high) */
sort(change_point, chg_nr, sizeof *change_point, cpcompare, NULL);
/* create a new bios memory map, removing overlaps */
overlap_entries = 0; /* number of entries in the overlap table */
new_bios_entry = 0; /* index for creating new bios map entries */
last_type = 0; /* start with undefined memory type */
last_addr = 0; /* start with 0 as last starting address */
/* loop through change-points, determining affect on the new bios map */
for (chgidx = 0; chgidx < chg_nr; chgidx++) {
/* keep track of all overlapping bios entries */
if (change_point[chgidx]->addr ==
change_point[chgidx]->pbios->addr) {
/*
* add map entry to overlap list (> 1 entry
* implies an overlap)
*/
overlap_list[overlap_entries++] =
change_point[chgidx]->pbios;
} else {
/*
* remove entry from list (order independent,
* so swap with last)
*/
for (i = 0; i < overlap_entries; i++) {
if (overlap_list[i] ==
change_point[chgidx]->pbios)
overlap_list[i] =
overlap_list[overlap_entries-1];
}
overlap_entries--;
}
/*
* if there are overlapping entries, decide which
* "type" to use (larger value takes precedence --
* 1=usable, 2,3,4,4+=unusable)
*/
current_type = 0;
for (i = 0; i < overlap_entries; i++)
if (overlap_list[i]->type > current_type)
current_type = overlap_list[i]->type;
/*
* continue building up new bios map based on this
* information
*/
if (current_type != last_type) {
if (last_type != 0) {
new_bios[new_bios_entry].size =
change_point[chgidx]->addr - last_addr;
/*
* move forward only if the new size
* was non-zero
*/
if (new_bios[new_bios_entry].size != 0)
/*
* no more space left for new
* bios entries ?
*/
if (++new_bios_entry >= max_nr_map)
break;
}
if (current_type != 0) {
new_bios[new_bios_entry].addr =
change_point[chgidx]->addr;
new_bios[new_bios_entry].type = current_type;
last_addr = change_point[chgidx]->addr;
}
last_type = current_type;
}
}
/* retain count for new bios entries */
new_nr = new_bios_entry;
/* copy new bios mapping into original location */
memcpy(biosmap, new_bios, new_nr * sizeof(struct e820entry));
*pnr_map = new_nr;
return 0;
}
ループの最初:
/* bail out if we find any unreasonable addresses in bios map */
for (i = 0; i < old_nr; i++)
if (biosmap[i].addr + biosmap[i].size < biosmap[i].addr)
return -1;
ここでは不合理なメモリレイアウト情報項目が存在するかどうかをテストし、総合的な検査を行うためのE820は、その後、終了するには、合理的なエントリはありません。
ループのための第二:
/* create pointers for initial change-point information (for sorting) */
for (i = 0; i < 2 * old_nr; i++)
change_point[i] = &change_point_list[i];
実際のchange_point_listでchange_pointとchange_point_list仲間は、本当に役割を果たしている、唯一のスタック領域を占めるchange_pointです。
ループとソート機能を一緒に分析のための第三の呼び出し:
/* record all known change-points (starting and ending addresses),
omitting those that are for empty memory regions */
chgidx = 0;
for (i = 0; i < old_nr; i++) {
if (biosmap[i].size != 0) {
change_point[chgidx]->addr = biosmap[i].addr;
change_point[chgidx++]->pbios = &biosmap[i];
change_point[chgidx]->addr = biosmap[i].addr +
biosmap[i].size;
change_point[chgidx++]->pbios = &biosmap[i];
}
}
chg_nr = chgidx;
/* sort change-point list by memory addresses (low -> high) */
sort(change_point, chg_nr, sizeof *change_point, cpcompare, NULL);
ここでは、change_pointが初期化され、boot_params.e820_mapの開始と終了アドレスに関連付けられ、その後、ソートでソートされています。その結果、メモリのメモリレイアウトは、開始と終了アドレスがローからハイにソートされているマーク情報の一種です。2つのアドレス値が等しい場合、2つの場所が後ろの列の値が等しい、終了後に、より多くのスペースである、尾を行うソートするe820_mapアイテム情報メモリ空間をマーク。
最後に、ループを探します。
/* loop through change-points, determining affect on the new bios map */
for (chgidx = 0; chgidx < chg_nr; chgidx++) {
/* keep track of all overlapping bios entries */
if (change_point[chgidx]->addr ==
change_point[chgidx]->pbios->addr) {
/*
* add map entry to overlap list (> 1 entry
* implies an overlap)
*/
overlap_list[overlap_entries++] =
change_point[chgidx]->pbios;
} else {
/*
* remove entry from list (order independent,
* so swap with last)
*/
for (i = 0; i < overlap_entries; i++) {
if (overlap_list[i] ==
change_point[chgidx]->pbios)
overlap_list[i] =
overlap_list[overlap_entries-1];
}
overlap_entries--;
}
/*
* if there are overlapping entries, decide which
* "type" to use (larger value takes precedence --
* 1=usable, 2,3,4,4+=unusable)
*/
current_type = 0;
for (i = 0; i < overlap_entries; i++)
if (overlap_list[i]->type > current_type)
current_type = overlap_list[i]->type;
/*
* continue building up new bios map based on this
* information
*/
if (current_type != last_type) {
if (last_type != 0) {
new_bios[new_bios_entry].size =
change_point[chgidx]->addr - last_addr;
/*
* move forward only if the new size
* was non-zero
*/
if (new_bios[new_bios_entry].size != 0)
/*
* no more space left for new
* bios entries ?
*/
if (++new_bios_entry >= max_nr_map)
break;
}
if (current_type != 0) {
new_bios[new_bios_entry].addr =
change_point[chgidx]->addr;
new_bios[new_bios_entry].type = current_type;
last_addr = change_point[chgidx]->addr;
}
last_type = current_type;
}
}
それは問題ではなかったものを内部にこのループ?その役割は、おそらく並べ替えがすでにプロパティとプロパティが隣のメモリ空間にマージされているためchange_pointの統合は、オーバーラップするメモリ空間が上映されます終了します。:次のように具体的な実装プロセス
- それらが一致している場合、現在のレコードchange_pointのアドレスに関連付けられた開始アドレスe820_map項目エントリは、(最初の時間は必ずしも同一である)かどうかを最初に第一サイクルはchange_pointアイテムを添加した、同じになり、現在のエントリが特定のメモリ・ブロックからのものであることを示しています開始アドレス、それが行くoverlap_listするために添加され、次いで、アイテムが更新さlast_addr new_biosに追加される、として理解優先このタイプの現在のメモリタイプlast_type(最後に更新メモリブロックは容易に理解されるコード、0であります最低優先度)
- 第二のループはchange_point用語に参加し、それは様々な条件に遭遇し始めます。
A.がchange_point新しいアイテムが最初の長期記憶の最後に追加されると仮定し、それはoverlap_listを削除します、new_biosは限りメモリブロックサイズは、カナダから、その後new_bios_entry、0ではないとして、new_biosが起動し、現在の項目のメモリブロックサイズに更新されます新しいレコードの内容。
B.がchange_point新しいアイテムは、メモリアドレスヘッダの新しいブロックに追加されると仮定し、次いでoverlap_list一つを追加し、その後current_type最大overlap_listリスト(最高優先度)、次いで再び現れた全てのタイプに更新します新しい状況:
a) 如果当前新加入change_point的类型值等于前者,继续开始下一循环;
b) 如果当前新加入change_point的类型值大于前者,那么new_bios将会以该change_point项纪录的地址作为new_bios前一项的结束地址,然后更新大小到new_bios中,开启new_bios的新一项的记录;
c) 如果当前新加入change_point的类型值小于前者,由于current_type将会仍然保持前者的类型值,后续将会跳过开始下一循环;
第3のループは、第二サイクルアクchange_pointアイテムの前面に基づいて、change_point用語に参加それがAの場合であれば、問題はそれの新しいスタートで、非常にシンプルであり、それは、ケースBであれば、もう少し複雑な状況が存在するであろう。
A.がchange_point新しいアイテムが新しいメモリブロックヘッダに追加され、値の問題の種類を伴うだろう、実際には、第二サイクルのBタイプの前にシーンを繰り返していると仮定すると、ここに住むません
B.項目は、メモリブロックの新しく追加されたchange_pointフロントエンドであると仮定すると、状況があってもよいです。
a) 如果前面已加入的两项类型相同,即B.a的情形,当前作为内存尾新加入change_point的类型值必然也是相同的,这仅会把与overlap_list中配对的那一项从队列中删除,继续开始加入change_point下一项; b) 如果前面两项,第一项类型值大于第二项,即B.c的情形,若当前项作为第一项的尾加入,那么当前的new_bios项将会以此作为结尾;但是若当前项作为第二项的尾加入,那么将把第二项从overlap_list中删除; c) 如果前面两项,第二项类型值大于第一项,即B.b的情形,若当前项作为第一项的尾加入,也仅是将第一项从overlap_list中删除;若当前项作为第一项的尾加入,那么new_bios将会就此作为尾完成当前项;
その後のサイクルはchange_point項目は、ばかりシーンの進化の前に、ここでの話ではありません追加しました。その後、正面からの情報は、この機能の目的は、メモリブロックの異なるタイプに応じて組み合わせた順序と同じタイプのメモリの連続ブロックのオーバーラップによるメモリレイアウト内側すなわちboot_params.e820_map、実質的に透明なものですメモリブロックの優先度が低いが離れて分割されています。
これは次のように説明されています。
【file:/arch/x86/kernel/e820.c】
/*
* Sanitize the BIOS e820 map.
*
* Some e820 responses include overlapping entries. The following
* replaces the original e820 map with a new one, removing overlaps,
* and resolving conflicting memory types in favor of highest
* numbered type.
*
* The input parameter biosmap points to an array of 'struct
* e820entry' which on entry has elements in the range [0, *pnr_map)
* valid, and which has space for up to max_nr_map entries.
* On return, the resulting sanitized e820 map entries will be in
* overwritten in the same location, starting at biosmap.
*
* The integer pointed to by pnr_map must be valid on entry (the
* current number of valid entries located at biosmap) and will
* be updated on return, with the new number of valid entries
* (something no more than max_nr_map.)
*
* The return value from sanitize_e820_map() is zero if it
* successfully 'sanitized' the map entries passed in, and is -1
* if it did nothing, which can happen if either of (1) it was
* only passed one map entry, or (2) any of the input map entries
* were invalid (start + size < start, meaning that the size was
* so big the described memory range wrapped around through zero.)
*
* Visually we're performing the following
* (1,2,3,4 = memory types)...
*
* Sample memory map (w/overlaps):
* ____22__________________
* ______________________4_
* ____1111________________
* _44_____________________
* 11111111________________
* ____________________33__
* ___________44___________
* __________33333_________
* ______________22________
* ___________________2222_
* _________111111111______
* _____________________11_
* _________________4______
*
* Sanitized equivalent (no overlap):
* 1_______________________
* _44_____________________
* ___1____________________
* ____22__________________
* ______11________________
* _________1______________
* __________3_____________
* ___________44___________
* _____________33_________
* _______________2________
* ________________1_______
* _________________4______
* ___________________2____
* ____________________33__
* ______________________4_
*/
理解を容易にするために、スケッチを整理:
連続的に解像度の優先度の種類に応じて異なる種類の断片化、重複部分の異なるタイプが、メモリブロックの様々なタイプの整合性を確保するために高い優先順位に応じて、同じタイプの一つにマージします。追加ナンセンス1:現在の仮想マシンをプレイするには、上記のような複雑な状況を持っていなかったが、このコードは、このライブを行うように設計されていることを否定することはできません。
default_machine_specific_memory_setupバックアウトsanitize_e820_mapから、次はappend_e820_mapの機能は次のとおりです。
【file:/arch/x86/kernel/e820.c】
static int __init append_e820_map(struct e820entry *biosmap, int nr_map)
{
/* Only one memory region (or negative)? Ignore it */
if (nr_map < 2)
return -1;
return __append_e820_map(biosmap, nr_map);
}
append_e820_mapパッケージは__append_e820_map呼び出します。
【file:/arch/x86/kernel/e820.c】
static int __init __append_e820_map(struct e820entry *biosmap, int nr_map)
{
while (nr_map) {
u64 start = biosmap->addr;
u64 size = biosmap->size;
u64 end = start + size;
u32 type = biosmap->type;
/* Overflow in 64 bits? Ignore the memory map. */
if (start > end)
return -1;
e820_add_region(start, size, type);
biosmap++;
nr_map--;
}
return 0;
boot_params.e820_mapを終えた後E820_add_regionサイクル呼び出しが操作を追加実行します。
【file:/arch/x86/kernel/e820.c】
void __init e820_add_region(u64 start, u64 size, int type)
{
__e820_add_region(&e820, start, size, type);
}
e820_add_region封装__e820_add_region:
【file:/arch/x86/kernel/e820.c】
static void __init __e820_add_region(struct e820map *e820x, u64 start, u64 size,
int type)
{
int x = e820x->nr_map;
if (x >= ARRAY_SIZE(e820x->map)) {
printk(KERN_ERR "e820: too many entries; ignoring [mem %#010llx-%#010llx]\n",
(unsigned long long) start,
(unsigned long long) (start + size - 1));
return;
}
e820x->map[x].addr = start;
e820x->map[x].size = size;
e820x->map[x].type = type;
e820x->nr_map++;
}
E820の__e820_add_regionは追加を行うために、すべての情報を吸い込ま。端的に言えば、それはE820の図にboot_params.e820_map有効にすることです。
最後に、e820_print_map機能を実現する方法を見てみましょう。
【file:/arch/x86/kernel/e820.c】
void __init e820_print_map(char *who)
{
int i;
for (i = 0; i < e820.nr_map; i++) {
printk(KERN_INFO "%s: [mem %#018Lx-%#018Lx] ", who,
(unsigned long long) e820.map[i].addr,
(unsigned long long)
(e820.map[i].addr + e820.map[i].size - 1));
e820_print_type(e820.map[i].type);
printk(KERN_CONT "\n");
}
}
これは、フック関数は、印刷の内容を返している、コンテンツを印刷、例えば、上記で得られたコマンドシェルをdmesgのが見ることができます。
要約すると、上記のコードは、BIOSメモリレイアウト情報によって検出された割り込みに主にために、生きてそんなにやった統合プロセスを行うboot_params.e820_map、ダンプ変数E820を終えました。