diff --git a/study/kernel/02-memory/00-code/cr3_pgd/Makefile b/study/kernel/02-memory/00-code/cr3_pgd/Makefile index c655fa9..5d64b8e 100644 --- a/study/kernel/02-memory/00-code/cr3_pgd/Makefile +++ b/study/kernel/02-memory/00-code/cr3_pgd/Makefile @@ -1,18 +1,80 @@ -ifneq ($(KERNELRELEASE),) - -obj-m := crs_pgd.o - -else - -KERNELDIR ?= /lib/modules/$(shell uname -r)/build - -PWD := $(shell pwd) - -all: - make -C $(KERNELDIR) M=$(PWD) modules - -clean: - make -C $(KERNELDIR) M=$(PWD) clean - - -endif +# ------------------------------------------------------------------------------ +# +# Makefile for the LDD-LinuxDeviceDrivers. +# +# Author: gatieme +# Create: 2016-07-29 15:50:46 +# Last modified: 2016-07-29 16:10:29 +# Description: +# This program is loaded as a kernel(v2.6.18 or later) module. +# Use "make install" to load it into kernel. +# Use "make remove" to remove the module out of kernel. +# +# ------------------------------------------------------------------------------ + + +ROOT=../../../../../ +#PLATFORM=$(shell $(ROOT)/systype.sh) +#include $(ROOT)/Make.defines.$(PLATFORM) + +# my driver description +DRIVER_VERSION := "1.0.0" +DRIVER_AUTHOR := "Gatieme @ AderStep Inc..." +DRIVER_DESC := "Demo module for LDD-LinuxDeviceDrivers devices" +DRIVER_LICENSE := "Dual BSD/GPL" + + +MODULE_NAME := crs_pgd + +ifneq ($(KERNELRELEASE),) + +#CFG_FLAGS += -O2 -I./ +#EXTRA_CFLAGS += $(C_FLAGS) $(CFG_INC) $(CFG_INC) + + + +obj-m := $(MODULE_NAME).o #smodule.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build + +PWD := $(shell pwd) + +modules: + make -C $(KERNELDIR) M=$(PWD) modules + +modules_install: + make -C $(KERNELDIR) M=$(PWD) modules_install + + + +insmod: + sudo insmod $(MODULE_NAME).ko + +reinsmod: + sudo rmmod $(MODULE_NAME) + sudo insmod $(MODULE_NAME).ko itype=2 btype=1 ctype=0xAC stype='a' + + +COMMIT="[1.1.4 带参数的可加载模块](http://book.51cto.com/art/201205/337663.htm)" +github: + cd $(ROOT) && make github + +rmmod: + sudo rmmod $(MODULE_NAME) + +test : + sudo ../injector/memInjector -l stack -m random -t word_0 --time 1 --timeout 3 -p 1 + +clean: + make -C $(KERNELDIR) M=$(PWD) clean + rm -f modules.order Module.symvers Module.markers + +.PHNOY: + modules modules_install clean + + + +endif + diff --git a/study/kernel/02-memory/00-code/cr3_pgd/crs_pgd.c b/study/kernel/02-memory/00-code/cr3_pgd/crs_pgd.c index 50ec77d..a919583 100644 --- a/study/kernel/02-memory/00-code/cr3_pgd/crs_pgd.c +++ b/study/kernel/02-memory/00-code/cr3_pgd/crs_pgd.c @@ -1,4 +1,4 @@ -//#include +#include #include #include #include @@ -9,11 +9,16 @@ static unsigned long read_pgd(void) return (unsigned long)current->mm->pgd; } + + +extern enum system_states system_state; static int __init init_cr3_pgd(void) { unsigned long cr3; unsigned long pgd = 0; + printk("system_state = %d\n", system_state); + cr3 = read_cr3(); printk("cr3----------------->0x%lx\n", cr3); pgd = __pa(read_pgd()); diff --git a/study/kernel/02-memory/01-description/01-memory/README.md b/study/kernel/02-memory/01-description/01-memory/README.md index afeb302..f1611b8 100644 --- a/study/kernel/02-memory/01-description/01-memory/README.md +++ b/study/kernel/02-memory/01-description/01-memory/README.md @@ -28,6 +28,8 @@ | [ 内存管理(二)struct page ](http://blog.chinaunix.net/uid-30282771-id-5176971.html) | | [进程页表页和内核页表](http://guojing.me/linux-kernel-architecture/posts/thread-page-table-and-kernel-page-table/) + + #1 前景回顾 ------- @@ -63,7 +65,7 @@ NUMA模式是一种分布式存储器访问方式,处理器可以同时访问 NUMA模式下,处理器被划分成多个"节点"(node), 每个节点被分配有的本地存储器空间。 所有节点中的处理器都可以访问全部的系统物理存储器,但是访问本节点内的存储器所需要的时间,比访问某些远程节点内的存储器所花的时间要少得多。 ->其访问时间随存储字的位置不同而变化。其共享存储器物理上是分布在所有处理机的本地存储器上。所有本地存储器的集合组成了全局地址空间,可被所有的处理机访问。处理机访问本地存储器是比较快的,但访问属于另一台处理机的远程存储器则比较慢,因为通过互连网络会产生附加时延。 +>其访问时间随存储字的位置不同而变化。其共享存储器物理上是分布在所有处理机的本地存储器上。所有本地存储器的集合组成了全局地址空间,可被所有的处理机访问。处理机访问本地存储器是比较快的,但访问属于另一台处理机的远程存储器则比较慢,因为通过互连网络会产生附加时延 NUMA 的主要优点是伸缩性。NUMA 体系结构在设计上已超越了 SMP 体系结构在伸缩性上的限制。通过 SMP,所有的内存访问都传递到相同的共享内存总线。这种方式非常适用于 CPU 数量相对较少的情况,但不适用于具有几十个甚至几百个 CPU 的情况,因为这些 CPU 会相互竞争对共享内存总线的访问。NUMA 通过限制任何一条内存总线上的 CPU 数量并依靠高速互连来连接各个节点,从而缓解了这些瓶颈状况。 @@ -78,7 +80,7 @@ Linux适用于各种不同的体系结构, 而不同体系结构在内存管理 Linux内核通过插入一些兼容层, 使得不同体系结构的差异很好的被隐藏起来, 内核对一致和非一致内存访问使用相同的数据结构 -#2.1 (N)UMA模型中linux内存的机构 +##2.1 (N)UMA模型中linux内存的机构 ------- @@ -115,55 +117,124 @@ Linux把物理内存划分为三个层次来管理 * 最后页帧(page frame)代表了系统内存的最小单位, 堆内存中的每个页都会创建一个struct page的一个实例. 传统上,把内存视为连续的字节,即内存为字节数组,内存单元的编号(地址)可作为字节数组的索引. 分页管理时,将若干字节视为一页,比如4K byte. 此时,内存变成了连续的页,即内存为页数组,每一页物理内存叫页帧,以页为单位对内存进行编号,该编号可作为页数组的索引,又称为页帧号. + 在一个单独的节点内,任一给定CPU访问页面所需的时间都是相同的。然而,对不同的CPU,这个时间可能就不同。对每个CPU而言,内核都试图把耗时节点的访问次数减到最少这就要小心地选择CPU最常引用的内核数据结构的存放位置. -对于UMA体系的,系统中只有一个node - -在LINUX中引入一个数据结构`struct pglist_data` ,来描述一个node,定义在[`include/linux/mmzone.h`](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L630) 文件中。(这个结构被typedef pg_data_t)。 - - -* 对于NUMA系统来讲, 整个系统的内存由一个[node_data](http://lxr.free-electrons.com/source/arch/s390/numa/numa.c?v=4.7#L23)的pg_data_t指针数组来管理。(因为可能有多个node),系统中的每个节点链接到一个以NULL结尾的[pgdat_list](http://lxr.free-electrons.com/source/arch/ia64/include/asm/numa.h#L27)链表中,而其中的每个节点利用pd_data_tnode_next字段链接到下一个节点。 - - -* 对于PC这样的UMA系统,使用struct pglist_data contig_page_data ,作为系统唯一的node管理所有的内存区域。(UMA系统中中只有一个node) -##内存节点node +##2.3 内存节点node ------- + + >CPU被划分为多个节点(node), 内存则被分簇, 每个CPU对应一个本地物理内存, 即一个CPU-node对应一个内存簇bank,即每个内存簇被认为是一个节点 > >系统的物理内存被划分为几个节点(node), 一个node对应一个内存簇bank,即每个内存簇被认为是一个节点 -* 首先, 内存被划分为结点. 每个节点关联到系统中的一个处理器, 内核中表示为`pg_data_t`的实例. 系统中每个节点被链接到一个以NULL结尾的`pgdat_list`链表中<而其中的每个节点利用`pg_data_tnode_next`字段链接到下一节.而对于PC这种UMA结构的机器来说, 只使用了一个成为contig_page_data的静态pg_data_t结构. +在LINUX中引入一个数据结构`struct pglist_data` ,来描述一个node,定义在[`include/linux/mmzone.h`](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L630) 文件中。(这个结构被typedef pg_data_t)。 -内存中的每个节点都是由pg_data_t描述,而pg_data_t由struct pglist_data定义而来, 该数据结构定义在[include/linux/mmzone.h, line 615](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L615) +* 对于NUMA系统来讲, 整个系统的内存由一个[node_data](http://lxr.free-electrons.com/source/arch/s390/numa/numa.c?v=4.7#L23)的pg_data_t指针数组来管理, +* 对于PC这样的UMA系统,使用struct pglist_data contig_page_data ,作为系统唯一的node管理所有的内存区域。(UMA系统中中只有一个node) + + +可以使用NODE_DATA(node_id)来查找系统中编号为node_id的结点, 参见[NODE_DATA的定义](http://lxr.free-electrons.com/ident?v=4.7;i=NODE_DATA) + +UMA结构下由于只有一个结点, 因此该宏总是返回全局的contig_page_data, 而与参数node_id无关. + +```cpp +extern struct pglist_data *node_data[]; +#define NODE_DATA(nid) (node_data[(nid)]) +``` + + +在UMA结构的机器中, 只有一个node结点即contig_page_data, 此时NODE_DATA直接指向了全局的contig_page_data, 而与node的编号nid无关, 参照[include/linux/mmzone.h?v=4.7, line 858](http://lxr.free-electrons.com/source/include/linux/mmzone.h?v=4.7#L858), 其中全局唯一的内存node结点contig_page_data定义在[mm/nobootmem.c?v=4.7, line 27](http://lxr.free-electrons.com/source/mm/nobootmem.c?v=4.7#L27), [linux-2.4.37](http://lxr.free-electrons.com/source/mm/numa.c?v=2.4.37#L15) + + + +```cpp +#ifndef CONFIG_NEED_MULTIPLE_NODES +extern struct pglist_data contig_page_data; +#define NODE_DATA(nid) (&contig_page_data) +#define NODE_MEM_MAP(nid) mem_map +else +/* ...... */ +#endif +``` 在分配一个页面时, Linux采用节点局部分配的策略, 从最靠近运行中的CPU的节点分配内存, 由于进程往往是在同一个CPU上运行, 因此从当前节点得到的内存很可能被用到 -在内存中,每个簇所对应的node又被分成的称为管理区(zone)的块,它们各自描述在内存中的范围。一个管理区(zone)由[struct zone](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L326)结构体来描述,在linux-2.4.37之前的内核中是用[`typedef struct zone_struct zone_t `](http://lxr.free-electrons.com/source/include/linux/mmzone.h?v=2.4.37#L47)数据结构来描述) -管理区的类型有如下几种 -* ZONE_DMA -* ZONE_NORMAL -* ZONE_HIGHMEM这三种类型 -不同的管理区的用途是不一样的,ZONE_DMA类型的内存区域在物理内存的低端,主要是ISA设备只能用低端的地址做DMA操作。ZONE_NORMAL类型的内存区域直接被内核映射到线性地址空间上面的区域(line address space),ZONE_HIGHMEM将保留给系统使用,是系统中预留的可用内存空间,不能被内核直接映射。 +##2.4 物理内存区域zone +------- + +因为实际的计算机体系结构有硬件的诸多限制, 这限制了页框可以使用的方式. 尤其是, Linux内核必须处理80x86体系结构的两种硬件约束. + +* ISA总线的直接内存存储DMA处理器有一个严格的限制 : 他们只能对RAM的前16MB进行寻址 + +* 在具有大容量RAM的现代32位计算机中, CPU不能直接访问所有的物理地址, 因为线性地址空间太小, 内核不可能直接映射所有物理内存到线性地址空间, 我们会在后面典型架构(x86)上内存区域划分详细讲解x86_32上的内存区域划分 + + +因此Linux内核对不同区域的内存需要采用不同的管理方式和映射方式, + +为了解决这些制约条件,Linux使用了三种区: + +1. ZONE_DMA : 这个区包含的页用来执行DMA操作。 + +2. ZONE_NOMAL : 这个区包含的都是能正常映射的页。 + +3. ZONE_HIGHEM : 这个区包"高端内存",其中的页能不永久地映射到内核地址空间 + +而为了兼容一些设备的热插拔支持以及内存碎片化的处理, 内核也引入一些逻辑上的内存区. + +1. ZONE_MOVABLE : 内核定义了一个伪内存域ZONE_MOVABLE, 在防止物理内存碎片的机制memory migration中需要使用该内存域. 供防止物理内存碎片的极致使用 + +2. ZONE_DEVICE : 为支持热插拔设备而分配的Non Volatile Memory非易失性内存 + +内核将每个簇所对应的node又被分成的称为管理区(zone)的块,它们各自描述在内存中的范围。一个管理区(zone)由[struct zone](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L326)结构体来描述,在linux-2.4.37之前的内核中是用[`typedef struct zone_struct zone_t `](http://lxr.free-electrons.com/source/include/linux/mmzone.h?v=2.4.37#L47)数据结构来描述) + + + +对于x86_32的机器,管理区(内存区域)类型如下分布 -对于x86机器,管理区(内存区域)类型如下分布 | 类型 | 区域 | | :------- | ----: | -| ZONE_DMA | 0~16MB | -| ZONE_NORMAL | 16MB~896MB | +| ZONE_DMA | 0~15MB | +| ZONE_NORMAL | 16MB~895MB | | ZONE_HIGHMEM | 896MB~物理内存结束 | -##内存页page + +内核在初始化内存管理区时, 首先建立管理区表zone_table. 参见[mm/page_alloc.c?v=2.4.37, line 38](http://lxr.free-electrons.com/source/mm/page_alloc.c?v=2.4.37#L38) + + +```cpp +/* + * + * The zone_table array is used to look up the address of the + * struct zone corresponding to a given zone number (ZONE_DMA, + * ZONE_NORMAL, or ZONE_HIGHMEM). + */ +zone_t *zone_table[MAX_NR_ZONES*MAX_NR_NODES]; +EXPORT_SYMBOL(zone_table); +``` + +该表处理起来就像一个多维数组, + +* MAX_NR_ZONES是一个节点中所能包容纳的管理区的最大数, 如3个, 定义在[include/linux/mmzone.h?v=2.4.37, line 25](http://lxr.free-electrons.com/source/include/linux/mmzone.h?v=2.4.37#L25), 与zone区域的类型(ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM)定义在一起. 当然这时候我们这些标识都是通过宏的方式来实现的, 而不是如今的枚举类型 + + +* MAX_NR_NODES是可以存在的节点的最大数. + +* 函数EXPORT_SYMBOL使得内核的变量或者函数可以被载入的模块(比如我们的驱动模块)所访问. + + +##2.5 内存页page ------- 大多数内核(kernel)的操作只使用ZONE_NORMAL区域,系统内存由很多固定大小的内存块组成的,这样的内存块称作为“页”(PAGE), @@ -174,7 +245,7 @@ x86体系结构中,page的大小为4096个字节。 -##高端内存 +##2.6 高端内存 ------- 由于能够被Linux内核直接访问的ZONE_NORMAL区域的内存空间也是有限的,所以LINUX提出了高端内存(High memory)的概念,并且允许对高端内存的访问 diff --git a/study/kernel/02-memory/01-description/03-zone/README.md b/study/kernel/02-memory/01-description/03-zone/README.md index 041a092..b1a7750 100644 --- a/study/kernel/02-memory/01-description/03-zone/README.md +++ b/study/kernel/02-memory/01-description/03-zone/README.md @@ -849,6 +849,34 @@ min_low_pfn, max_pfn和max_low_pfn这3个值,也要用于对高端内存(h +#5 管理区表zone_table与管理区节点的映射 +------- + + +内核在初始化内存管理区时, 首先建立管理区表zone_table. 参见[mm/page_alloc.c?v=2.4.37, line 38](http://lxr.free-electrons.com/source/mm/page_alloc.c?v=2.4.37#L38) + +```cpp +/* + * + * The zone_table array is used to look up the address of the + * struct zone corresponding to a given zone number (ZONE_DMA, + * ZONE_NORMAL, or ZONE_HIGHMEM). + */ +zone_t *zone_table[MAX_NR_ZONES*MAX_NR_NODES]; +EXPORT_SYMBOL(zone_table); +``` + + +MAX_NR_ZONES是一个节点中所能包容纳的管理区的最大数, 如3个, 定义在[include/linux/mmzone.h?v=2.4.37, line 25](http://lxr.free-electrons.com/source/include/linux/mmzone.h?v=2.4.37#L25), 与zone区域的类型(ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM)定义在一起. 当然这时候我们这些标识都是通过宏的方式来实现的, 而不是如今的枚举类型 + +MAX_NR_NODES是可以存在的节点的最大数. + +函数EXPORT_SYMBOL使得内核的变量或者函数可以被载入的模块(比如我们的驱动模块)所访问. + +该表处理起来就像一个多维数组, 在函数free_area_init_core中, 一个节点的所有页面都会被初始化. + + + #总结 ------- @@ -883,3 +911,5 @@ Linux必须处理如下两种硬件存在缺陷而引起的内存寻址问题: ![每个区及其在X86上所占的列表](./images/zone_x86_32.png) + + diff --git a/study/kernel/02-memory/03-initialize/01-init_struct/1.c b/study/kernel/02-memory/03-initialize/01-init_struct/1.c new file mode 100644 index 0000000..567abea --- /dev/null +++ b/study/kernel/02-memory/03-initialize/01-init_struct/1.c @@ -0,0 +1,87 @@ + +/* + * http://lxr.free-electrons.com/source/init/main.c?v=4.7#L479 +*/ +asmlinkage __visible void __init start_kernel(void) +{ + + setup_arch(&command_line); + mm_init_cpumask(&init_mm); + + setup_per_cpu_areas(); + + + build_all_zonelists(NULL, NULL); + page_alloc_init(); + + + /* + * These use large bootmem allocations and must precede + * mem_init(); + * kmem_cache_init(); + */ + mm_init(); + + kmem_cache_init_late(); + + kmemleak_init(); + setup_per_cpu_pageset(); + + rest_init(); +} + + +/* + * http://lxr.free-electrons.com/source/mm/page_alloc.c?v4.7#L5029 + * Called with zonelists_mutex held always + * unless system_state == SYSTEM_BOOTING. + * + * __ref due to (1) call of __meminit annotated setup_zone_pageset + * [we're only called with non-NULL zone through __meminit paths] and + * (2) call of __init annotated helper build_all_zonelists_init + * [protected by SYSTEM_BOOTING]. + */ +void __ref build_all_zonelists(pg_data_t *pgdat, struct zone *zone) +{ + /* 设定zonelist的顺序,是按节点还是按管理区排序,只对NUMA有意义 */ + set_zonelist_order(); + + if (system_state == SYSTEM_BOOTING) + { + build_all_zonelists_init(); + } + else + { +#ifdef CONFIG_MEMORY_HOTPLUG + if (zone) + setup_zone_pageset(zone); +#endif + /* we have to stop all cpus to guarantee there is no user + of zonelist */ + stop_machine(__build_all_zonelists, pgdat, NULL); + /* cpuset refresh routine should be here */ + } + + /* 得到所有管理区可分配的空闲页面数 */ + vm_total_pages = nr_free_pagecache_pages(); + /* + * Disable grouping by mobility if the number of pages in the + * system is too low to allow the mechanism to work. It would be + * more accurate, but expensive to check per-zone. This check is + * made on memory-hotadd so a system can start with mobility + * disabled and enable it later + */ + if (vm_total_pages < (pageblock_nr_pages * MIGRATE_TYPES)) + page_group_by_mobility_disabled = 1; + else + page_group_by_mobility_disabled = 0; + + pr_info("Built %i zonelists in %s order, mobility grouping %s. Total pages: %ld\n", + nr_online_nodes, + zonelist_order_name[current_zonelist_order], + page_group_by_mobility_disabled ? "off" : "on", + vm_total_pages); +#ifdef CONFIG_NUMA + pr_info("Policy zone: %s\n", zone_names[policy_zone]); +#endif +} \ No newline at end of file diff --git a/study/kernel/02-memory/03-initialize/01-init_struct/README.md b/study/kernel/02-memory/03-initialize/01-init_struct/README.md index c18be47..66bf934 100644 --- a/study/kernel/02-memory/03-initialize/01-init_struct/README.md +++ b/study/kernel/02-memory/03-initialize/01-init_struct/README.md @@ -8,7 +8,7 @@ | 2016-06-14 | [Linux-4.7](http://lxr.free-electrons.com/source/?v=4.7) | X86 & arm | [gatieme](http://blog.csdn.net/gatieme) | [LinuxDeviceDrivers](https://github.com/gatieme/LDD-LinuxDeviceDrivers) | [Linux内存管理](http://blog.csdn.net/gatieme/article/category/6225543) | -http://blog.csdn.net/lobbve/article/details/23208817 +http://www.linuxidc.com/Linux/2012-05/60230.htm 在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后奇偶内核才能检测到可用内存和寄存器. @@ -131,7 +131,9 @@ UMA体系结构中,free_area_init函数在系统唯一的struct node对象cont #1 系统启动 ------- -首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在 +首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在[init/main.c?v=4.7, line 479](http://lxr.free-electrons.com/source/init/main.c?v=4.7#L479) + +其代码很复杂, 我们只截取出其中与内存管理初始化相关的部分, 如下所示 ```cpp asmlinkage __visible void __init start_kernel(void) @@ -166,10 +168,10 @@ asmlinkage __visible void __init start_kernel(void) | 函数 | 功能 | |:----:|:----:| | [setup_arch](http://lxr.free-electrons.com/ident?v=4.7;i=setup_arch) | 是一个特定于体系结构的设置函数, 其中一项任务是负责初始化自举分配器 | -| mm_init_cpumask | +| [mm_init_cpumask](http://lxr.free-electrons.com/source/include/linux/mm_types.h?v=4.7#L522) | 初始化CPU屏蔽字 | | [setup_per_cpu_areas](http://lxr.free-electrons.com/ident?v=4.7;i=setup_per_cpu_areas) | 函数[(查看定义)](http://lxr.free-electrons.com/source/mm/percpu.c?v4.7#L2205])给每个CPU分配内存,并拷贝.data.percpu段的数据. 为系统中的每个CPU的per_cpu变量申请空间.
在SMP系统中, setup_per_cpu_areas初始化源代码中(使用[per_cpu宏](http://lxr.free-electrons.com/source/include/linux/percpu-defs.h#L256))定义的静态per-cpu变量, 这种变量对系统中每个CPU都有一个独立的副本.
此类变量保存在内核二进制影像的一个独立的段中, setup_per_cpu_areas的目的就是为系统中各个CPU分别创建一份这些数据的副本
在非SMP系统中这是一个空操作 | | [build_all_zonelists](http://lxr.free-electrons.com/source/mm/page_alloc.c?v4.7#L5029) | 建立并初始化结点和内存域的数据结构 | -| [mm_init](http://lxr.free-electrons.com/source/init/main.c?v4.7#L464) | 建立了内核的内存分配器,
其中通过[mem_init](http://lxr.free-electrons.com/ident?v=4.7&i=mem_init)停用bootmem分配器并迁移到实际的内存管理器
然后调用kmem_cache_init函数初始化内核内部用于小块内存区的分配器 | +| [mm_init](http://lxr.free-electrons.com/source/init/main.c?v4.7#L464) | 建立了内核的内存分配器,
其中通过[mem_init](http://lxr.free-electrons.com/ident?v=4.7&i=mem_init)停用bootmem分配器并迁移到实际的内存管理器(比如伙伴系统)
然后调用kmem_cache_init函数初始化内核内部用于小块内存区的分配器 | | [kmem_cache_init_late](http://lxr.free-electrons.com/source/mm/slab.c?v4.7#L1378) | 在kmem_cache_init之后, 完善分配器的缓存机制, 当前3个可用的内核内存分配器[slab](http://lxr.free-electrons.com/source/mm/slab.c?v4.7#L1378), [slob](http://lxr.free-electrons.com/source/mm/slob.c?v4.7#L655), [slub](http://lxr.free-electrons.com/source/mm/slub.c?v=4.7#L3960)都会定义此函数 | | [kmemleak_init](http://lxr.free-electrons.com/source/mm/kmemleak.c?v=4.7#L1857) | Kmemleak工作于内核态,Kmemleak 提供了一种可选的内核泄漏检测,其方法类似于跟踪内存收集器。当独立的对象没有被释放时,其报告记录在 [/sys/kernel/debug/kmemleak](http://lxr.free-electrons.com/source/mm/kmemleak.c?v=4.7#L1467)中, Kmemcheck能够帮助定位大多数内存错误的上下文 | | [setup_per_cpu_pageset](http://lxr.free-electrons.com/source/mm/page_alloc.c?v=4.7#L5392) | 初始化CPU高速缓存行, 为pagesets的第一个数组元素分配内存, 换句话说, 其实就是第一个系统处理器分配
由于在分页情况下,每次存储器访问都要存取多级页表,这就大大降低了访问速度。所以,为了提高速度,在CPU中设置一个最近存取页面的高速缓存硬件机制,当进行存储器访问时,先检查要访问的页面是否在高速缓存中. | @@ -179,6 +181,140 @@ asmlinkage __visible void __init start_kernel(void) ------- +内核在start_kernel中通过build_all_zonelists完成了内存结点及其管理内存域的初始化工作, 调用如下 + + +```cpp + build_all_zonelists(NULL, NULL); +``` + +[build_all_zonelists](http://lxr.free-electrons.com/source/mm/page_alloc.c?v4.7#L5029)建立内存管理结点及其内存域所需的数据结构. + + + + +前面我们讲解内存管理域时候讲解到, 系统中的所有管理域都存储在一个多维的数组zone_table. 内核在初始化内存管理区时, 必须要建立管理区表zone_table. 参见[mm/page_alloc.c?v=2.4.37, line 38](http://lxr.free-electrons.com/source/mm/page_alloc.c?v=2.4.37#L38) +```cpp +/* + * + * The zone_table array is used to look up the address of the + * struct zone corresponding to a given zone number (ZONE_DMA, + * ZONE_NORMAL, or ZONE_HIGHMEM). + */ +zone_t *zone_table[MAX_NR_ZONES*MAX_NR_NODES]; +EXPORT_SYMBOL(zone_table); +``` + +* MAX_NR_NODES为系统中内存结点的数目 + +* MAX_NR_ZONES为系统中单个内存结点所拥有的最大内存区域数目 + +##2.1 set_zonelist_order +------- + +##2.1.1 zonelist order +------- + +NUMA系统中存在多个节点, 每个节点对应一个struct pglist_data结构, 此结构中可以包含多个zone, 如: ZONE_DMA, ZONE_NORMAL,这样就产生几种排列顺序,以2个节点2个zone为例(zone从高到低排列, ZONE_DMA0表示节点0的ZONE_DMA,其它类似): + +在build_all_zonelists开始, 首先内核通过set_zonelist_order函数设置了`zonelist_order`,如下所示, 参见[mm/page_alloc.c?v=4.7, line 5031](http://lxr.free-electrons.com/source/mm/page_alloc.c?v=4.7#L5031) + + +```cpp +void __ref build_all_zonelists(pg_data_t *pgdat, struct zone *zone) +{ + set_zonelist_order(); + /* ....... */ +} +``` + +该函数 + +```cpp +// http://lxr.free-electrons.com/source/mm/page_alloc.c?v=4.7#L4551 +/* + * zonelist_order: + * 0 = automatic detection of better ordering. + * 1 = order by ([node] distance, -zonetype) + * 2 = order by (-zonetype, [node] distance) + * + * If not NUMA, ZONELIST_ORDER_ZONE and ZONELIST_ORDER_NODE will create + * the same zonelist. So only NUMA can configure this param. + */ +#define ZONELIST_ORDER_DEFAULT 0 +#define ZONELIST_ORDER_NODE 1 +#define ZONELIST_ORDER_ZONE 2 +/* zonelist order in the kernel. + * set_zonelist_order() will set this to NODE or ZONE. + */ +static int current_zonelist_order = ZONELIST_ORDER_DEFAULT; +static char zonelist_order_name[3][8] = {"Default", "Node", "Zone"}; + +// http://lxr.free-electrons.com/source/mm/page_alloc.c?v=4.7#L4571 +#ifdef CONFIG_NUMA +/* The value user specified ....changed by config */ +static int user_zonelist_order = ZONELIST_ORDER_DEFAULT; +/* string for sysctl */ +#define NUMA_ZONELIST_ORDER_LEN 16 +char numa_zonelist_order[16] = "default"; + + +// http://lxr.free-electrons.com/source/mm/page_alloc.c#L4571 +static void set_zonelist_order(void) +{ + if (user_zonelist_order == ZONELIST_ORDER_DEFAULT) + current_zonelist_order = default_zonelist_order(); + else + current_zonelist_order = user_zonelist_order; +} + + +#else /* CONFIG_NUMA */ + +// http://lxr.free-electrons.com/source/mm/page_alloc.c?v=4.7#L4892 +static void set_zonelist_order(void) +{ + current_zonelist_order = ZONELIST_ORDER_ZONE; +} +``` + + + +##system_state系统状态标识 + +其中`system_state`变量是一个系统全局定义的用来表示系统当前运行状态的枚举变量, 其定义在[include/linux/kernel.h?v=4.7, line 487](http://lxr.free-electrons.com/source/include/linux/kernel.h?v=4.7#L487) + +```cpp +/* Values used for system_state */ +extern enum system_states +{ + SYSTEM_BOOTING, + SYSTEM_RUNNING, + SYSTEM_HALT, + SYSTEM_POWER_OFF, + SYSTEM_RESTART, +} system_state; +``` + +* 如果系统system_state是SYSTEM_BOOTING, 则调用`build_all_zonelists_init`初始化所有的内存结点 + +* 否则的话如果定义了冷热页`CONFIG_MEMORY_HOTPLUG`且参数zone(待初始化的内存管理域zone)不为NULL, 则调用setup_zone_pageset设置冷热页 + + + +```cpp +if (system_state == SYSTEM_BOOTING) +{ + build_all_zonelists_init(); +} +else +{ +#ifdef CONFIG_MEMORY_HOTPLUG + if (zone) + setup_zone_pageset(zone); +#endif +``` + #3 特定于体系结构的设置 -------