添砖加瓦

5.2.6 添砖加瓦

回到setup_arch,来到1007,调用paging_init()进行页面初始化。

 

825void __init paging_init(void)

 826{

 827        pagetable_init();

 828

 829        __flush_tlb_all();

 830

 831        kmap_init();

 832

 833        /*

 834         * NOTE: at this point the bootmem allocator is fully available.

 835         */

 836        sparse_init();

 837        zone_sizes_init();

 838}

 

我们看到,paging_init()分别调用下面的几个函数来实现页面的初始化工作:

pagetable_init();

kmap_init();

sparse_init();

zone_sizes_init();

 

首先pagetable_init()初始化页全局目录的目录项:

 

544 static void __init pagetable_init(void)

545 {

546        pgd_t *pgd_base = swapper_pg_dir;

547

548        permanent_kmaps_init(pgd_base);

549 }

 

如果CONFIG_HIGHMEM被配置(当前大多数系统肯定都是大内存,都会有配置),则调用permanent_kmaps_init函数,把页全局目录swapper_pg_dir地址传给他。该函数调用page_table_range_init设置目录项。随后paging_init的两个函数kmap_init() sparse_init()分别对第一个页表项进行缓存,以及映射非线性的段(也就是不连续的段)。至于高端内存的映射,请查看博客“高端内存映射相关内容。

 

paging_init的最后调用zone_sizes_init()

 

739static void __init zone_sizes_init(void)

 740{

 741        unsigned long max_zone_pfns[MAX_NR_ZONES];

 742        memset(max_zone_pfns, 0, sizeof(max_zone_pfns));

 743        max_zone_pfns[ZONE_DMA] =

 744                virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT;

 745        max_zone_pfns[ZONE_NORMAL] = max_low_pfn;

 746#ifdef CONFIG_HIGHMEM

 747        max_zone_pfns[ZONE_HIGHMEM] = highend_pfn;

 748#endif

 749

 750        free_area_init_nodes(max_zone_pfns);

 751}

 

zone_sizes_init741~749行设置一个内部变量max_zone_pfns数组其只有三个元素:ZONE_DMAZONE_NORMALZONE_HIGHMEM,表示我们NUMA体系中一个NODE的三个Zone。那么元素的具体值就是每个Zone的理论上能使用的页框数最大值。

 

最后750行调用free_area_init_nodes()来实现节点空闲区域的初始化,这函数很复杂,其本质上就等于:

void __init free_area_init_nodes(unsigned long *max_zone_pfn)

{

for_each_online_node(nid) {

              pg_data_t *pgdat = NODE_DATA(nid);

       free_area_init_node(nid, NULL,

                     find_min_pfn_for_node(nid), NULL);

 

       /* Any memory on that node */

       if (pgdat->node_present_pages)

              node_set_state(nid, N_HIGH_MEMORY);

       check_for_regular_memory(pgdat);

}

}

 

for_each_online_node是什么呢?它定义为:

#define for_each_online_node(node) for_each_node_state(node, N_ONLINE)

#define for_each_node_state(__node, __state) /

       for_each_node_mask((__node), node_states[__state])

#define for_each_node_mask(node, mask)                 /

       for ((node) = first_node(mask);                  /

              (node) < MAX_NUMNODES;                   /

              (node) = next_node((node), (mask)))

 

first_nodenext_node又分别是:

#define first_node(src) __first_node(&(src))

static inline int __first_node(const nodemask_t *srcp)

{

       return min_t(int, MAX_NUMNODES, find_first_bit(srcp->bits, MAX_NUMNODES));

}

#define next_node(n, src) __next_node((n), &(src))

static inline int __next_node(int n, const nodemask_t *srcp)

{

       return min_t(int,MAX_NUMNODES,find_next_bit(srcp->bits, MAX_NUMNODES, n+1));

}

 

好了,由于我们假设只有一个节点,所以这也是一个只执行一次的循环。这里有个奇怪的NODE_DATA(nid),这个nid我们知道,是0。那么NODE_DATA宏是什么?来看它的定义:

extern struct pglist_data *node_data[];

#define NODE_DATA(nid)   (node_data[nid])

 

来了,最著名的pglist_data结构出现了:

600typedef struct pglist_data {

 601        struct zone node_zones[MAX_NR_ZONES];

 602        struct zonelist node_zonelists[MAX_ZONELISTS];

 603        int nr_zones;

 604#ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */

 605        struct page *node_mem_map;

 606#ifdef CONFIG_CGROUP_MEM_RES_CTLR

 607        struct page_cgroup *node_page_cgroup;

 608#endif

 609#endif

 610#ifndef CONFIG_NO_BOOTMEM

 611        struct bootmem_data *bdata;

 612#endif

 613#ifdef CONFIG_MEMORY_HOTPLUG

 614        /*

 615         * Must be held any time you expect node_start_pfn, node_present_pages

 616         * or node_spanned_pages stay constant.  Holding this will also

 617         * guarantee that any pfn_valid() stays that way.

 618         *

 619         * Nests above zone->lock and zone->size_seqlock.

 620         */

 621        spinlock_t node_size_lock;

 622#endif

 623        unsigned long node_start_pfn;

 624        unsigned long node_present_pages; /* total number of physical pages */

 625        unsigned long node_spanned_pages; /* total size of physical page

 626                                             range, including holes */

 627        int node_id;

 628        wait_queue_head_t kswapd_wait;

 629        struct task_struct *kswapd;

 630        int kswapd_max_order;

 631} pg_data_t;

 

这个结构是每个NUMA节点一个,那么我PC上唯一的0NODE就是NODE_DATA(0),也就是全局数组node_data[]0号元素。

 

再来看接下来的free_area_init_node函数,来自mm/page_alloc.c

 

3932void __paginginit free_area_init_node(int nid, unsigned long *zones_size,

3933                unsigned long node_start_pfn, unsigned long *zholes_size)

3934{

3935        pg_data_t *pgdat = NODE_DATA(nid);

3936

3937        pgdat->node_id = nid;

3938        pgdat->node_start_pfn = node_start_pfn;

3939        calculate_node_totalpages(pgdat, zones_size, zholes_size);

3940

3941        alloc_node_mem_map(pgdat);

3942#ifdef CONFIG_FLAT_NODE_MEM_MAP

3943        printk(KERN_DEBUG "free_area_init_node: node %d, pgdat %08lx, node_mem_map %08lx/n",

3944                nid, (unsigned long)pgdat,

3945                (unsigned long)pgdat->node_mem_map);

3946#endif

3947

3948        free_area_init_core(pgdat, zones_size, zholes_size);

3949}

 

传递给他的参数作为整个pg_data_t的第一个页号,由函数find_min_pfn_for_node(0)计算出:

static unsigned long __init find_min_pfn_for_node(int nid)

{

       int i;

       unsigned long min_pfn = ULONG_MAX;

 

       /* Assuming a sorted map, the first range found has the starting pfn */

       for_each_active_range_index_in_nid(i, nid)

              min_pfn = min(min_pfn, early_node_map[i].start_pfn);

 

       if (min_pfn == ULONG_MAX) {

              printk(KERN_WARNING

                     "Could not find start_pfn for node %d/n", nid);

              return 0;

       }

 

       return min_pfn;

}

其实很简单,就是位于early_node_map[]数组中第一个可用的页号(ULONG_MAX321),即第一个空闲页面的页号。注意这里,最后返回的其实是early_node_map[]数组中,start_pfn最小的那个元素的值。

 

3935行的pgdat我们已经知道了,随后初始化它的两个字段:NUMA节点号和第一个Zone的第一个页框号。接着3939行计算pgdat的所有Zone的总共页面数和可用页面数,针对我们的x86体系MAX_NR_ZONES等于3

 

3699static void __meminit calculate_node_totalpages(struct pglist_data *pgdat,

3700                unsigned long *zones_size, unsigned long *zholes_size)

3701{

3702        unsigned long realtotalpages, totalpages = 0;

3703        enum zone_type i;

3704

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

3706                totalpages += zone_spanned_pages_in_node(pgdat->node_id, i,

3707                                                                zones_size);

3708        pgdat->node_spanned_pages = totalpages;

3709

3710        realtotalpages = totalpages;

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

3712                realtotalpages -=

3713                        zone_absent_pages_in_node(pgdat->node_id, i,

3714                                                                zholes_size);

3715        pgdat->node_present_pages = realtotalpages;

3716        printk(KERN_DEBUG "On node %d totalpages: %lu/n", pgdat->node_id,

3717                                                        realtotalpages);

3718}

 

然后free_area_init_node3941行,调用alloc_node_mem_map()为我们的NODE(0)的所有页描述符page分配空闲区域:

 

3891static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)

3892{

3893        /* Skip empty nodes */

3894        if (!pgdat->node_spanned_pages)

3895                return;

3896

3897#ifdef CONFIG_FLAT_NODE_MEM_MAP

3898        /* ia64 gets its own node_mem_map, before this, without bootmem */

3899        if (!pgdat->node_mem_map) {

3900                unsigned long size, start, end;

3901                struct page *map;

3902

3903                /*

3904                 * The zone's endpoints aren't required to be MAX_ORDER

3905                 * aligned but the node_mem_map endpoints must be in order

3906                 * for the buddy allocator to function correctly.

3907                 */

3908                start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1);

3909                end = pgdat->node_start_pfn + pgdat->node_spanned_pages;

3910                end = ALIGN(end, MAX_ORDER_NR_PAGES);

3911                size =  (end - start) * sizeof(struct page);

3912                map = alloc_remap(pgdat->node_id, size);

3913                if (!map)

3914                        map = alloc_bootmem_node(pgdat, size);

3915                pgdat->node_mem_map = map + (pgdat->node_start_pfn - start);

3916        }

3917#ifndef CONFIG_NEED_MULTIPLE_NODES

3918        /*

3919         * With no DISCONTIG, the global mem_map is just set as node 0's

3920         */

3921        if (pgdat == NODE_DATA(0)) {

3922                mem_map = NODE_DATA(0)->node_mem_map;

3923#ifdef CONFIG_ARCH_POPULATES_NODE_MAP

3924                if (page_to_pfn(mem_map) != pgdat->node_start_pfn)

3925                        mem_map -= (pgdat->node_start_pfn - ARCH_PFN_OFFSET);

3926#endif /* CONFIG_ARCH_POPULATES_NODE_MAP */

3927        }

3928#endif

3929#endif /* CONFIG_FLAT_NODE_MEM_MAP */

3930}

 

看到3897行,我在.config中找了找,没有看到CONFIG_FLAT_NODE_MEM_MAP配置选项,估计是最新的内核已经不把FLATMEM作为默认内存模型了。既然提到了内存模型(Memory Model),就多给大家补充一下这方面的知识。什么是内存模型,针对x86体系就是对页面的管理算法及其相关的数据结构。Linux目前支持三种内存管理模型:FLATMEM(平坦模型)、DISCONTIGMEM(折扣模型)和SPARSEMEM(稀疏模型)。不知道从哪个Linux版本以后,就把SPARSEMEM作为默认的内存模型了。

 

FLATMEM模型将所有的页描述符,全都放到全局数组mem_map中:

struct page *mem_map;

mem_map的每一个元素就是一个page结构,那么当我要获得一个页面的信息,就把对应页面的页号pfn加上首地址mem_map就能得到对应的页描述符结构page

 

DISCONTIGMEMSPARSEMEM模型则要复杂得多,牵涉到一个vmemmap全局变量和mem_section数据结构。笔者对这两个模型没有深入的研究,所以不敢妄言。下面就假设我们配置了CONFIG_FLAT_NODE_MEM_MAP,针对FLATMEM模型来对余下的代码进行讲解。

 

3899pgdat->node_mem_map还没有初始化,肯定是空的,所以进入条件分支。设置内部变量startendsize分别为本节点pglist_data的第一个可用页面页号node_start_pfn,最后一个可用页面页号,以及总的page大小。3912行首先尝试一下alloc_remap分配,由于我们没有配置CONFIG_HAVE_ARCH_ALLOC_REMAP,所以肯定会失败,那么肯定会调用alloc_bootmem_node函数进行分配。这个函数最终会走到__alloc_bootmem_node函数:

 

833void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size,

 834                                   unsigned long align, unsigned long goal)

 835{

 836        if (WARN_ON_ONCE(slab_is_available()))

 837                return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);

 838

 839#ifdef CONFIG_NO_BOOTMEM

 840        return __alloc_memory_core_early(pgdat->node_id, size, align,

 841                                         goal, -1ULL);

 842#else

 843        return ___alloc_bootmem_node(pgdat->bdata, size, align, goal, 0);

 844#endif

 845}

 

836行,WARN_ON_ONCE(slab_is_available()),内有只要有万分之一可能性都要去尝试一下,比如这里有可能一个计算集群中的其他节点先于本地节点初始化了一个slab分配器,就去尝试使用它来分配。

 

我们配置了CONFIG_NO_BOOTMEM,所以调用__alloc_memory_core_early函数:

 

3412void * __init __alloc_memory_core_early(int nid, u64 size, u64 align,

3413                                        u64 goal, u64 limit)

3414{

3415        int i;

3416        void *ptr;

3417

3418        /* need to go over early_node_map to find out good range for node */

3419        for_each_active_range_index_in_nid(i, nid) {

3420                u64 addr;

3421                u64 ei_start, ei_last;

3422

3423                ei_last = early_node_map[i].end_pfn;

3424                ei_last <<= PAGE_SHIFT;

3425                ei_start = early_node_map[i].start_pfn;

3426                ei_start <<= PAGE_SHIFT;

3427                addr = find_early_area(ei_start, ei_last,

3428                                         goal, limit, size, align);

3429

3430                if (addr == -1ULL)

3431                        continue;

3432

3433#if 0

3434                printk(KERN_DEBUG "alloc (nid=%d %llx - %llx) (%llx - %llx) %llx %llx => %llx/n",

3435                                nid,

3436                                ei_start, ei_last, goal, limit, size,

3437                                align, addr);

3438#endif

3439

3440                ptr = phys_to_virt(addr);

3441                memset(ptr, 0, size);

3442                reserve_early_without_check(addr, addr + size, "BOOTMEM");

3443                return ptr;

3444        }

3445

3446        return NULL;

3447}

 

for_each_active_range_index_in_nid(i, nid)我们见过了,遍历所有early_node_map数组的元素。3427行,调用最低级的函数find_early_areaearly_node_map[]数组的某个元素中找到一个可用的空间,来存放mem_map数组,对这个函数还有一些疑惑的同学可以在“着手建立永久内核页表”一节中找到答案。

 

最后通过pgdat->node_mem_map = map + (pgdat->node_start_pfn - start)pglist_datanode_mem_map字段指向这个size大小的page区域。

 

存放所有页描述符page的空间有了,回到free_area_init_node函数中。3948行,调用free_area_init_core,传递给他的参数是本节点的pglist_data、为NULLzones_sizezholes_size

 

3793/*

3794 * Set up the zone data structures:

3795 *   - mark all pages reserved

3796 *   - mark all memory queues empty

3797 *   - clear the memory bitmaps

3798 */

3799static void __paginginit free_area_init_core(struct pglist_data *pgdat,

3800                unsigned long *zones_size, unsigned long *zholes_size)

3801{

3802        enum zone_type j;

3803        int nid = pgdat->node_id;

3804        unsigned long zone_start_pfn = pgdat->node_start_pfn;

3805        int ret;

3806

3807        pgdat_resize_init(pgdat);

3808        pgdat->nr_zones = 0;

3809        init_waitqueue_head(&pgdat->kswapd_wait);

3810        pgdat->kswapd_max_order = 0;

3811        pgdat_page_cgroup_init(pgdat);

3812       

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

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

3815                unsigned long size, realsize, memmap_pages;

3816                enum lru_list l;

3817

3818                size = zone_spanned_pages_in_node(nid, j, zones_size);

3819                realsize = size - zone_absent_pages_in_node(nid, j,

3820                                                                zholes_size);

3821

3822                /*

3823                 * Adjust realsize so that it accounts for how much memory

3824                 * is used by this zone for memmap. This affects the watermark

3825                 * and per-cpu initialisations

3826                 */

3827                memmap_pages =

3828                        PAGE_ALIGN(size * sizeof(struct page)) >> PAGE_SHIFT;

3829                if (realsize >= memmap_pages) {

3830                        realsize -= memmap_pages;

3831                        if (memmap_pages)

3832                                printk(KERN_DEBUG

3833                                       "  %s zone: %lu pages used for memmap/n",

3834                                       zone_names[j], memmap_pages);

3835                } else

3836                        printk(KERN_WARNING

3837                                "  %s zone: %lu pages exceeds realsize %lu/n",

3838                                zone_names[j], memmap_pages, realsize);

3839

3840                /* Account for reserved pages */

3841                if (j == 0 && realsize > dma_reserve) {

3842                        realsize -= dma_reserve;

3843                        printk(KERN_DEBUG "  %s zone: %lu pages reserved/n",

3844                                        zone_names[0], dma_reserve);

3845                }

3846

3847                if (!is_highmem_idx(j))

3848                        nr_kernel_pages += realsize;

3849                nr_all_pages += realsize;

3850

3851                zone->spanned_pages = size;

3852                zone->present_pages = realsize;

3853#ifdef CONFIG_NUMA

3854                zone->node = nid;

3855                zone->min_unmapped_pages = (realsize*sysctl_min_unmapped_ratio)

3856                                                / 100;

3857                zone->min_slab_pages = (realsize * sysctl_min_slab_ratio) / 100;

3858#endif

3859                zone->name = zone_names[j];

3860                spin_lock_init(&zone->lock);

3861                spin_lock_init(&zone->lru_lock);

3862                zone_seqlock_init(zone);

3863                zone->zone_pgdat = pgdat;

3864

3865                zone->prev_priority = DEF_PRIORITY;

3866

3867                zone_pcp_init(zone);

3868                for_each_lru(l) {

3869                        INIT_LIST_HEAD(&zone->lru[l].list);

3870                        zone->reclaim_stat.nr_saved_scan[l] = 0;

3871                }

3872                zone->reclaim_stat.recent_rotated[0] = 0;

3873                zone->reclaim_stat.recent_rotated[1] = 0;

3874                zone->reclaim_stat.recent_scanned[0] = 0;

3875                zone->reclaim_stat.recent_scanned[1] = 0;

3876                zap_zone_vm_stats(zone);

3877                zone->flags = 0;

3878                if (!size)

3879                        continue;

3880

3881                set_pageblock_order(pageblock_default_order());

3882                setup_usemap(pgdat, zone, size);

3883                ret = init_currently_empty_zone(zone, zone_start_pfn,

3884                                                size, MEMMAP_EARLY);

3885                BUG_ON(ret);

3886                memmap_init(size, nid, j, zone_start_pfn);

3887                zone_start_pfn += size;

3888        }

3889}

 

3807行,调用pgdat_resize_init函数初始化pglist_data的自旋锁:

void pgdat_resize_init(struct pglist_data *pgdat)

{

       spin_lock_init(&pgdat->node_size_lock);

}

 

3809行,初始化pglist_datakswapd_wait字段,作为等待队列的头。有关等待队列的知识如果还欠缺的话,请访问博客“非运行状态进程的组织

 

3813,进入一个循环,循环次数MAX_NR_ZONES,我们知道是3。每个NODEZone信息都存放在pglist_datanode_zones数组中,所以3814首先获得每个元素的地址,赋给内部变量zone。随后zone_spanned_pages_in_node为根据传递进来的zholes_size去掉一些page。由于我们传递进来的是NULL,所以这个函数啥也不做,得到的size实际上就是页框page的总量,即early_node_map[i].end_pfn减去early_node_map[i].start_pfn的结果:

void __meminit get_pfn_range_for_nid(unsigned int nid,

                     unsigned long *start_pfn, unsigned long *end_pfn)

{

       int i;

       *start_pfn = -1UL;

       *end_pfn = 0;

 

       for_each_active_range_index_in_nid(i, nid) {

              *start_pfn = min(*start_pfn, early_node_map[i].start_pfn);

              *end_pfn = max(*end_pfn, early_node_map[i].end_pfn);

       }

 

       if (*start_pfn == -1UL)

              *start_pfn = 0;

}

 

继续走,3819zone_absent_pages_in_node也是个空函数,所以realsizesize的值相等。3827行,计算出所有页框的大小:PAGE_ALIGN(size * sizeof(struct page)) >> PAGE_SHIFT。结果左移PAGE_SHIFT位的意思我们很熟悉了,就是计算出要装下size个页描述符需要几个实际的页框,最后把这个数赋值给内部变量memmap_pages。这个逻辑关系千万要注意!

 

继续,2829 - 3838其实是个出错信息的打印,因为如果计算出来的memmap_pages比现在已经有的realsize还大,那我PC内存可就太小了,可以退休了。3841 - 3849,让realsize去掉一些保留的页面并增加一些内核使用的页面数量,最后将计算出的总的页框数量size和实际使用的页框数量realsize赋值给zonespanned_pages字段和present_pages

 

后面3853 - 3885初始化每个pgdat->node_zones数组的其他字段,最后3886行调用memmap_init(size, nid, j, zone_start_pfn)初始化所有的页描述符:

#ifndef __HAVE_ARCH_MEMMAP_INIT

#define memmap_init(size, nid, zone, start_pfn) /

       memmap_init_zone((size), (nid), (zone), (start_pfn), MEMMAP_EARLY)

#endif

 

.config文件中找不到__HAVE_ARCH_MEMMAP_INIT,所以内核调用memmap_init_zone()初始化所有的页描述符,下面是具体的初始化,传入的参数是总页框数size,节点号nid(为0),zone号(0~2),以及第一个可用页面的页号zone_start_pfn

 

2995void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,

2996                unsigned long start_pfn, enum memmap_context context)

2997{

2998        struct page *page;

2999        unsigned long end_pfn = start_pfn + size;

3000        unsigned long pfn;

3001        struct zone *z;

3002

3003        if (highest_memmap_pfn < end_pfn - 1)

3004                highest_memmap_pfn = end_pfn - 1;

3005

3006        z = &NODE_DATA(nid)->node_zones[zone];

3007        for (pfn = start_pfn; pfn < end_pfn; pfn++) {

3008                /*

3009                 * There can be holes in boot-time mem_map[]s

3010                 * handed to this function.  They do not

3011                 * exist on hotplugged memory.

3012                 */

3013                if (context == MEMMAP_EARLY) {

3014                        if (!early_pfn_valid(pfn))

3015                                continue;

3016                        if (!early_pfn_in_nid(pfn, nid))

3017                                continue;

3018                }

3019                page = pfn_to_page(pfn);

3020                set_page_links(page, zone, nid, pfn);

3021                mminit_verify_page_links(page, zone, nid, pfn);

3022                init_page_count(page);

3023                reset_page_mapcount(page);

3024                SetPageReserved(page);

3025                /*

3026                 * Mark the block movable so that blocks are reserved for

3027                 * movable at startup. This will force kernel allocations

3028                 * to reserve their blocks rather than leaking throughout

3029                 * the address space during boot when many long-lived

3030                 * kernel allocations are made. Later some blocks near

3031                 * the start are marked MIGRATE_RESERVE by

3032                 * setup_zone_migrate_reserve()

3033                 *

3034                 * bitmap is created for zone's valid pfn range. but memmap

3035                 * can be created for invalid pages (for alignment)

3036                 * check here not to call set_pageblock_migratetype() against

3037                 * pfn out of zone.

3038                 */

3039                if ((z->zone_start_pfn <= pfn)

3040                    && (pfn < z->zone_start_pfn + z->spanned_pages)

3041                    && !(pfn & (pageblock_nr_pages - 1)))

3042                        set_pageblock_migratetype(page, MIGRATE_MOVABLE);

3043

3044                INIT_LIST_HEAD(&page->lru);

3045#ifdef WANT_PAGE_VIRTUAL

3046                /* The shift won't overflow because ZONE_NORMAL is below 4G. */

3047                if (!is_highmem_idx(zone))

3048                        set_page_address(page, __va(pfn << PAGE_SHIFT));

3049#endif

3050        }

3051}

 

前面alloc_node_mem_map为我们的NODE(0)的所有页描述符page分配空闲区域,并由全局变量mem_map指向,所以3007for (pfn = start_pfn; pfn < end_pfn; pfn++) 对每个空闲页框,都指向一系列初始化的操作,其中3019行通过宏pfn_to_page(pfn) 得到页框号对应的页描述符page的地址,并把它赋给内部变量page

#define __pfn_to_page(pfn)  (mem_map + ((pfn) - ARCH_PFN_OFFSET))

后面的代码就是对这个页描述符进行一系列的初始化。

 

到此为止,setup_arch()中比较重要的内容基本介绍完毕,主要涉及内存管理系统的一些底层初始化代码。经过这一番初始化后,内存初始化已经完成了特定系统的基本初始化,接下来,start_kernel开始构建内存管理的经典算法应用:伙伴系统和slab内存管理。对这两个概念不太熟悉的同学请查阅博客“伙伴系统算法和“slab分配器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/** * * @description 获取每日一言并发送到消息。如果是定时任务则推送到指定群组。需配合任务计划使用。版本号:v1..2。作者:猫咪。来源:傻妞官方。 * @author 佚名 * @version v1.0.0 * @create_at 2022-09-08 07:40:14 * @title 每日一言 * @public false * @rule ?一言 * @rule 一言 * @rule 一言 ? * @cron 15 7 * * * * @description 每日一言,随机获取一条每日一言,用于给群组或好友“添砖加瓦”,增进彼此之间的情感。 * @icon https://bpic.51yuansu.com/pic3/cover/01/90/73/598262544f307_610.jpg */ const s = sender // 获取每日一言 const { hitokoto, from } = JSON.parse(request({ url: 'https://v1.hitokoto.cn', method: 'get', headers: { 'User-Agent': 'Mozilla/5.' } }).body) // 组装每日一言消息 const content = `${hitokoto} ——${from}` // 获取早安图片 var { body } = request({ url: "https://api.2xb.cn/zaob", method: "get", dataType: "json", }) // 回复每日一言和早安图片到消息 s.reply(`${content}\n${image(body.imageUrl)}`) // 判断是否定时任务 var isCron = s.getPlatform() == "cron" if (isCron) { // 如果是定时任务,则推送到指定群组 (new SillyGirl).push({ platform: "wx", groupCode:24079622488, content: `${content}\n${image(body.imageUrl)}` }), (new SillyGirl).push({ platform: "wx", groupCode:44584783019, content: `${content}\n${image(body.imageUrl)}` }), (new SillyGirl).push({ platform: "wx", groupCode:43584492904, content: `${content}\n${image(body.imageUrl)}` }), (new SillyGirl).push({ platform: "wx", groupCode:47517350193, content: `${content}\n${image(body.imageUrl)}` }) }
最新发布
04-23

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值