ARM32 虚拟地址设定:
内核的虚拟地址只有 1G空间,对于需要访问4G 范围地址是不够的,因此对于内核而言,kmalloc, vmalloc 建立ZONE_DMA, ZONE_NORMAL 的线性映射,而对于剩下的部分,内核需要时 remap 到ZONE_HIGHMEM, 用完释放掉以便其它模块复用,从而利用4G 空间。
用户空间,则实际只访问了3G 空间,它映射到 ZONE_HIGNMEM 物理地址空间。
ARM64 的虚拟地址足够大,通常使用 48bit 来描述地址范围
用户地址:0x0000 0000 0000 0000 0000 0000 0000 0000 ~ 0x0000 7fff ffff ffff ffff ffff ffff ffff
内核地址:0xffff 8000 0000 0000 0000 0000 0000 0000 ~ 0xffff ffff ffff ffff ffff ffff ffff ffff
如果地址落在上面两个范围之外,则说明地址异常。
对于一个进程,其虚拟地址的 vma ,如 stack, heap, data, bin 等都是在 load elf binary 过程中加载实现的,对于 stack, heap, bss 这些是通过 map anonymous memory 建立 vma, 而对于 data,text 则主要是 map file 实现的。
Linux 对于每个线程,它用 task_struct 来进行描述,对于除了 Kthread的线程之外,都有一个 mm_struct, 它描述进程的虚拟内存空间,对于虚拟地址中的每个部分,都使用一个结构体 struct vm_area_struct 来描述。
在进程中, vm_area_struct 彼此之间通过双向链表关联起来,同时在 mm_struct 中 mm_rb用来快速查找一个 vma.
- static void dump_task_vma(struct task_struct *tsk)
- {
- struct mm_struct *mm = tsk->mm;
- struct vm_area_struct *vma;
- unsigned long flags;
- char perm[BUFLEN] = {0};
- int count;
-
- for (vma = mm->mmap; vma; vma = vma->vm_next) {
-
- }
- void *mmap(void *addr, size_t length, int prot, int flags,
- int fd, off_t offset);
-
- int munmap(void *addr, size_t length);
建立函数的映射和解除映射。
对于映射,其参数解释:
对于文件映射,其基本流程是:
对于mmap 本身而言,read/write 是需要用户自己进行并发控制的。
建立映射基本要点:
Pagefault 映射
这里仅从宏观上分析vma 的page建立方式, 关于pgfault 具体细节分析,我们下次继续。