在对free_area_init_node()函数分析之前,我们也要看看它的源头,这个函数的调用过程如下start_kernel()->paging_init()->free_area_init_node(),我们来看看在调用这个函数之前,在paging_init()前面的语句做了些什么。

/*
  * initialise the zones within each node
  */

for (node = 0; node < numnodes; node++) {undefined
  unsigned long zone_size[MAX_NR_ZONES];//存放这个内存node的全部内存页数。
  unsigned long zhole_size[MAX_NR_ZONES];//存放这次内存node的孔洞内存页数。(每个成员对应该内存node的一个页区)
  struct bootmem_data *bdata;
  pg_data_t *pgdat;
  int i;

/*
   * Initialise the zone size information.
   */
  for (i = 0; i < MAX_NR_ZONES; i++) {undefined
              zone_size[i]  = 0;
              zhole_size[i] = 0;
  }//对这次内存node的最多3个内存页区内存页数和孔洞内存页数清零。

pgdat = NODE_DATA(node);//获得这个node对应的disconting_node_data[n]
  bdata = pgdat->bdata;

/*
   * The size of this node has already been determined.
   * If we need to do anything fancy with the allocation
   * of this memory to the zones, now is the time to do
   * it.
   */
  zone_size[0] = bdata->node_low_pfn -
    (bdata->node_boot_start >> PAGE_SHIFT);//获得本次内存node的全部页数

/*
   * If this zone has zero size, skip it.
   */
  if (!zone_size[0])
   continue;

/*
   * For each bank in this node, calculate the size of the
   * holes.  holes = node_size - sum(bank_sizes_in_node)
   */
  zhole_size[0] = zone_size[0];
  for (i = 0; i < mi->nr_banks; i++) {undefined
   if (mi->bank[i].node != node)
    continue;

zhole_size[0] -= mi->bank[i].size >> PAGE_SHIFT;
  }//上面这个循环可以很容易弄明白zhole_size[0]存放着本次内存node的内存孔洞的页数。

/*
   * Adjust the sizes according to any special
   * requirements for this machine type.
   */
  arch_adjust_zones(node, zone_size, zhole_size);//这是一个空函数。

free_area_init_node(node, pgdat, zone_size,
    bdata->node_boot_start >> PAGE_SHIFT, zhole_size);
 }

void __init free_area_init_node(int nid, struct pglist_data *pgdat,
  unsigned long *zones_size, unsigned long node_start_pfn,
  unsigned long *zholes_size)
{undefined
  pgdat->node_id = nid;
  pgdat->node_start_pfn = node_start_pfn;
  calculate_zone_totalpages(pgdat, zones_size, zholes_size);//求出本节点所有内存页区的页数总和(包括内存孔洞的),所有内存页区的实际页数(不包括内存孔洞的)。

if (!pfn_to_page(node_start_pfn))//我们来看看它的宏定义:

//#define pfn_to_page(pfn) ((mem_map + (pfn)) - PHYS_PFN_OFFSET),按注释来说

//这个宏是通过pfn(这是物理页号)来求得对应的struct page结构体所在的存储单元。

//mem_map表示这个节点所有页的struct page结构体的空间起始位置,PHYS_PFN_OFFSET

//是RAM空间的起始页号,通过上面的计算就可以求得对应页的struct page结构体的地址。

//如果地址为空,就说明没有为其分配空间了。
       node_alloc_mem_map(pgdat);//为该内存node的所有内存页的strcut page结构体申请空间,我们是从DMA内存空间之后的空

//间开始申请的,申请空间按cache line (cache块大小=32)的方式对其的。

free_area_init_core(pgdat, zones_size, zholes_size);//这个是核心函数,等下我们会把它展开细讲的,这个函数主要就是完成

//本次内存node的内存页区的struct zoon结构体成员的设置。
}

static void __init free_area_init_core(struct pglist_data *pgdat,
  unsigned long *zones_size, unsigned long *zholes_size)
{undefined
 unsigned long i, j;
 const unsigned long zone_required_alignment = 1UL << (MAX_ORDER-1);
 int cpu, nid = pgdat->node_id;
 unsigned long zone_start_pfn = pgdat->node_start_pfn;

pgdat->nr_zones = 0;//将该内存节点的内存页区数清0.
 init_waitqueue_head(&pgdat->kswapd_wait);//初始化内存节点的内存置换等待队列。
 
 for (j = 0; j < MAX_NR_ZONES; j++) {//对每个节点的每个页区进行操作。
             struct zone *zone = pgdat->node_zones + j;//让zone作为本次内存节点每个页区的结构指针。
             unsigned long size, realsize;
             unsigned long batch;

zone_table[NODEZONE(nid, j)] = zone;//把内存node的各个页区的指针结构保留在全局指针数组zone_table[n]里。
             realsize = size = zones_size[j];
             if (zholes_size)
                  realsize -= zholes_size[j];

if (j == ZONE_DMA || j == ZONE_NORMAL)
                  nr_kernel_pages += realsize;//nr_kernel_pages是全局变量,保存着所有内存node的dma和normal页区的实际页数。
             nr_all_pages += realsize;//nr_all_pages是全局变量,保存着每个内存node的3个页区的实际存在的页数。

zone->spanned_pages = size;//该页区内的总页数。
             zone->present_pages = realsize;//该页区内的实际页数。
             zone->name = zone_names[j];//该页区的明“dma, normal, highMem”
             spin_lock_init(&zone->lock);
             spin_lock_init(&zone->lru_lock);//页区的锁。
             zone->zone_pgdat = pgdat;//页区所属内存节点的内存节点数据结构。
             zone->free_pages = 0;//该页区的空闲页数。

zone->temp_priority = zone->prev_priority = DEF_PRIORITY;//页区临时和预置优先级别都是DEF_PRIORITY=12.

/*
   * The per-cpu-pages pools are set to around 1000th of the
   * size of the zone.  But no more than 1/4 of a meg - there's
   * no point in going beyond the size of L2 cache.
   *
   * OK, so we don't know how big the cache is.  So guess.
   */
             batch = zone->present_pages / 1024;
             if (batch * PAGE_SIZE > 256 * 1024)
                    batch = (256 * 1024) / PAGE_SIZE;
             batch /= 4;//要求最大不可以超过16
             if (batch < 1)
                    batch = 1;//小于1的话就附上1.

for (cpu = 0; cpu < NR_CPUS; cpu++) {//设置本页区中每个处理器的冷区和热区内存数据结构。
                    struct per_cpu_pages *pcp;

pcp = &zone->pageset[cpu].pcp[0]; //每个页区都为每个处理器维护一组高速缓存内存--struct per_cpu_pageset

//pageset[]。每个pageset[]包含一个struct per_cpu_pages pcp[2]数组

//这里的pcp[0]代表热区高速缓存内存。
                    pcp->count = 0;//per_cpu_pages这个结构体是描述一些空闲页的,count就代表当前空闲页数。
                    pcp->low = 2 * batch;//这是current的下限原则。
                    pcp->high = 6 * batch;//这是current得上限原则。
                    pcp->batch = 1 * batch;
                    INIT_LIST_HEAD(&pcp->list);//初始化一个链表,这个是用来连接pcp高速缓存内存所指的那些内存页的struct page

pcp = &zone->pageset[cpu].pcp[1]; /* cold */
                    pcp->count = 0;
                    pcp->low = 0;
                    pcp->high = 2 * batch;
                    pcp->batch = 1 * batch;
                    INIT_LIST_HEAD(&pcp->list);//上面的几行是设置冷区的,不同的地方在于low,high
             }
             printk(KERN_DEBUG "  %s zone: %lu pages, LIFO batch:%lu/n",
             zone_names[j], realsize, batch);
             INIT_LIST_HEAD(&zone->active_list);//该内存页区中以激活的内存页的描述结构中的lru会连接到这个链表中的。
             INIT_LIST_HEAD(&zone->inactive_list);//这里和上述的意义是相反的,这里是未激活的。
             zone->nr_scan_active = 0;//扫描激活内存页数。
             zone->nr_scan_inactive = 0;
             zone->nr_active = 0;//激活的内存页数。
             zone->nr_inactive = 0;
             if (!size)
                continue;//如果该页区的内存页数为0时,不考虑这个页区。

/*
   * The per-page waitqueue mechanism uses hashed waitqueues
   * per zone.
   */
             zone->wait_table_size = wait_table_size(size);//zone->wait_table_size表示等待队列的大小,其实就是等待队列的

//个数,一般PAGES_PER_WAITQUEUE(256)页为一个等待队列,

//等待队列的个数介于0~4096,同时也要求必须是2的N次幂。
            zone->wait_table_bits =
            wait_table_bits(zone->wait_table_size);//其实就是求出上面语句2的N次幂中的"1"所在的位号。
            zone->wait_table = (wait_queue_head_t *)
            alloc_bootmem_node(pgdat, zone->wait_table_size
            * sizeof(wait_queue_head_t));//wait_table_size个wait_queue_head_t结构申请了空间,返回的地址放在

//wait_table。一个wait_queue_head_t可以构成一个链表。

for(i = 0; i < zone->wait_table_size; ++i)
                init_waitqueue_head(zone->wait_table + i);//初始化wait_queue_head_t这个结构体的锁和链表。

pgdat->nr_zones = j+1;//统计这个内存node的页区数

zone->zone_mem_map = pfn_to_page(zone_start_pfn);//保存这个内存页区struct page结构空间的起始地址。
            zone->zone_start_pfn = zone_start_pfn;//该内存页区的物理起始页号(会发现这个和本次节点的物理起始页号相同。)

if ((zone_start_pfn) & (zone_required_alignment-1))//可以看出这里要求这个物理地址要按2exp(max_order)对齐
                  printk("BUG: wrong zone alignment, it will crash/n");

memmap_init(size, nid, j, zone_start_pfn);//该内存页区中每页内存对应的struct page结构设置一下。具体设置如下:

//1.该内存页区在全局变量zone_table[]数组中的下标号存放到该页区所有页

//的struct page结构体中标志字flags的最高若干位。

//2.page->_count=-1;page->_mapcount=-1;设置标志字flags中

//PG_reserved=1这样是为了保护内存页不被释放掉。

zone_start_pfn += size;//计算下一个内存页区的开始页的页号。

zone_init_free_lists(pgdat, zone, zone->spanned_pages);//初始化这个内存页区对应的struct zone的成员

//free_area[MAX_ORDER]数组,主要干如下事情:

//1.初始化free_area[order].free_list链表

//2.为每个order的伙伴位码表申请空间,空间地址存放在

//free_area[order].map处。
            }
}

好了,这样我就把free_area_init_node()分析完了。
————————————————

//----------------------------------------

//1.mm/Numa.c->free_area_init_node()

void __init free_area_init_node(int nid, pg_data_t *pgdat, struct page *pmap,

unsigned long *zones_size, unsigned long zone_start_paddr,

unsigned long *zholes_size)

{

free_area_init_core(0, &contig_page_data, &mem_map, zones_size,

zone_start_paddr, zholes_size, pmap);

}

//----------------------------------------

//2.mm/Page_alloc.c->free_area_init_core()

void __init free_area_init_core(int nid, pg_data_t *pgdat, struct page **gmap,

unsigned long *zones_size, unsigned long zone_start_paddr,

unsigned long *zholes_size, struct page *lmem_map)

{

unsigned long i, j;

unsigned long map_size;

unsigned long totalpages, offset, realtotalpages;

const unsigned long zone_required_alignment = 1UL (MAX_ORDER-1);

if (zone_start_paddr & ~PAGE_MASK)//zone_start_paddr一定是要页对齐的

BUG();

totalpages = 0;

//#define MAX_NR_ZONES 3

//累加所有zone大小到totalpages

for (i = 0; i MAX_NR_ZONES; i++) {

unsigned long size = zones_size;

totalpages += size;

}

realtotalpages = totalpages;

if (zholes_size)

for (i = 0; i MAX_NR_ZONES; i++)

realtotalpages -= zholes_size;//挖掉每一个zone内部的holes空间

printk("On node %d totalpages: %lu\n", nid, realtotalpages);

//每一个4k物理页都对应一个mem_map_t来管理

map_size = (totalpages + 1)*sizeof(struct page);

//多申请1个sizeof(struct page),因为可能会调整掉第一个sizeof(struct page)大小空间的前几个字节

if (lmem_map == (struct page *)0) {

lmem_map = (struct page *) alloc_bootmem_node(pgdat, map_size);

//将申请到的map_size大小的空间起始地址lmem_map进行sizeof(mem_map_t)字节对齐调整,其对齐基址为PAGE_OFFSET

lmem_map = (struct page *)(PAGE_OFFSET + MAP_ALIGN((unsigned long)lmem_map - PAGE_OFFSET));

}

*gmap = pgdat->node_mem_map = lmem_map;//存储到mem_map中,之后mem_map=lmem_map

pgdat->node_size = totalpages;

pgdat->node_start_paddr = zone_start_paddr;

pgdat->node_start_mapnr = (lmem_map - mem_map);//lmem_map和mem_map相对距离,距离多少个mem_map_t

pgdat->nr_zones = 0;

offset = lmem_map - mem_map;

for (j = 0; j MAX_NR_ZONES; j++) {

zone_t *zone = pgdat->node_zones + j;

unsigned long mask;

unsigned long size, realsize;

zone_table[nid * MAX_NR_ZONES + j] = zone;

realsize = size = zones_size[j];

if (zholes_size)

realsize -= zholes_size[j];

//打印输出内容如下:

//On node 0 totalpages: 8192

//zone(0): 8192 pages.

//zone(1): 0 pages.

//zone(2): 0 pages.

printk("zone(%lu): %lu pages.\n", j, size);

zone->size = size;

zone->name = zone_names[j];

//zone_names[0]="DMA"

//zone_names[1]="Normal"

//zone_names[2]="HighMem"

//zone只是内核为了管理方便做的一种逻辑上的划分,并不存在这种物理硬件单元[gliethttp]

zone->lock = SPIN_LOCK_UNLOCKED;

zone->zone_pgdat = pgdat;

zone->free_pages = 0;

zone->need_balance = 0;

if (!size)

continue;

//用于进程睡眠在当前页上的waitqueue个数

zone->wait_table_size = wait_table_size(size);

zone->wait_table_shift =

BITS_PER_LONG - wait_table_bits(zone->wait_table_size);

//申请zone->wait_table_size*sizeof(wait_queue_head_t)个字节连续数据

zone->wait_table = (wait_queue_head_t *)

alloc_bootmem_node(pgdat, zone->wait_table_size

* sizeof(wait_queue_head_t));

//初始化wait_table中wait_queue_head表项

for(i = 0; i zone->wait_table_size; ++i)

init_waitqueue_head(zone->wait_table + i);

pgdat->nr_zones = j+1;//记录当前zone的下一个zone号

//static int zone_balance_ratio[MAX_NR_ZONES] __initdata = { 128, 128, 128, };

//static int zone_balance_min[MAX_NR_ZONES] __initdata = { 20 , 20, 20, };

//static int zone_balance_max[MAX_NR_ZONES] __initdata = { 255, 255, 255, };

mask = (realsize / zone_balance_ratio[j]);

if (mask zone_balance_min[j])

mask = zone_balance_min[j];

else if (mask > zone_balance_max[j])

mask = zone_balance_max[j];

//系统中空闲物理页数绝对不要少于pages_min

//该zone区内存最底限,到达该值时,分配器将同步调用kswapd,

//让其将内核中的某些页交换到外存,从而保证系统中有足够的空闲页块.

zone->pages_min = mask;

//当系统中空闲物理页数少于pages_low时,开始强化交换

zone->pages_low = mask*2;

//当空闲物理页数少于pages_high时,启动后台交换;而当空闲物理页数大于pages_high时,内核交换守护进程什么也不做

zone->pages_high = mask*3;//该zone区内存临界值

//管理每个4k页的数据结构mem_map_t的起始地址zone->zone_mem_map

zone->zone_mem_map = mem_map + offset;

zone->zone_start_mapnr = offset;//lmem_map和mem_map相对距离,距离多少个mem_map_t

zone->zone_start_paddr = zone_start_paddr;//该zone的起始地址

//该zone_start_paddr应该是2^9,即:512页对齐,即:512*4k=2M字节对齐

if ((zone_start_paddr >> PAGE_SHIFT) & (zone_required_alignment-1))

printk("BUG: wrong zone alignment, it will crash\n");

for (i = 0; i size; i++) {

struct page *page = mem_map + offset + i;

//对每一个4k页对应的mem_map_t管理结构进行如下相同的初始化

set_page_zone(page, nid * MAX_NR_ZONES + j);//设置flag的zone标志区

set_page_count(page, 0);//设置使用本页的用户数目为0

SetPageReserved(page);//设置本页flag为PG_reserved,由系统保留使用

INIT_LIST_HEAD(&page->list);//初始化list的头和屁股使其都指向自己

if (j != ZONE_HIGHMEM)

set_page_address(page, __va(zone_start_paddr));//对于我的armlinux版本,此为空

zone_start_paddr += PAGE_SIZE;//因为上一句没有,所以此举也无用

}

offset += size;

for (i = 0; ; i++) {

//linux的物理页分配采用链表和位图结合的方法.linux内核定义了一个称为free_area的数组,

//该数组的每一项描述某一种页块的信息

//zone->free_area[0]--2^0=1页空闲链表

//zone->free_area[1]--2^1=2页空闲链表

//zone->free_area[2]--2^2=4页空闲链表

//...

//zone->free_area[9]--2^9=512页空闲链表

//数组free_area的每项包含三个元素:next、prev和map.指针next、prev用于将物理页块结构mem_map_t连结成一个双向链表;

//而map则是记录这种页块组分配情况的位图,例如,位图的第N位为1,表明第N个页块是空闲的.从图中也可以看到,

//用来记录页块组分配情况的位图大小各不相同,显然页块越小,位图越大.free_area数组的大小为10或12(可调),

//因此它管理的最小内存块的大小是1页(4K),最大内存块的大小是512页(2M)或2048页(8M).

//页分配代码使用向量表free_area来分配和回收物理页

unsigned long bitmap_size;

INIT_LIST_HEAD(&zone->free_area.free_list);//初始化free_area的链表

if (i == MAX_ORDER-1) {

zone->free_area.map = NULL;

//我的armlinux,MAX_ORDER=10,所以一共2^9=512个页,所以kmalloc最多申请512个连续页=512*4k=2M

break;

}

//对于Buddy(伙伴)算法,采用模型:index >> (i+1);[i为order值,index一般是"size-1"]

//所以Buddy管理的内存总大小为size-1,记得在念高中的时候有这样一道数学题

//s=1/2+1/4+1/8+...1/2^n的值是多少,怎么计算呢?先将等式两端分别乘上2,即:

//2s=1+1/2+1/4+...1/2^(n+1)=逼近1+s,所以s=逼近1,但永远都达不到1.

//所以从这个角度我们可以看到Buddy的算法中,不论MAX_ORDER有多大10,10000...,甚至无穷,也不会把size-1切割到0

//size-1数据穷尽,当然这还要考虑小于1字节时已经不存在意义的问题,

//但是MAX_ORDER已经有很强的伸缩性了[gliethttp 2007-08-05]

//free_area[0].map位图大小=((size-1)/ 2)/8=(size-1) >> (0+1+3)

//free_area[1].map位图大小=((size-1)/ 4)/8=(size-1) >> (1+1+3)

//...

//free_area[9].map位图大小=((size-1)/1024)/8=(size-1) >> (9+1+3)

bitmap_size = (size-1) >> (i+4);

//因为所有bitmap的操作都是long类型4字节操作,所以需要将bitmap_size调整成>=先前bitmap_size的4倍数

//可以假想bitmap_size&0x03=1,出现零头,怎么办,显然我们不能不管,所以

//#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))

//(1+3)&~3这样对齐到了上边界,

//但是为什么要传递bitmap_size+1进行调整呢,这是为了,当bitmap_size&0x03=0时,它还要进行

//(0+1+3)&~3,这样就到了下一个对齐单元,但明明已经对齐了,为什么还要加,我想这可能和统一的风格和舒适度有关系吧!

//所以调整后的bitmap_size肯定大于先前为调整的bitmap_size,1~4字节之间

bitmap_size = LONG_ALIGN(bitmap_size+1);

zone->free_area.map = //返回32字节对齐后的bitmap_size大小虚拟地址空间首地址

//请参考《浅析armlinux-setup_arch()->alloc_bootmem_low_pages()函数5-1》[http://gliethttp.cublog.cn]

(unsigned long *) alloc_bootmem_node(pgdat, bitmap_size);

}

}

build_zonelists(pgdat);//将zone登记到zonelist中

}

//----------------------------------------

//3.

#define PAGES_PER_WAITQUEUE 256

static inline unsigned long wait_table_size(unsigned long pages)

{unsigned long size = 1;

pages /= PAGES_PER_WAITQUEUE;//8192/256=32

while (size pages)

size = 1;//计算上边界

size = min(size, 4096UL);//权衡一个用来进程睡眠wait queue的合理大小

return size;

}

//----------------------------------------

//4.

static inline unsigned long wait_table_bits(unsigned long size)

{

//fft[find first zero in word]从低位开始找到第一个0的位置

//所以这里是查找size中第一个出现1的位置

return ffz(~size);

}

//----------------------------------------

//5.

static inline void set_page_zone(struct page *page, unsigned long zone_num)

{

page->flags &= ~(~0UL ZONE_SHIFT);//清0高8位

page->flags |= zone_num ZONE_SHIFT;//将zone_num送到高8位

}

//----------------------------------------

//6.include/linux/Mm.h->SetPageReserved()

#define SetPageReserved(page) set_bit(PG_reserved, &(page)->flags)

//arch/arm/lib/setbit.S->set_bit()

ENTRY(set_bit)

and r2, r0, #7//r0在字节内的移位量0~7

mov r3, #1

mov r3, r3, lsl r2//将计算偏移后结果存入r3

save_and_disable_irqs ip, r2//禁用中断,同时ip=cpsr

ldrb r2, [r1, r0, lsr #3]//读取第r0位对应字节处的字节数据,即:r1+r0/8

orr r2, r2, r3//相应位置1

strb r2, [r1, r0, lsr #3]//回写

restore_irqs ip

RETINSTR(mov,pc,lr)

//----------------------------------------

//7.mm/Page_alloc.c->build_zonelists()

static inline void build_zonelists(pg_data_t *pgdat)

{int i, j, k;

//#define GFP_ZONEMASK 0x0f

for (i = 0; i = GFP_ZONEMASK; i++) {

zonelist_t *zonelist;

zone_t *zone;

zonelist = pgdat->node_zonelists + i;

//对memset的理解可参见《浅析armlinux-__arch_clear_user内存拷贝函数之3》[http://gliethttp.cublog.cn]

memset(zonelist, 0, sizeof(*zonelist));

j = 0;

//#define ZONE_DMA 0

//#define ZONE_NORMAL 1

//#define ZONE_HIGHMEM 2

//所以GFP_ZONEMASK的所有i值,都被0x3掩掉

k = ZONE_NORMAL;

if (i & __GFP_HIGHMEM)

k = ZONE_HIGHMEM;

if (i & __GFP_DMA)

k = ZONE_DMA;

switch (k) {//我的armlinux版本只提供3种zone

default:

BUG();

case ZONE_HIGHMEM:

zone = pgdat->node_zones + ZONE_HIGHMEM;

if (zone->size) {

#ifndef CONFIG_HIGHMEM

BUG();

#endif

zonelist->zones[j++] = zone;//将ZONE_HIGHMEM对应的node填入表中

}

case ZONE_NORMAL:

zone = pgdat->node_zones + ZONE_NORMAL;

if (zone->size)

zonelist->zones[j++] = zone;//将ZONE_NORMAL对应的node填入表中

case ZONE_DMA:

zone = pgdat->node_zones + ZONE_DMA;

if (zone->size)

zonelist->zones[j++] = zone;//将ZONE_DMA对应的node填入表中

}

zonelist->zones[j++] = NULL;//给node链表追加结尾

}

}

free_area_init_node相关推荐

  1. 怎么查linux的虚拟内核,Linux 内核 虚拟地址 物理地址 转换

    这里只分析分配连续物理地址的函数.对于 vmalloc() 这种分配非连续物理地址的函数不在本记录范围之内. 1.kmalloc() 分配连续的物理地址,用于小内存分配. 2.__get_free_p ...

  2. linux 初始化内存管理_Linux内存管理第二章 -- Describing Physical Memory

    首先来描述几个名词: NUMA:Non-Uniform Memory Access即内存非一致性访问.在很多巨星机上内存被分成了多个区域,CPU访问不同的内存区域耗费的时间是不同的.但在同一区域内,C ...

  3. 一文掌握 Linux 内存管理

    作者:dengxuanshi,腾讯 IEG 后台开发工程师 以下源代码来自 linux-5.10.3 内核代码,主要以 x86-32 为例. Linux 内存管理是一个很复杂的"工程&quo ...

  4. 嵌入式Linux内核移植相关代码分析(转)

    本文通过整理之前研发的一个项目(ARM7TDMI +uCLinux),分析内核启动过程及需要修改的文件,以供内核移植者参考.整理过程中也同时参考了众多网友的帖子,在此谢过.由于整理过程匆忙,难免 错误 ...

  5. mini2440-Openwrt启动信息

    2019独角兽企业重金招聘Python工程师标准>>> [    0.000000] Booting Linux on physical CPU 0x0 [    0.000000] ...

  6. linux内核那些事之pg_data_t、zone结构初始化

    free_area_init 继续接着<linux内核那些事之ZONE>,分析内核物理内存初始化过程,zone_sizes_init()在开始阶段主要负责对各个类型zone 大小进行计算, ...

  7. linux内核那些事之ZONE

    struct zone 从linux 三大内存模型中可以了解到,linux内核将物理内存按照实际使用用途划分成不同的ZONE区域,ZONE管理在物理内存中占用重要地位,在内核中对应的结构为struct ...

  8. linux内核那些事之物理内存模型之DISCONTIGMEM(2)

    距离写linux物理内存模型-FLATMEM已经过去很久了,终于能够抽时间能够继续整下,中间这么长时间一直在看不同版本的内核代码,加之工作比较忙,希望自己能够坚持下去. UMA VS NUMA DIS ...

  9. linux内核那些事之物理内存模型之FLATMEM(1)

    linux内核中物理内存管理是其中比较重要的一块,随着内核从32位到64位发展,物理内存管理也不断进行技术更新,按照历史演进共有FLATMEM.DISCONTIGMEM以及SPRARSEMEM模型.( ...

最新文章

  1. 关于jsp页面是放在webroot目录下和web-inf下优缺点
  2. AI让边缘更智能,边缘让AI无处不在
  3. vlc android 移植版编译
  4. 四、规则组织的衍生组织——经向破斜组织数学模型的建立
  5. 软件测试人员棘手的问题,Èí¼þ²âÊԵļ¬ÊÖÎÊÌ⣺ÈçºÎ±ÜÃâÖظ´ÌύȱÏÝ...
  6. 启动vue项目失败,报错Failed at the node-sass@4.14.1 postinstall script.
  7. KNN算法(K近邻学习)
  8. 计算机的cpu怎么设计出来的,CPU是如何制作出来的?cpu的制造流程介绍
  9. 【缺陷检测】基于matlab GUI形态学PCB电路板缺陷检测【含Matlab源码 821期】
  10. eclipse 设置和快捷键
  11. 解密深圳IT人士的当前薪情【转自:中国it实验室】
  12. 计算机图形学学习:GAMES101
  13. 手机存储数据恢复软件哪个好用?试了10款,我只认准这一款!
  14. 速都app推广:移动网页seo优化和APP发布与aso优化试水
  15. 计算机无法发现网络共享打印机,网络打印机,无法被其它电脑识别
  16. qt获取本机IP地址、计算机名、网络连接名、MAC地址、子网掩码、广播地址
  17. 常数和基本初等函数导数公式推导
  18. 3D与2D动画哪一个更容易?
  19. 面试官:大文件上传如何做断点续传?
  20. Chrome 浏览器打开页面变成下载html文件的问题

热门文章

  1. 同义词词典有利于提高Tag向量匹配度
  2. FLV:适合录制的多媒体格式
  3. 轻度分析游戏之《啪啪三国》
  4. 为何会拿好人卡(二) 安全感是什么?
  5. 网络直播采用的技术(一)
  6. 同事离职,留下了我独自流泪改代码
  7. 2021百度云存储面试
  8. 80后的超彪悍省钱术
  9. 如何仿写iOS微信打飞机
  10. 使用计算机还原可以恢复被误杀的软件吗,杀毒软件误杀的文件怎么恢复?