添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

void __init build_all_zonelists(void)
{
int i;

for(i = 0 ; i < numnodes ; i++)
build_zonelists(NODE_DATA(i));//NODE_DATA(i)宏定义可以找到i号内存node对应的discontig_node_data[i]
printk("Built %i zonelists/n", numnodes);
}

build_zonelists()函数调用分两种请款,我们来浅析一下吧。

支持非统一内存访问(即配置了CONFIG_NUMA选项,该选项一般用于多处理器系统)

static void __init build_zonelists(pg_data_t *pgdat)
{
int i, j, k, node, local_node;
int prev_node, load;
struct zonelist *zonelist;//先要介绍一个结构体--struct zonelist.   struct zonelist {
//struct zone *zones[MAX_NUMNODES * MAX_NR_ZONES + 1]; };

//其实就是就是一个指针数组,这个数组里面的每个成员都是一个struct zone,最多可以容纳整个系统的

//所有页区数。

DECLARE_BITMAP(used_mask, MAX_NUMNODES);//这个宏定义其实就是申明一个used_mask[1]这样的数组。

/* initialize zonelists */
for (i = 0; i < GFP_ZONETYPES; i++) {
zonelist = pgdat->node_zonelists + i;//node_zonelists页区列表结构数组,其实就是上面介绍的zonelist结构类型。

//这个列表数组其实就只是有三个元素,分别是我们说的3类页区。可以看出这里只

//是清零初始化。
memset(zonelist, 0, sizeof(*zonelist));
zonelist->zones[0] = NULL;
}

local_node = pgdat->node_id;
load = numnodes;
prev_node = local_node;

bitmap_zero(used_mask, MAX_NUMNODES);//这里是使16个node对应的used_mask[0]这个32位的相应位为0

while ((node = find_next_best_node(local_node, used_mask)) >= 0) {//对于find_next_best_node()函数的分析如下:

static int __init find_next_best_node(int node, void *used_node_mask)
{
int i, n, val;
int min_val = INT_MAX;
int best_node = -1;

for (i = 0; i < numnodes; i++) {
cpumask_t tmp;//这里是为每个处理器设置一位,来标记是否使用过,tmp里面的每一位都代表一个cpu。

n = (node+i)%numnodes;//求余,一上来肯定是以本次node开始的。

if (test_bit(n, used_node_mask))//因为是初始化函数调用的,所以都没有被使用过的。这里会跳过。

continue;

if (!test_bit(node, used_node_mask)) {//这里会执行。

best_node = node;
break;//找到没有被使用过的node就跳出上面的for循环。

val = node_distance(node, n);//如果发现的node不是从本次node开始的,val=1,否则就是0

tmp = node_to_cpumask(n);//由于这里注明是多个cpu的,所以如果是哪个node被使用了,对应的cpu也要标注一下,对应位变化

if (!cpus_empty(tmp))

val += PENALTY_FOR_NODE_WITH_CPUS;

if (val < min_val) {
min_val = val;
best_node = n;
}
}//上面涉及多个cpu时如何选取合适的node,本人现在未能理解。希望以后可以回来完善。

if (best_node >= 0)
set_bit(best_node, used_node_mask);//确实找到所需的node,这样在相应位置上1,表示这个node被使用了。

return best_node;
}//我们回到刚才的while,代码如下:

if (node_distance(local_node, node) !=node_distance(local_node, prev_node))
node_load[node] += load;
prev_node = node;
load--;//重点在后面拉。

for (i = 0; i < GFP_ZONETYPES; i++) {//开始为这个node的成员node_zonelist[]进行设置了。

zonelist = pgdat->node_zonelists + i;

for (j = 0; zonelist->zones[j] != NULL; j++);//寻找到一个不指向空的struct zone的结构体,记录这个J。

k = ZONE_NORMAL;
if (i & __GFP_HIGHMEM)//node_zonelist[2]存放highMem类型页区的strcut zone结构体
k = ZONE_HIGHMEM;
if (i & __GFP_DMA)//node_zonrlist[1]存放dma类型页区的strcut zone结构体
k = ZONE_DMA;

j = build_zonelists_node(NODE_DATA(node), zonelist, j, k);//可以看出第一次调用此node时,把本次node各类页

//区对应的struct zone链接到各类node_zonelist[]上

//等下一次while的时候,我们就把下个node的各类页区

//加载上一次的后面。

zonelist->zones[j] = NULL;//每次都使其尾部指向null。以便下次利用。

下种情况是内存统一的,个人认为,如果只有一个cpu的话,就是使用一个统一的内存。我们用arm的大多数是使用下面这种。

static void __init build_zonelists(pg_data_t *pgdat)
{
int i, j, k, node, local_node;

local_node = pgdat->node_id;
for (i = 0; i < GFP_ZONETYPES; i++) {
struct zonelist *zonelist;

zonelist = pgdat->node_zonelists + i;
memset(zonelist, 0, sizeof(*zonelist));

j = 0;
k = ZONE_NORMAL;
if (i & __GFP_HIGHMEM)
k = ZONE_HIGHMEM;
if (i & __GFP_DMA)
k = ZONE_DMA;

j = build_zonelists_node(pgdat, zonelist, j, k);//上面的容易理解,这里不多说了。

for (node = local_node + 1; node < numnodes; node++)
j = build_zonelists_node(NODE_DATA(node), zonelist, j, k);//这里把本次node后面的node都往上加。

for (node = 0; node < local_node; node++)
j = build_zonelists_node(NODE_DATA(node), zonelist, j, k);//把前面的node也往上加。
zonelist->zones[j] = NULL;//结尾处附上NULL

}//可以看到,我们在哪个内存node都可以访问到其他node不同类型页区的struct zoon结构数据,靠的就是我们这里的主角node_zonelist。

在bootmem_init初始化的时候,已经初始化了 内存 节点的zone成员,该成员是 struct zone数组,存放该 内存 节点的zone信息。在 linux 内存 管理中,分几个阶段进行抽象,用数据结构来管理。先用结点集合管理 内存 ,然后用zone管理结点,再用页的管理zone。此时使用的数据结构分别为pglist_data、zone、page结构体,本章的主要是来 分析 内核是如何完成zonelist的初始化。 1. 数据结构 在结点的pglist_data数据结构中有一个node_zone_list[]类型的st
为什么需要zonelist? 之前bootmem_init初始化的时候,已经初始化了 内存 节点的zone成员,该成员是一个 struct zone数组,存放该 内存 节点的zone信息。 对于uma系统来说,这已经够了,因为uma系统只有一个本地 内存 节点,所有zone的信息都存放在本地 内存 节点的zone成员中。 对于numa系统来说,除了本地 内存 节点,还可以存在一个或多个远端内...
关键 函数 1.__ build _all_zonelists() 2.nr_free_pagecache_pages()__ build _all_zonelists(void *data): 对于UMA系统这个 函数 执行的子 函数 主要有以下几个:(CONFIG_HAVE_MEMORYLESS_NODES 没有配置) for_each_online_node(nid) {//循环找到系统的所有节点
build _zone_zonelists() 初始化备用 内存 域链表 build _zone_zonelists() 建立管理结点及其 内存 域所需的数据结构,即node_zonelists数组。type struct pglist_data{ struct zonelists node_zonelists[MAX_ZONELISTS]; }pg_da
细心的读者可能会发现bootmem流程中每个节点strcut pglist_data构体 中的 strcut zonelist node_zonelists[]成员还未进行初始化。实际上这些操作是通过start_kernel 函数 中的 build _all_zonlists 函数 来完成的。在 分析 函数 前可通过下面链接的博客了解一下备选节点的概念:http://blog.chinaunix.net/uid-30282771-id-5171166.html build _all_zonlists 函数 主要是为node创建一个内
1. 前言 本专题文章承接之前《kernel启动流程_head.S的执行》专题文章,我们知道在head.S执行过程中保存了bootloader传递的启动参数、启动模式以及FDT地址等,创建了内核空间的页表,最后为init进程初始化好了堆栈,并跳转到start_kernel执行。 本文重点介绍start_kernel的 build _all_zonelists的主要流程. kernel版本:5.10 平台:arm64 2. pg_data_t 先来重点看下pg_data_t结构体,有一段注释 On NUMA m
1. 今日内容(第二阶段(二)–初始化备用 内存 域列表zonelists) 我们之前讲了在memblock完成之后, 内存 初始化开始进入第二阶段, 第二阶段是一个漫长的过程, 它执行了一系列复杂的操作, 从体系结构相关信息的初始化慢慢向上层展开, 其主要执行了如下操作 特定于体系结构的设置 在完成了基础的 内存 结点和 内存 域的初始化工作以后, 我们必须克服一些硬件的特殊设置 在初始化 内存 的结点和 内存 区...
内存 管理子系统是 linux 内核最核心最重要的一部分,内核的其他部分都需要在 内存 管理子系统的基础上运行。而对其初始化是了解整个 内存 管理子系统的基础。对相关数据结构的初始化是从全局启动例程start_kernel开始的。本文详细描述了从bootloader跳转到 linux 内核 内存 管理子系统初始化期间所做的操作,从而来加深对 内存 管理子系统知识的理解和掌握。 内核的入口是stext,这是在arch/ar
初始化后zonelists: m1 == node1 movable ; h1 == node1 highmen; n1 == node1 normal; d32.1 == node1 DMA32; d1 == node1 DMA m2 == node2 movable; ......... ZONELIST_ORDER_NODE:pgdat->node_zonelists[0]->_zonerefs[0 ~ node * node->node_zones] = m1 h1 n1
一、伙伴系统原理 1. 伙伴关系 定义:由一个母实体分成的两个各方面属性一致的两个子实体,这两个子实体就处于伙伴关系。在操作系统 分配 内存 的过程中,一个 内存 块常常被分成两个大小相等的 内存 块,这两个大小相等的 内存 块就处于伙伴关系。它满足 3 个条件 :  两个块具有相同大小记为 2^K 它们的物理地址是连续的 从同一个大块中拆分出来 不坑爹不坑娘: 楼主,我最近在看一个有关文件系统的代码,在查找dentry的过程中有个动作是,如果dentry缓存里边没有想要查找的,那么会在缓存里边新建一个dentry: dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL); 看这代码的上下文,可以推断这方法已经把存储介质里边的dentry还原出来放到缓存。 我的疑惑是:我不确定我上面的判断是否正确? Linux中的内存分配和释放之kmem_cache_alloc()函数分析 运动码农: 好文!!!