總結了高端內存區的固定內核映射區、臨時內核映射與永久內核映射。但是對於高端內存中各個區間的布置我們任然不是很清楚,首先我們從整體上看看內核對高端內存的劃分情況。
如果內存足夠大(比如用戶:內核線性空間=3:1,內核就只能訪問線性空間的第4GB內容,如果物理內存超過1GB則視為足夠大),內核線性空間無法同時映射所有內存。這就需要將內核線性空間分出一段不直接映射物理內存,而是作為窗口分時映射使用到的未映射的內存。
相關閱讀:
http://www.linuxidc.com/Linux/2012-02/53457.htm
http://www.linuxidc.com/Linux/2012-02/53458.htm
http://www.linuxidc.com/Linux/2012-02/53459.htm
一、非連續內存區布局
Linux內核中對於非連續區間的開始:
[cpp]
- #define VMALLOC_START ((unsigned long)high_memory + VMALLOC_OFFSET)
[cpp]
- #define VMALLOC_OFFSET (8 * 1024 * 1024)
對於變量high_memory變量:
[cpp]
- void __init initmem_init(unsigned long start_pfn,
- unsigned long end_pfn)
- {
- highstart_pfn = highend_pfn = max_pfn;
- if (max_pfn > max_low_pfn)
- highstart_pfn = max_low_pfn;
- ……
- num_physpages = highend_pfn;
- /*高端內存開始地址物理*/
- high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1;
- ……
- }
其中,變量max_low_pfn在highmem_pfn_init()函數中初始化為下面值
[cpp]
- #define MAXMEM (VMALLOC_END - PAGE_OFFSET - __VMALLOC_RESERVE)
[cpp]
- <p>unsigned int __VMALLOC_RESERVE = 128 << 20;</p>
對於非連續區間的結束定義:
[cpp]
- # define VMALLOC_END (PKMAP_BASE - 2 * PAGE_SIZE)
由上面的內核代碼,畫出內存布局細節圖如下
由上面的布局可知128M+4M+4M+8K,然而直接映射區和連續內存之間空出來了8M的空間不能用,非連續空間和永久內核映射區之間也有8K的空間不可用,另外,內存頂端空出了4K不可用的。這樣,高端內存能用的空間為128M+4M+4M+8K-4K-8M-8K=128M-4K大小的內存。
二、數據結構描述
虛擬內存區描述(對於vmlist鏈表)
[cpp]
- struct vm_struct {
- struct vm_struct *next;
- void *addr;/*內存區的第一個內存單元的線性地址*/
- unsigned long size;
- unsigned long flags;/*類型*/
- struct page **pages;/*指向nr_pages數組的指針,該數組由指向頁描述符的指針組成*/
- unsigned int nr_pages;/*內存區填充的頁的個數*/
- unsigned long phys_addr;/*該字段設為0,除非內存已被創建來映射一個硬件設備的IO共享內存*/
- void *caller;
- };
虛擬內存區描述(對於紅黑樹)
[html]
- struct vmap_area {
- unsigned long va_start;
- unsigned long va_end;
- unsigned long flags;
- struct rb_node rb_node; /* address sorted rbtree */
- struct list_head list; /* address sorted list */
- struct list_head purge_list; /* "lazy purge" list */
- void *private;
- struct rcu_head rcu_head;
- };
內存區由next字段鏈接到一起,並且為了查找簡單,他們以地址為次序。為了防止溢出,每個區域至少由一個頁面隔離開。
三、非連續內存區初始化
非連續內存區的初始化工作在start_kernel()->mm_init()->vmalloc_init()完成
[cpp]
- void __init vmalloc_init(void)
- {
- struct vmap_area *va;
- struct vm_struct *tmp;
- int i;
-
- for_each_possible_cpu(i) {
- struct vmap_block_queue *vbq;
-
- vbq = &per_cpu(vmap_block_queue, i);
- spin_lock_init(&vbq->lock);
- INIT_LIST_HEAD(&vbq->free);
- INIT_LIST_HEAD(&vbq->dirty);
- vbq->nr_dirty = 0;
- }
-
- /* Import existing vmlist entries. */
- for (tmp = vmlist; tmp; tmp = tmp->next) {/*導入vmlist中已經有的數據到紅黑樹中*/
- va = kzalloc(sizeof(struct vmap_area), GFP_NOWAIT);
- va->flags = tmp->flags | VM_VM_AREA;
- va->va_start = (unsigned long)tmp->addr;
- va->va_end = va->va_start + tmp->size;
- __insert_vmap_area(va);
- }
-
- vmap_area_pcpu_hole = VMALLOC_END;
-
- vmap_initialized = true;/*已經初始化*/
- }