电子说
64位Linux一般使用48位表示虚拟地址空间,43位表示物理地址,通过命令:
cat /proc/cpuinfo
ARM64架构处理器采用48位物理寻址机制,最大可寻找256TB的物理地址空间。对于 目前应用完全足够,不需要扩展到64位的物理寻址。虚拟地址也同样最大支持48位寻址,所以 在处理器架构设计上,把虚拟地址空间划分为两个空间,每个空间最大支持256TB,linux内核 在大多数体系结构上都把两个地址划分为:用户空间和内核空间。
用户空间:0x0000_0000_0000_0000至0x0000_ffff_ffff_ffff;
内核空间:0xffff_0000_0000_0000至0xffff_ffff_ffff_ffff;
QEMU平台,可以打印ARM64架构linux内核内存分布情况
堆是进程中主要用于动态分配变量和数据的内存区域,堆的管理对应程序员不是直接可见的。因为它依赖标准库提供的各个辅助函数(其中最重要的是malloc)来分配任意长度的内存区。malloc和内核之间的经典接口是brk系统调用,负责扩展/收缩堆。
检查资源限制;
将brk值对齐到页;
是否想增加brk值?(这个地方要结合源码看)
是-->do_brk()
;返回新的brk的值;
否-->do_munmap()
;返回新的brk的值;
brk机制不是一个独立的内核概念,而是基于匿名映射实现,以减少内部的开销。在检查过用brk的值的新地址未超出推的限制之后,
sys_brk
第一个重要操作是请求的地址按页长对齐。brk()
用于进程向内核申请空间,用于扩展用户堆栈空间,或者回收堆栈空间。
brk()
为大块空间申请。do_brk()
用于增长动态分配区。do_munmap()
释放动态分配区;do_brk()
源码分析:
static unsigned long do_brk(unsigned long addr, unsigned long len)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma, *prev;
unsigned long flags;
struct rb_node **rb_link, *rb_parent;
pgoff_t pgoff = addr >> PAGE_SHIFT;
int error;
// 首先对len这个长度进行页面对齐去判断页面对齐之后是否超出边界
len = PAGE_ALIGN(len);
if (!len)
return addr;
flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
// 检查是否有足够内存空间来分析len大小的内存。判断虚拟地址空间是否足够
error = get_unmapped_area(NULL, addr, len, 0, MAP_FIXED);
if (offset_in_page(error))
return error;
error = mlock_future_check(mm, mm->def_flags, len);
if (error)
return error;
/*
* mm->mmap_sem is required to protect against another thread
* changing the mappings in case we sleep.
*/
verify_mm_writelocked(mm);
/*
* Clear old maps. this also does some error checking for us
*/
// 循环遍历用户进程红黑树中VMA,然后根据addr来查找合适的插入点
while (find_vma_links(mm, addr, addr + len, &prev, &rb_link,
&rb_parent)) {
if (do_munmap(mm, addr, len))
return -ENOMEM;
}
/* Check against address space limits *after* clearing old maps... */
// 检查是否要对此虚拟区间进行扩充
if (!may_expand_vm(mm, len >> PAGE_SHIFT))
return -ENOMEM;
if (mm->map_count > sysctl_max_map_count)
return -ENOMEM;
// 判断系统是否有足够内存
if (security_vm_enough_memory_mm(mm, len >> PAGE_SHIFT))
return -ENOMEM;
/* Can we just expand an old private anonymous mapping? */
// 判读是否可以合并,如果可以合并就合并成为一个vam区
vma = vma_merge(mm, prev, addr, addr + len, flags,
NULL, NULL, pgoff, NULL, NULL_VM_UFFD_CTX);
// 如果能合并直接goto out
if (vma)
goto out;
/*
* create a vma struct for an anonymous mapping
*/
//如果没有办法合并,只有新创建一个VMA,VMA地址空间是【addr,addr+len】
vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
if (!vma) {
vm_unacct_memory(len >> PAGE_SHIFT);
return -ENOMEM;
}
// 指向匿名域指针
INIT_LIST_HEAD(&vma->anon_vma_chain);
vma->vm_mm = mm; // 指向VMA所属于进程struct mm_struct结构
vma->vm_start = addr;
vma->vm_end = addr + len;
vma->vm_pgoff = pgoff;
vma->vm_flags = flags;
vma->vm_page_prot = vm_get_page_prot(flags);
vma_link(mm, vma, prev, rb_link, rb_parent);
out: // 增加进程地址空间长度
perf_event_mmap(vma);
mm->total_vm += len >> PAGE_SHIFT;
if (flags & VM_LOCKED)
mm->locked_vm += (len >> PAGE_SHIFT);
vma->vm_flags |= VM_SOFTDIRTY;
return addr;
}
- END -
审核编辑 :李倩
全部0条评论
快来发表一下你的评论吧 !