内存管理 初始化(八) 至kswapd_init

至此,内存初始化部分已看完,遗留问题:

1、对于unicore或者mips的页表建立都很清楚,但是对于ARM我不清楚:

      初始化部分涉及的页表映射建立,我都以unicore架构为准,ARM的页表映射从原理上讲easy,问题在于ARM的页表中没有引入Dirty、Accessed位,因此,对于如何在基于ARM架构的Linux系统上实现页回收就有些疑问,上次和同学看下代码,ARM使用了软件的方法解决了该问题,但是具体方法自己并不清楚.  当然对于新的ARM架构可能在页表项上已支持Dirty、Accessed位。

2、kswap的原理、实现?

3、对于zone->free_area的free_list的链表中的页的添加顺序(tail or head),以及在其它的缓存机制中加入链表时——加入链表尾和链表头的区别?冷页、热页?

4、ZONE_MOVABLE区,希望关于这个区是我自己看错了,或者是对于嵌入式系统该区就没有利用,因为在我跟踪内存管理初始化的过程中,该区基本没用,但确实存在该区:为什么要引入ZONE_MOVABLE、有什么用?

5、zone:lowmem_reserve有什么用?

6、我回避了文件系统初始化部分,以及init_post的实现过程,我暂时不想把它们混入内存初始化部分。

7、.etc

kmem_cache_init_late之后的代码至kswapd_init

start_kernel()
 
|-->page_address_init() | |-->setup_arch(&command_line); | |-->setup_per_cpu_areas(); | |-->build_all_zonelist() | |-->page_alloc_init() | |-->pidhash_init() | |-->vfs_caches_init_early() | |-->mm_init() | |-->....... | |-->init_IRQ() | |-->...... | |-->gfp_allowed_mask = __GFP_BITS_MASK; | |-->kmem_cache_init_late(); | |-->...... | |-->setup_per_cpu_pageset(); | 各个CPU申请内存时,如果需要获取页,则从各个zone中自己 | per_cpu_pageset获取(zone->pageset[]), 此处完成初始化. | |-->...... | |-->anon_vma_init(); |-->anon_vma_cachep = kmem_cache_create("anon_vma",
      |                                    sizeof(struct anon_vma), |        0, SLAB_DESTROY_BY_RCU|SLAB_PANIC, anon_vma_ctor);       | |-->anon_vma_chain_cachep = KMEM_CACHE(anon_vma_chain, SLAB_PANIC); | 即anon_vma_chain_cachep = kmem_cache_create("anon_vma_chain", | sizeof(struct anon_vma_chain), | __alignof__(struct anon_vma_chain), | SLAB_PANIC, NULL); | |-->fork_init(totalram_pages); | |-->proc_caches_init(); | |-->...... | |-->buffer_init(); | 没看 |-->...... | |-->vfs_caches_init(totalram_pages); | 虚拟文件系统初始化 | |-->...... | |-->rest_init();

 

 

void setup_per_cpu_pageset(void)

  |-->struct zone *zone = NULL;

  |   int cpu = 0;

  |

  |--for_each_populated_zone(zone)

  |--{

  |    遍历所有的zone(zone->present_pages需不为0,注意ZONE_MOVABLE)

  |

  |    zone->pageset = alloc_percpu(struct per_cpu_pageset);

  |    原来的pageset职责是由全局的boot_pageset变量担当的,现在进行重新申请.

  |    关于alloc_percpu的percpu资源初始化是在setup_per_cpu_areas中完成的,

  |    这是通过bootmem完成的资源分配.

  |

  |    for_each_possible_cpu(cpu)

  |    {

  |      struct per_cpu_pageset *pcp = per_cpu_ptr(zone->pageset, cpu);

  |      setup_pageset(pcp, zone_batchsize(zone));

  |

  |      此处我们可以关注下对于boot_pageset的初始化:

  |      setup_pageset(&per_cpu(boot_pageset, cpu), 0);最后的参数是0

  |

  |      if(percpu_pagelist_fraction) setup_pagelist_highmark(pcp,

  |                 (zone->present_pages / percpu_pagelist_fraction));
 |      关于percpu_pagelist_fraction是通过proc来配置的,此处我们可以认为 | percpu_pagelist_fraction是0 | | } |--}

 

 

void fork_init(unsigned long mempages)
|-->task_struct_cachep = kmem_cache_create("task_struct",
 |                                      sizeof(struct task_struct), | ARCH_MIN_TASKALIGN, SLAB_PANIC|SLAB_NOTRACK, NULL); | |-->max_threads = mempages / (8 * THREAD_SIZE / PAGE_SIZE); | if(max_threads < 20) max_threads = 20; | |-->init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2; | init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2; | init_task.signal->rlim[RLIMIT_SIGPENDING] = | init_task.signal->rlim[RLIMIT_NRPOC];

 

 

void proc_caches_init(void)

  |-->sighand_cachep = kmem_cache_create("sighand_cache",

  |                    sizeof(struct sighand_struct), 0,

  |    SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_DESTROY_BY_RCU|

  |    SLAB_NOTRACK, sighand_ctor);

  |

  |-->signal_cachep = kmem_cache_create("signal_cache",

  |                     sizeof(struct signal_struct), 0,

  |    SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK, NULL);

  |

  |-->files_cachep = kmem_cache_create("files_cache",

  |                     sizeof(struct files_struct), 0,

  |    SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK, NULL);

  |

  |-->fs_cachep = kmem_cache_create("fs_cache",

  |                     sizeof(struct fs_struct), 0,

  |    SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK, NULL);

  |

  |-->mm_cachep = kmem_cache_create("mm_struct",

  |     sizeof(struct mm_struct), ARCH_MIN_MMSTRUCT_ALIGN,

  |      SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK, NULL);

  |

  |-->vm_area_cachep = KMEM_CACHE(vm_area_struct, SLAB_PANIC);

  | 即kmem_cache_create("vm_area_struct", 

  |                      sizeof(struct vm_area_struct), 
  |  __alignof__(struct vm_area_struct), SLAB_PANIC, NULL); |-->mmap_init();



void mmap_init(void)

  |-->int ret;

  |   ret = percpu_counter_init(&vm_committed_as, 0);

  |         |-->__percpu_counter_init(&vm_committed_as, 0);

            

int __percpu_counter_init(struct percpu_counter *fbc, s64 amount,

        struct lock_class_key *key)

  |-->spin_lock_init(&fbc->lock);

  |-->fbc->count = amount;

  |-->fbc->counters = alloc_percpu(s32);

  |-->return 0;

 

 

 

void rest_init(void)
 |-->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); | |-->kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); | |-->kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); | |-->cpu_idle();

 

 

int kernel_init(void *unused)

  |-->smp_prepare_cpus(setup_max_cpus);

  |   |-->wakeup_secondary();

  |

  |-->smp_init()

  |   |-->cpu_up(cpu)

  |   |   |-->_cpu_up(cpu, 0)

  |   |   |   |-->__cpu_up(cpu);

  |   |   |      |-->boot_secondary(cpu, idle)

  |

  |-->do_basic_setup();

      |-->......

      |   并不意味这不重要,此处先回避相应的函数

      |

      |-->do_initcalls();

      |   对于这个函数,我们主要关注下:

      |   rootfs_initcall(populate_rootfs);

      |   module_init(init_per_zone_wmark_min);

      |   module_init(kswapd_init);

      |

  |

  |-->......

  |

  |-->init_post();


int init_post(void)

  |-->free_initmem()

      |-->totalram_pages += free_area(__phys_to_pfn(__pa(__init_begin)),

      |                               __phys_to_ptn(__pa(__init_end)),

      |                               "init");

      |   释放内核初始化相关部分的空间.

  |

  |-->system_state = SYSTEM_RUNNING;

  |

  |-->run_init_process(...)

 

 

 

我们此处重点关注: 

rootfs_initcall(populate_rootfs);

module_init(init_per_zone_wmark_min);

module_init(kswapd_init).

为什么?

1、populate_rootfs因为不知对于__initramfs_start ~ __initramfs_end的处理,

所以看下,当然不会太深入,这可是文件系统啊; 

2、init_per_zone_wmark_min涉及zone->watermark[]初始化;

3、kswapd_init页回收机制初始化(这个没有深入,我想看会资料后,在详细做下记录).



直接编译进内核:

#define __define_initcall(level, fn, id) \

    static initcall_t __initcall_##fn##id __used \

    __attribute__((__section__(".initcall" level ".init"))) = fn



#define rootfs_initcall(fn) __define_initcall("rootfs", fn, rootfs) 



#define device_initcall(fn) __define_initcall("6", fn, 6)

#define __initcall(fn)      device_initcall(fn)

#define module_init(x)      __initcall(x)

链接脚本中的__early_initcall_end和__initcall_end

                                                                                                                   

void do_initcalls(void)

  |-->initcall_t *fn;

  |-->for(fn = __early_initcall_end; fn < __initcall_end; fn++)

  |      do_one_initcall(*fn);

  |

  |-->flush_scheduled_work();


int populate_rootfs(void)

  |-->char *err = unpack_to_rootfs(__initramfs_start,

  |      __initramfs_end - __initramfs_start);

  |   关注下usr目录下的Makefile就明白了.

  |

  |-->if(initrd_start)

  |--{

  |    err = unpack_to_rootfs((char*)initrd_start, initrd_start,

  |          initrd_end - initrd_start);

  |    此处没有深入下去,文件系统以后解决.

  |

  |    if(!err){free_initrd(); return 0;}

  |    else .....

  |    ......

  |--}

  |  return 0;





这个没有深入,我想看会资料后,在详细做下记录

int kswapd_init(void)

  |-->swap_setup();

  |-->kswapd_run(0);

      |-->contig_page_data.kswapd = kthread_run(
     
| &contig_page_data, "kswapd0");

 

 

 

int init_per_zone_wmark_min(void)

  |-->unsigned long lowmem_kbytes;

  |  lowmen_kbytes = nr_free_buffer_pages() * (PGAE_SIZE >> 10);

  |  nr_free_buffer_pages()获取ZONE_DMA和ZONE_NORMAL区的页数.

  |  当然,如果没有ZONE_DMA,则只获取ZONE_NORMAL区的页数

  |

  |-->min_free_kbytes = int_sqrt(lowmem_kbytes * 16);

  |

  |-->if(min_free_kbytes < 128) min_free_kbytes = 128;

  |-->if(min_free_kbytes > 65536) min_free_kbytes = 65536;

  |

  |-->setup_per_zone_wmarks();

  |-->setup_per_zone_lowmem_reserve();

  |-->setup_per_zone_inactive_ratio();

  |-->return 0;

 

 

关于该函数的理解,可能有误:

每个zone都可能被耗尽,为了解决这种情况,

就在比自己低阶的zone中,把自身的一部分内存保存在低阶的

zone->lowmem_reserve[]中,以备自身被耗尽.

void setup_per_zone_lowmem_reserve(void)

  |-->struct pglist_data *pgdat;

  |   enum zone_type j, idx;

  |

  |-->考虑UMA

  |-->pgdat = contig_page_data;

  |-->for(j = 0; j < MAX_NR_ZONES; j++)

  |--{

  |     struct zone *zone = pgdat->node_zones + j;

  |     unsigned long present_pages = zone->present_pages;

  |     zone->lowmem_reserve[j] = 0;

  |     idx = j;

  |     while(idx)

  |     {

  |        struct zone *lower_zone;

  |        idx--;

  |        if(sysctl_lowmem_reserve_ratio[idx] < 1)

  |            sysctl_lowmem_reserve_ratio = 1;

  |      

  |        lower_zone = pgdat->node_zones + idx;

  |        lower_zone->lowmem_reserve[j] = present_pages /

  |              sysctl_lowmem_reserve_ratio[idx];

  |        present_pages += lower_zone->present_pages;

  |     }

  |--}

  |

  |-->calculate_totalreserve_pages();

 

 

void setup_per_zone_inactive_ratio(void)

  |-->for_each_zone(zone)

      |-->calculate_zone_inactive_ratio(zone);


void calculate_zone_inactive_ratio(struct zone *zone)

  |-->unsigned int gb, ratio;

  |

  |-->gb = zone->present_pages >> (30 - PAGE_SHIFT);

  |

  |-->if(gb) = int_sqrt(10 * gb);

  |   else ratio = 1;

  |

  |-->zone->inactive_ratio = ratio;

 

 

 

void setup_per_zone_wmarks(void)

  |-->unsigned long pages_min = min_free_kbytes >> (PAGE_SHITF - 10);

  |   获取min_free_kbytes所对应的页数. 

  |

  |-->unsigned long lowmem_pages = 0;

  |   struct zone *zone = NULL; 

  |   unsigned long flags = 0;

  |

  |-->for_each_zone(zone)

  |--{

  |     if(!is_highmem(zone)) lowmem_pages += zone->present_pages;

  |     如注释所言,获取非ZONE_HIGHMEM的zone中的present_pages总和.

  |--}

  |

  |--for_each_zone(zone)

  |--{

  |     u64 tmp;

  |     tmp = (u64)pages_min * zone->present_pages;

  |     do_div(tmp, lowmem_pages); //tmp = tmp / lowmem_pages

  |     对于以上两句,以通常的数学意义容易理解:

  |     pages_min * (zone->present_pages / lowmem_pages);

  |     if(is_highmem(zone))

  |    {  

  |      对于ZONE_HIGMEM做特殊处理. 

  |      int min_pages = zone->present_pages / 1024; 

  |      if(min_pages < SWAP_CLUSTER_MAX)

  |         min_pages = SWAP_CLUSTER_MAX;

  |      if(min_pages > 128)

  |         min_pages = 128;  

  |      zone->watermark[WMARK_MIN] = min_pages;

  |    }  

  |    else    

  |       zone->watermark[WMARK_MIN] = tmp;

  |

  |    zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + (tmp >> 2);

  |    zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + (tmp >> 1);

  |    setup_zone_migrate_reserve(zone);

  |--}

  |

  |--calculate_totalreserve_pages();

  |

 

 

void setup_zone_migrate_reserve(struct zone *zone)

  |-->unsigned long start_pfn, pfn, end_pfn;

  |   struct page *page;

  |   unsigned long block_migratetype;

  |   int reserve;

  |

  |-->start_pfn = zone->zone_start_pfn;

  |   zone->zone_start_pfn是该zone中起始的那个页在物理内存中位置.

  |   end_pfn = start_pfn + zone->spanned_pages;

  |

  |-->reserve = roundup(min_wmark_pages(zone), pageblock_nr_pages)

  |             >> pageblock_order;

  |   reserve = min(2, reserve);

  |   以pageblock_order的整数被内存作为MIGRATE_RESERVE大小.

  |

  |-->for(pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages)

  |--{

  |      page = pfn_to_page(pfn);

  |      if(PageRserved(page)) continue;

  |      block_migratetype = get_pageblock_migratetype(page);

  |

  |      if(reserve > 0 && block_migratetype == MIGRATE_RESERVE)

  |      {

  |         初始化过程中,到现在为止,我们只用到了MIGRATE_UNMOVABLE,

  |         MIGRATE_MOVABLE,只所以要检查,因为该函数可能通过proc

  |         被调用.

  |         reserve--; continue;

  |      }

  |

  |      if(reserve > 0 && block_migratetype == MIGRATE_MOVABLE)

  |      {

  |         set_pageblock_migratetype(page, MIGRATE_RESERVE);

  |         move_freepages_block(zone, page, MIGRATE_RESERVE);

  |         从MIGRATE_MOVABLE上迁移页块到MIGRATE_RESERVE链表上.

  |         reserve--;

  |         continue;

  |      }
  |
  | if(block_migratetype = MIGRATE_RESERVE)
 
| {
 | set_pageblock_migratetype(page, MIGRATE_MOVABLE); | move_freepages_block(zone, page, MIGRATE_MOVABLE); | } |--}

 

 

int zone_batchsize(struct zone *zone)

  |-->int batch;

  |

  |-->batch = zone->present_pages / 1024;

  |   if(batch * PAGE_SIZE > 512 * 1024)

  |      batch = (512 * 1024) / PAGE_SIZE;

  |   batch /= 4;

  |   if(batch < 1) batch = 1;

  |   batch = rounddown_pow_of_two(batch + batch/2) - 1;

  |   return batch;

 

 

void calculate_totalreserve_pages(void)
 
|-->struct pglist_data *pgdat = NULL; | unsigned long reserve_pages = 0; | enum zone_type i, j; | |-->考虑UMA |--pgdat = contig_page_data; |--for(i = 0; i < MAX_NR_ZONES; i++) |--{ | struct zone *zone = pgdat->node_zones + i; | unsigned long max = 0; | for(j = i; j < MAX_NR_ZONES; j++) | { | if(zone->lowmem_reserve[j] > max) | max = zone->lowmem_reserve[j]; | 在初始化过程中,setup_per_lowmem_reserve是后于setup_per_zone_wmarks | 执行的,因此,首次执行calculate_totoalreseve_pages时,
  | zone->lowmem_reserve[]为0,执行setup_per_lowmem_reserve
  | 后zone->lowmem_reserve[]被修正.
  |        注意,在setup_per_lowmem_reserve中也会执行该函数.
| } | | max += high_wmark_pages(zone); | | if(max > zone->present_pages) | max = zone->present_pages; | | reserve_pages += max; |--} | |-->totoalreserve_pages = reserve_pages;

 

已是十月份,毕设+论文……后阶段,内容会更新地较慢,更多地从机制上阐述内核,关于具体的策略实现不会再如此详细。

你可能感兴趣的:(init)