mirror of
https://github.com/gatieme/LDD-LinuxDeviceDrivers.git
synced 2026-11-09 08:38:04 +08:00
鏇存柊浜嗗唴瀛樼鐞嗙殑鐩綍缁撴瀯...
This commit is contained in:
1 parent
89a8b11a7b
commit
bbad7ff534
4 files changed
+1151
-914
No files matched your search
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,138 @@
|
||||
初始化内存管理
|
||||
=======
|
||||
|
||||
|
||||
|
||||
| 日期 | 内核版本 | 架构| 作者 | GitHub| CSDN |
|
||||
| ------- |:-------:|:-------:|:-------:|:-------:|:-------:|
|
||||
| 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) |
|
||||
|
||||
|
||||
|
||||
|
||||
在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后奇偶内核才能检测到可用内存和寄存器.
|
||||
|
||||
|
||||
|
||||
#1 前景回顾
|
||||
-------
|
||||
|
||||
|
||||
##1.1 Linux内存管理的层次结构
|
||||
-------
|
||||
|
||||
|
||||
Linux把物理内存划分为三个层次来管理
|
||||
|
||||
| 层次 | 描述 |
|
||||
|:----:|:----:|
|
||||
| 存储节点(Node) | CPU被划分为多个节点(node), 内存则被分簇, 每个CPU对应一个本地物理内存, 即一个CPU-node对应一个内存簇bank,即每个内存簇被认为是一个节点 |
|
||||
| 管理区(Zone) | 每个物理内存节点node被划分为多个内存管理区域, 用于表示不同范围的内存, 内核可以使用不同的映射方式映射物理内存 |
|
||||
| 页面(Page) | 内存被细分为多个页面帧, 页面是最基本的页面分配的单位 |
|
||||
|
||||
为了支持NUMA模型,也即CPU对不同内存单元的访问时间可能不同,此时系统的物理内存被划分为几个节点(node), 一个node对应一个内存簇bank,即每个内存簇被认为是一个节点
|
||||
|
||||
|
||||
* 首先, 内存被划分为**结点**. 每个节点关联到系统中的一个处理器, 内核中表示为`pg_data_t`的实例. 系统中每个节点被链接到一个以NULL结尾的`pgdat_list`链表中<而其中的每个节点利用`pg_data_tnode_next`字段链接到下一节.而对于PC这种UMA结构的机器来说, 只使用了一个成为contig_page_data的静态pg_data_t结构.
|
||||
|
||||
* 接着各个节点又被划分为内存管理区域, 一个**管理区域**通过struct zone_struct描述, 其被定义为zone_t, 用以表示内存的某个范围, 低端范围的16MB被描述为ZONE_DMA, 某些工业标准体系结构中的(ISA)设备需要用到它, 然后是可直接映射到内核的普通内存域ZONE_NORMAL,最后是超出了内核段的物理地址域ZONE_HIGHMEM, 被称为高端内存. 是系统中预留的可用内存空间, 不能被内核直接映射.
|
||||
|
||||
|
||||
* 最后**页帧(page frame)**代表了系统内存的最小单位, 堆内存中的每个页都会创建一个struct page的一个实例. 传统上,把内存视为连续的字节,即内存为字节数组,内存单元的编号(地址)可作为字节数组的索引. 分页管理时,将若干字节视为一页,比如4K byte. 此时,内存变成了连续的页,即内存为页数组,每一页物理内存叫页帧,以页为单位对内存进行编号,该编号可作为页数组的索引,又称为页帧号.
|
||||
|
||||
|
||||
##1.2 内存结点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)。
|
||||
|
||||
|
||||
* 对于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的结点, 而UMA结构下由于只有一个结点, 因此该宏总是返回全局的contig_page_data, 而与参数node_id无关.
|
||||
|
||||
|
||||
##1.2 物理内存区域
|
||||
-------
|
||||
|
||||
因为实际的计算机体系结构有硬件的诸多限制, 这限制了页框可以使用的方式. 尤其是, Linux内核必须处理80x86体系结构的两种硬件约束.
|
||||
|
||||
* ISA总线的直接内存存储DMA处理器有一个严格的限制 : 他们只能对RAM的前16MB进行寻址
|
||||
|
||||
* 在具有大容量RAM的现代32位计算机中, CPU不能直接访问所有的物理地址, 因为线性地址空间太小, 内核不可能直接映射所有物理内存到线性地址空间, 我们会在后面典型架构(x86)上内存区域划分详细讲解x86_32上的内存区域划分
|
||||
</font>
|
||||
|
||||
因此Linux内核对不同区域的内存需要采用不同的管理方式和映射方式, 因此内核将物理地址或者成用zone_t表示的不同地址区域,
|
||||
|
||||
对于x86_32的机器,管理区(内存区域)类型如下分布
|
||||
|
||||
|
||||
| 类型 | 区域 |
|
||||
| :------- | ----: |
|
||||
| ZONE_DMA | 0~15MB |
|
||||
| ZONE_NORMAL | 16MB~895MB |
|
||||
| ZONE_HIGHMEM | 896MB~物理内存结束 |
|
||||
|
||||
|
||||
##1.3 物理页帧
|
||||
-------
|
||||
|
||||
内核把物理页作为内存管理的基本单位. 尽管处理器的最小可寻址单位通常是字, 但是, 内存管理单元MMU通常以页为单位进行处理. 因此,从虚拟内存的上来看,页就是最小单位.
|
||||
|
||||
页帧代表了系统内存的最小单位, 对内存中的每个页都会创建struct page的一个实例. 内核必须要保证page结构体足够的小,否则仅struct page就要占用大量的内存.
|
||||
|
||||
|
||||
内核用[struct page(include/linux/mm_types.h?v=4.7, line 45)](http://lxr.free-electrons.com/source/include/linux/mm_types.h?v4.7#L45)结构表示系统中的每个物理页.
|
||||
|
||||
出于节省内存的考虑,struct page中使用了大量的联合体union.
|
||||
|
||||
|
||||
`mem_map`是一个struct page的数组,管理着系统中所有的物理内存页面。在系统启动的过程中,创建和分配mem_map的内存区域, mem_map定义在[mm/page_alloc.c?v=4.7, line 6691](http://lxr.free-electrons.com/source/mm/page_alloc.c?v=4.7#L6691)
|
||||
|
||||
|
||||
UMA体系结构中,free_area_init函数在系统唯一的struct node对象contig_page_data中node_mem_map成员赋值给全局的mem_map变量
|
||||
|
||||
|
||||
|
||||
#1 启动过程中的内存初始化
|
||||
-------
|
||||
|
||||
在初始化过程中, 还必须建立内存管理的数据结构, 以及很多事务. 因为内核在内存管理完全初始化之前就需要使用内存. 在系统启动过程期间, 使用了额外的简化悉尼股市的内存管理模块, 然后在初始化完成后, 将旧的模块丢弃掉.
|
||||
|
||||
|
||||
#1 建立数据结构
|
||||
-------
|
||||
|
||||
|
||||
|
||||
对相关数据结构的初始化是从全局启动函数start_kernel中开始的, 该函数在加载内核并激活各个子系统之后执行. 由于内存管理是内核一个非常重要的部分, 因此在特定体系结构的设置步骤中检测并确定系统中内存的分配情况后, 会立即执行内存管理的初始化.
|
||||
|
||||
|
||||
##1.2 系统启动
|
||||
-------
|
||||
|
||||
##1.3 节点和内存域的初始化
|
||||
-------
|
||||
|
||||
|
||||
#2 特定于体系结构的设置
|
||||
-------
|
||||
|
||||
##2.1 内核在内存中的布局
|
||||
-------
|
||||
|
||||
##2.2 初始化过程
|
||||
-------
|
||||
|
||||
|
||||
##2.3 分页机制初始化
|
||||
-------
|
||||
|
||||
##2.4 注册活动内存区
|
||||
-------
|
||||
|
||||
##2.5 系统的地址空间设置
|
||||
-------
|
||||
|
||||
@@ -1,62 +1,138 @@
|
||||
初始化内存管理
|
||||
|
||||
=======
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| 日期 | 内核版本 | 架构| 作者 | GitHub| CSDN |
|
||||
| ------- |:-------:|:-------:|:-------:|:-------:|:-------:|
|
||||
| 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) |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后奇偶内核才能检测到可用内存和寄存器.
|
||||
|
||||
#1 启动过程中的内存初始化
|
||||
-------
|
||||
|
||||
在初始化过程中, 还必须建立内存管理的数据结构, 以及很多事务. 因为内核在内存管理完全初始化之前就需要使用内存. 在系统启动过程期间, 使用了额外的简化悉尼股市的内存管理模块, 然后在初始化完成后, 将旧的模块丢弃掉.
|
||||
|
||||
|
||||
#1 建立数据结构
|
||||
-------
|
||||
|
||||
|
||||
|
||||
对相关数据结构的初始化是从全局启动函数start_kernel中开始的, 该函数在加载内核并激活各个子系统之后执行. 由于内存管理是内核一个非常重要的部分, 因此在特定体系结构的设置步骤中检测并确定系统中内存的分配情况后, 会立即执行内存管理的初始化.
|
||||
|
||||
##1.1 先决条件
|
||||
-------
|
||||
|
||||
|
||||
##1.2 系统启动
|
||||
-------
|
||||
|
||||
##1.3 节点和内存域的初始化
|
||||
-------
|
||||
|
||||
|
||||
#2 特定于体系结构的设置
|
||||
-------
|
||||
|
||||
##2.1 内核在内存中的布局
|
||||
-------
|
||||
|
||||
##2.2 初始化过程
|
||||
-------
|
||||
|
||||
|
||||
##2.3 分页机制初始化
|
||||
-------
|
||||
|
||||
##2.4 注册活动内存区
|
||||
-------
|
||||
|
||||
##2.5 系统的地址空间设置
|
||||
-------
|
||||
|
||||
初始化内存管理
|
||||
=======
|
||||
|
||||
|
||||
|
||||
| 日期 | 内核版本 | 架构| 作者 | GitHub| CSDN |
|
||||
| ------- |:-------:|:-------:|:-------:|:-------:|:-------:|
|
||||
| 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) |
|
||||
|
||||
|
||||
|
||||
|
||||
在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后奇偶内核才能检测到可用内存和寄存器.
|
||||
|
||||
|
||||
|
||||
#1 前景回顾
|
||||
-------
|
||||
|
||||
|
||||
##1.1 Linux内存管理的层次结构
|
||||
-------
|
||||
|
||||
|
||||
Linux把物理内存划分为三个层次来管理
|
||||
|
||||
| 层次 | 描述 |
|
||||
|:----:|:----:|
|
||||
| 存储节点(Node) | CPU被划分为多个节点(node), 内存则被分簇, 每个CPU对应一个本地物理内存, 即一个CPU-node对应一个内存簇bank,即每个内存簇被认为是一个节点 |
|
||||
| 管理区(Zone) | 每个物理内存节点node被划分为多个内存管理区域, 用于表示不同范围的内存, 内核可以使用不同的映射方式映射物理内存 |
|
||||
| 页面(Page) | 内存被细分为多个页面帧, 页面是最基本的页面分配的单位 |
|
||||
|
||||
为了支持NUMA模型,也即CPU对不同内存单元的访问时间可能不同,此时系统的物理内存被划分为几个节点(node), 一个node对应一个内存簇bank,即每个内存簇被认为是一个节点
|
||||
|
||||
|
||||
* 首先, 内存被划分为**结点**. 每个节点关联到系统中的一个处理器, 内核中表示为`pg_data_t`的实例. 系统中每个节点被链接到一个以NULL结尾的`pgdat_list`链表中<而其中的每个节点利用`pg_data_tnode_next`字段链接到下一节.而对于PC这种UMA结构的机器来说, 只使用了一个成为contig_page_data的静态pg_data_t结构.
|
||||
|
||||
* 接着各个节点又被划分为内存管理区域, 一个**管理区域**通过struct zone_struct描述, 其被定义为zone_t, 用以表示内存的某个范围, 低端范围的16MB被描述为ZONE_DMA, 某些工业标准体系结构中的(ISA)设备需要用到它, 然后是可直接映射到内核的普通内存域ZONE_NORMAL,最后是超出了内核段的物理地址域ZONE_HIGHMEM, 被称为高端内存. 是系统中预留的可用内存空间, 不能被内核直接映射.
|
||||
|
||||
|
||||
* 最后**页帧(page frame)**代表了系统内存的最小单位, 堆内存中的每个页都会创建一个struct page的一个实例. 传统上,把内存视为连续的字节,即内存为字节数组,内存单元的编号(地址)可作为字节数组的索引. 分页管理时,将若干字节视为一页,比如4K byte. 此时,内存变成了连续的页,即内存为页数组,每一页物理内存叫页帧,以页为单位对内存进行编号,该编号可作为页数组的索引,又称为页帧号.
|
||||
|
||||
|
||||
##1.2 内存结点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)。
|
||||
|
||||
|
||||
* 对于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的结点, 而UMA结构下由于只有一个结点, 因此该宏总是返回全局的contig_page_data, 而与参数node_id无关.
|
||||
|
||||
|
||||
##1.2 物理内存区域
|
||||
-------
|
||||
|
||||
因为实际的计算机体系结构有硬件的诸多限制, 这限制了页框可以使用的方式. 尤其是, Linux内核必须处理80x86体系结构的两种硬件约束.
|
||||
|
||||
* ISA总线的直接内存存储DMA处理器有一个严格的限制 : 他们只能对RAM的前16MB进行寻址
|
||||
|
||||
* 在具有大容量RAM的现代32位计算机中, CPU不能直接访问所有的物理地址, 因为线性地址空间太小, 内核不可能直接映射所有物理内存到线性地址空间, 我们会在后面典型架构(x86)上内存区域划分详细讲解x86_32上的内存区域划分
|
||||
</font>
|
||||
|
||||
因此Linux内核对不同区域的内存需要采用不同的管理方式和映射方式, 因此内核将物理地址或者成用zone_t表示的不同地址区域,
|
||||
|
||||
对于x86_32的机器,管理区(内存区域)类型如下分布
|
||||
|
||||
|
||||
| 类型 | 区域 |
|
||||
| :------- | ----: |
|
||||
| ZONE_DMA | 0~15MB |
|
||||
| ZONE_NORMAL | 16MB~895MB |
|
||||
| ZONE_HIGHMEM | 896MB~物理内存结束 |
|
||||
|
||||
|
||||
##1.3 物理页帧
|
||||
-------
|
||||
|
||||
内核把物理页作为内存管理的基本单位. 尽管处理器的最小可寻址单位通常是字, 但是, 内存管理单元MMU通常以页为单位进行处理. 因此,从虚拟内存的上来看,页就是最小单位.
|
||||
|
||||
页帧代表了系统内存的最小单位, 对内存中的每个页都会创建struct page的一个实例. 内核必须要保证page结构体足够的小,否则仅struct page就要占用大量的内存.
|
||||
|
||||
|
||||
内核用[struct page(include/linux/mm_types.h?v=4.7, line 45)](http://lxr.free-electrons.com/source/include/linux/mm_types.h?v4.7#L45)结构表示系统中的每个物理页.
|
||||
|
||||
出于节省内存的考虑,struct page中使用了大量的联合体union.
|
||||
|
||||
|
||||
`mem_map`是一个struct page的数组,管理着系统中所有的物理内存页面。在系统启动的过程中,创建和分配mem_map的内存区域, mem_map定义在[mm/page_alloc.c?v=4.7, line 6691](http://lxr.free-electrons.com/source/mm/page_alloc.c?v=4.7#L6691)
|
||||
|
||||
|
||||
UMA体系结构中,free_area_init函数在系统唯一的struct node对象contig_page_data中node_mem_map成员赋值给全局的mem_map变量
|
||||
|
||||
|
||||
|
||||
#1 启动过程中的内存初始化
|
||||
-------
|
||||
|
||||
在初始化过程中, 还必须建立内存管理的数据结构, 以及很多事务. 因为内核在内存管理完全初始化之前就需要使用内存. 在系统启动过程期间, 使用了额外的简化悉尼股市的内存管理模块, 然后在初始化完成后, 将旧的模块丢弃掉.
|
||||
|
||||
|
||||
#1 建立数据结构
|
||||
-------
|
||||
|
||||
|
||||
|
||||
对相关数据结构的初始化是从全局启动函数start_kernel中开始的, 该函数在加载内核并激活各个子系统之后执行. 由于内存管理是内核一个非常重要的部分, 因此在特定体系结构的设置步骤中检测并确定系统中内存的分配情况后, 会立即执行内存管理的初始化.
|
||||
|
||||
|
||||
##1.2 系统启动
|
||||
-------
|
||||
|
||||
##1.3 节点和内存域的初始化
|
||||
-------
|
||||
|
||||
|
||||
#2 特定于体系结构的设置
|
||||
-------
|
||||
|
||||
##2.1 内核在内存中的布局
|
||||
-------
|
||||
|
||||
##2.2 初始化过程
|
||||
-------
|
||||
|
||||
|
||||
##2.3 分页机制初始化
|
||||
-------
|
||||
|
||||
##2.4 注册活动内存区
|
||||
-------
|
||||
|
||||
##2.5 系统的地址空间设置
|
||||
-------
|
||||
|
||||
Reference in new issue
Block a user