首页 > 系统相关 >进程空间管理:用户态和内核态

进程空间管理:用户态和内核态

时间:2023-11-02 23:33:00浏览次数:27  
标签:映射 vm 用户 start brk 内核 进程 内存

用户态虚拟空间里面有几类数据,例如代码、全局变量、堆、栈、内存映射区等。在 struct mm_struct 里面,有下面这些变量定义了这些区域的统计信息和位置。

unsigned long mmap_base;  /* base of mmap area */
unsigned long total_vm;    /* Total pages mapped */
unsigned long locked_vm;  /* Pages that have PG_mlocked set */
unsigned long pinned_vm;  /* Refcount permanently increased */
unsigned long data_vm;    /* VM_WRITE & ~VM_SHARED & ~VM_STACK */
unsigned long exec_vm;    /* VM_EXEC & ~VM_WRITE & ~VM_STACK */
unsigned long stack_vm;    /* VM_STACK */
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
unsigned long arg_start, arg_end, env_start, env_end;

其中,total_vm 是总共映射的页的数目。我们知道,这么大的虚拟地址空间,不可能都有真实内存对应,所以这里是映射的数目。当内存吃紧的时候,有些页可以换出到硬盘上,有的页因为比较重要,不能换出。locked_vm 就是被锁定不能换出,pinned_vm 是不能换出,也不能移动。

data_vm 是存放数据的页的数目,exec_vm 是存放可执行文件的页的数目,stack_vm 是栈所占的页的数目。

start_code 和 end_code 表示可执行代码的开始和结束位置,start_data 和 end_data 表示已初始化数据的开始位置和结束位置。

start_brk 是堆的起始位置,brk 是堆当前的结束位置。前面咱们讲过 malloc 申请一小块内存的话,就是通过改变 brk 位置实现的。

start_stack 是栈的起始位置,栈的结束位置在寄存器的栈顶指针中。

arg_start 和 arg_end 是参数列表的位置, env_start 和 env_end 是环境变量的位置。它们都位于栈中最高地址的地方。

mmap_base 表示虚拟地址空间中用于内存映射的起始地址。一般情况下,这个空间是从高地址到低地址增长的。前面咱们讲 malloc 申请一大块内存的时候,就是通过 mmap 在这里映射一块区域到物理内存。咱们加载动态链接库 so 文件,也是在这个区域里面,映射一块区域到 so 文件。

这下所有用户态的区域的位置基本上都描述清楚了。整个布局就像下面这张图这样。虽然 32 位和 64 位的空间相差很大,但是区域的类别和布局是相似的。

进程空间管理:用户态和内核态_linux

堆是从低地址向高地址增长的,sys_brk 函数的参数 brk 是新的堆顶位置,而当前的 mm->brk 是原来堆顶的位置。

首先要做的第一个事情,将原来的堆顶和现在的堆顶,都按照页对齐地址,然后比较大小。如果两者相同,说明这次增加的堆的量很小,还在一个页里面,不需要另行分配页,直接跳到 set_brk 那里,设置 mm->brk 为新的 brk 就可以了。

如果发现新旧堆顶不在一个页里面,麻烦了,这下要跨页了。如果发现新堆顶小于旧堆顶,这说明不是新分配内存了,而是释放内存了,释放的还不小,至少释放了一页,于是调用 do_munmap 将这一页的内存映射去掉。

如果堆将要扩大,就要调用 find_vma。如果打开这个函数,看到的是对红黑树的查找,找到的是原堆顶所在的 vm_area_struct 的下一个 vm_area_struct,看当前的堆顶和下一个 vm_area_struct 之间还能不能分配一个完整的页。如果不能,没办法只好直接退出返回,内存空间都被占满了。

如果还有空间,就调用 do_brk 进一步分配堆空间,从旧堆顶开始,分配计算出的新旧堆顶之间的页数。

内核态的虚拟空间和某一个进程没有关系,所有进程通过系统调用进入到内核之后,看到的虚拟地址空间都是一样的。

在内核态,32 位和 64 位的布局差别比较大,主要是因为 32 位内核态空间太小了。32 位的内核态虚拟地址空间一共就 1G,占绝大部分的前 896M,我们称为直接映射区。

进程空间管理:用户态和内核态_linux_02

所谓的直接映射区,就是这一块空间是连续的,和物理内存是非常简单的映射关系,其实就是虚拟内存地址减去 3G,就得到物理内存的位置。

  • __pa(vaddr) 返回与虚拟地址 vaddr 相关的物理地址;
  • __va(paddr) 则计算出对应于物理地址 paddr 的虚拟地址。

其实 64 位的内核布局反而简单,因为虚拟空间实在是太大了,根本不需要所谓的高端内存,因为内核是 128T,根本不可能有物理内存超过这个值。

进程空间管理:用户态和内核态_linux_03

64 位的内核主要包含以下几个部分。从 0xffff800000000000 开始就是内核的部分,只不过一开始有 8T 的空档区域。

从 __PAGE_OFFSET_BASE(0xffff880000000000) 开始的 64T 的虚拟地址空间是直接映射区域,也就是减去 PAGE_OFFSET 就是物理地址。虚拟地址和物理地址之间的映射在大部分情况下还是会通过建立页表的方式进行映射。

从 VMALLOC_START(0xffffc90000000000)开始到 VMALLOC_END(0xffffe90000000000)的 32T 的空间是给 vmalloc 的。从 VMEMMAP_START(0xffffea0000000000)开始的 1T 空间用于存放物理页面的描述结构 struct page 的。

从 __START_KERNEL_map(0xffffffff80000000)开始的 512M 用于存放内核代码段、全局变量、BSS 等。这里对应到物理内存开始的位置,减去 __START_KERNEL_map 就能得到物理内存的地址。这里和直接映射区有点像,但是不矛盾,因为直接映射区之前有 8T 的空当区域,早就过了内核代码在物理内存中加载的位置。

进程运行状态在 32 位下对应关系。

进程空间管理:用户态和内核态_linux_04

对于 64 位的对应关系,只是稍有区别。

进程空间管理:用户态和内核态_linux_05

标签:映射,vm,用户,start,brk,内核,进程,内存
From: https://blog.51cto.com/key3feng/8155435

相关文章

  • 操作系统实验——进程管理的算法实现
    前言笔者在大学下属的事业单位上班,最近去帮着带下操作系统的实验课,这里随手水点参考代码,欢迎各位领导老师莅临指正实验目标编写一个简单的进程调度器实验内容进程控制块(PCB)的定义与管理进程调度算法的实现进程创建、销毁和切换给定一批进程对比3-4种调度算法的时间(自选算......
  • 【专题】智能汽车致力打造用户 “移动智能空间”报告PDF合集分享(附原数据表)
    原文链接:https://tecdat.cn/?p=34111随着新一轮技术革命和产业变革的推动,以及国家政策的大力扶持,电动化、智能化、网联化已经成为汽车行业发展的新趋势。在这种背景下,各大企业纷纷争夺数字化人才,以推动产品的规模化落地和商业化创新应用。阅读原文,获取专题报告合集全文,解锁文末53......
  • 深入理解Cookie与Session:实现用户跟踪和数据存储
    1.会话跟踪技术介绍会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。一次会话中可以包含多次请求和响应.HTTP协议是无状态协议,每次同一浏览器向服务器请求时,服务器都会将该请求视为新的请求,因此我们需要会话跟踪技术来实现同一会话内数据共享思考:下图......
  • 手动签发token(多方式登录:用户名、手机号、邮箱)
    要求登陆数据格式#使用用户名,手机号,邮箱,都可以登录##前端需要传的数据格式{ 用户名/手机号/邮箱"username":"lqz/1332323223/[email protected]","password":"lqz12345"}序列化类中ser.pyfromrest_frameworkimportserializersimportrefromrest_framework.exce......
  • openGauss学习笔记-112 openGauss 数据库管理-管理用户及权限-行级访问控制
    openGauss学习笔记-112openGauss数据库管理-管理用户及权限-行级访问控制行级访问控制特性将数据库访问控制精确到数据表行级别,使数据库达到行级访问控制的能力。不同用户执行相同的SQL查询操作,读取到的结果是不同的。用户可以在数据表创建行访问控制(RowLevelSecurity)策略,该......
  • sql server 截断和收缩所有用户数据库日志--用游标循环所有正常状态的用户数据库
    在服务器运维活动中,我们经常需要做一项工具就是将好多年的用户数据库日志文件截断并收缩为最小,以节省大量的磁盘空间。当数据库只有一两个时可以手动操作,但数据库数量众多时,就需要采用sql脚本,批量化执行这个过程。本人写了一段这样的脚本。并且经过验证执行无误。现在分享出来,您......
  • Ubuntu 一直卡在开机界面或者用户登录界面死循环问题的解决
    此方法并不全部通用,根据自己实际情况建议提前快照再试试此方法原因:NVIDIA驱动所致,之前安装方式nvidia驱动出问题。解决办法:卸载nvidia驱动,重新安装。(1)进入文本模式:CTRL+ALT+F1或者进入高级选项(按住esc或shift)(2)Uninstallanypreviousdrivers:sudoapt-getremovenvidia......
  • JUC并发编程学习笔记(一)认知进程和线程
    进程和线程进程一个程序,如QQ.exe,是程序的集合一个进程往往可以包含多个线程,至少包含一个java默认有两个线程,GC垃圾回收线程和Main线程线程:一个进程中的各个功能java无法真正的开启线程,因为java是运行在虚拟机上的,所以只能通过C++,通过native本地方法调用C++开启线程priva......
  • 数据分析和用户个性化体验:开发同城外卖APP的技术解决方案
    在数字化时代,外卖服务已经成为人们日常生活中不可或缺的一部分。无论是忙碌的白领、学生,还是家庭主妇,都倚赖同城外卖APP来方便地满足他们的饥饿需求。然而,随着竞争的激烈,外卖APP必须不断改进,以满足用户的期望,其中数据分析和用户个性化体验变得至关重要。一、数据分析的重要性1.1了......
  • 终于有人把进程与线程讲清楚了
    前言很多人对进程、线程没有什么概念,面试的时候也说不出其中的核心内涵。所以,今天我打算花点篇幅把进程和线程讲清楚。01CPU与内存**CPU**大家都知道是计算机的中央运算单元,用来计算的。CPU从内存里面读取一条一条的代码指令,然后根据指令来执行运算(加,减,乘,除,复制数据等)。......