start_kernel()之page_address_init()函数

该函数初始化高端内存(High Memory)线性地址空间中永久映射相关的全局变量。所以在不支持高端内存即在没有配置CONFIG_HIGHMEM这个宏的时候,该函数是个空函数什么也不做,在ARM系统中,是没有高端内存相关的代码的,所以这个函数就是个空函数。

在支持高端内存的体系结构中,page_address_init()函数初始化高端内存页表池的链表 struct list_head page_address_pool变量,将内存页地址映射表 struct page_address_map page_address_maps[LIST_PKMAP]中的每一个页地址映射结构 page_address_maps[n]都通过其list成员加入到链表page_address_pool中。初始化内存地址槽结构数组static struct page_address_slot page_address_htable[1<<PA_HASH_ORDER(=7)]中的每一个结构变量的链表1h和自旋锁lock,最后初始化高端内存池全局自旋锁pool_lock。

其代码如下:

void __init page_address_init(void)
{
__int i;
 
__INIT_LIST_HEAD(&page_address_pool);
__for (i = 0; i < ARRAY_SIZE(page_address_maps); i++)
____list_add(&page_address_maps[i].list, &page_address_pool);
__for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) {
____INIT_LIST_HEAD(&page_address_htable[i].lh);
____spin_lock_init(&page_address_htable[i].lock);
__}
__spin_lock_init(&pool_lock);
}

page_address_pool的定义如下:

static struct list_head page_address_pool;

就是一个静态的双向全局链表,相当与一个全局变量。struct list_head的结构如下:

struct list_head {
__struct list_head *next, *prev;
};

第一句代码INIT_LIST_HEAD就是将page_address_pool的前向指针和后续指针都指向了自己。防止指向NULL。

然后下面是一个for循环,循环的次数就是数组page_address_maps中元素的个数,page_address_maps的定义如下:

static struct page_address_map page_address_maps[LAST_PKMAP];

struct page_address_map定义如下:

/*
 * Describes one page->virtual association
 */
struct page_address_map {
__struct page *page;
__void *virtual;
__struct list_head list;
};

其中 struct page也是一个页的结构内容,其定义如下:

struct page {
__page_flags_t flags;___/* Atomic flags, some possibly
__________ * updated asynchronously */
__atomic_t _count;____/* Usage count, see below. */
__atomic_t _mapcount;___/* Count of ptes mapped in mms,
__________ * to show when page is mapped
__________ * & limit reverse map searches.
__________ */
__unsigned long private;____/* Mapping-private opaque data:
__________ * usually used for buffer_heads
__________ * if PagePrivate set; used for
__________ * swp_entry_t if PageSwapCache
__________ */
__struct address_space *mapping;__/* If low bit clear, points to
__________ * inode address_space, or NULL.
__________ * If page mapped as anonymous
__________ * memory, low bit is set, and
__________ * it points to anon_vma object:
__________ * see PAGE_MAPPING_ANON below.
__________ */
__pgoff_t index;______/* Our offset within mapping. */
__struct list_head lru;___/* Pageout list, eg. active_list
__________ * protected by zone->lru_lock !
__________ */
__/*
__ * On machines where all RAM is mapped into kernel address space,
__ * we can simply calculate the virtual address. On machines with
__ * highmem some memory is mapped into kernel virtual memory
__ * dynamically, so we need a place to store that address.
__ * Note that this field could be 16 bits on x86 ... ;)
__ *
__ * Architectures with slow multiplication can define
__ * WANT_PAGE_VIRTUAL in asm/page.h
__ */
#if defined(WANT_PAGE_VIRTUAL)
__void *virtual;______/* Kernel virtual address (NULL if
__________   not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
};

上面的变量*mapping 的结构定义如下:

struct address_space {
__struct inode____*host;____/* owner: inode, block_device */
__struct radix_tree_root__page_tree;__/* radix tree of all pages */
__spinlock_t____tree_lock;__/* and spinlock protecting it */
__unsigned int____i_mmap_writable;/* count VM_SHARED mappings */
__struct prio_tree_root_i_mmap;___/* tree of private and shared mappings */
__struct list_head__i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
__spinlock_t____i_mmap_lock;__/* protect tree, count, list */
__atomic_t____truncate_count;_/* Cover race condition with truncate */
__unsigned long___nrpages;__/* number of total pages */
__pgoff_t_____writeback_index;/* writeback starts here */
__struct address_space_operations *a_ops;_/* methods */
__unsigned long___flags;____/* error bits/gfp mask */
__struct backing_dev_info *backing_dev_info; /* device readahead, etc */
__spinlock_t____private_lock;_/* for use by the address_space */
__struct list_head__private_list;_/* ditto */
__struct address_space__*assoc_mapping;_/* ditto */
} __attribute__((aligned(sizeof(long))));

相信到这里大家肯定都觉得有点懵,不知道结构中的每个变量都是干嘛用的,其实在这里我也不是非常的了解,所以就不一一介绍每一个变量了,只要知道数组page_address_maps中的每一个变量存放的内容是什么就好了,后续我们讲解内存管理的时候,再详细的介绍每一个变量的具体用途。现在还是回到代码的主线for循环中。

for (i = 0; i < ARRAY_SIZE(page_address_maps); i++)
____list_add(&page_address_maps[i].list, &page_address_pool);

ARRAY_SIZE就是获取数组的长度,有多少个元素。其定义如下:

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

这个for循环就是将数组page_address_maps中的每一个元素的成员.list添加到全局链表page_address_pool这个全局链表中。

再看代码下面的for循环,

for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) {

____INIT_LIST_HEAD(&page_address_htable[i].lh);

____spin_lock_init(&page_address_htable[i].lock);

__}

page_address_htable也是一个全局变量数组,数组中的每一个元素的格式就是page_address_slot这个结构的格式。

其代码的定义如下:

/*
 * Hash table bucket
 */
static struct page_address_slot {
__struct list_head lh;______/* List of page_address_maps */
__spinlock_t lock;______/* Protect this bucket's list */
} ____cacheline_aligned_in_smp page_address_htable[1<<PA_HASH_ORDER];

这里__cacheline_aligned_in_smp的定义如下:

#ifndef ____cacheline_aligned_in_smp
#ifdef CONFIG_SMP
#define ____cacheline_aligned_in_smp ____cacheline_aligned
#else
#define ____cacheline_aligned_in_smp
#endif /* CONFIG_SMP */
#endif

被它修改的代码段表示在编译的时候地址采用cache访问最快的对其方式来编译,具体多少个字节对齐才是cache访问最快的方式在具体的CPU架构中不一致,cache的设计也不同,因此在不同的架构中其对齐的字节书并不完全相同。在ARM32中cache line的大小就是32位,因此读写内存都采用的是一个cache line的长度,因此要求4字节对齐,而在ARM64中,则采用16字节对齐。

现在回到上面的for循环中,这个for循环的主要作用就是依次初始化page_address_htable[i]元素中的自旋锁变量和链表,最后将zheng个内存池的大自旋锁初始化。这样这个函数就运行结束,主要做了内存池相关的初始化工作。

猜你喜欢

转载自blog.csdn.net/Lq19880521/article/details/83572803
今日推荐