爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      Linux内核设计与实现(13)--进程地址空间

      首页 知识中心 存储 文章详情页

      Linux内核设计与实现(13)--进程地址空间

      2023-06-16 06:12:13 阅读次数:109

      Linux,内存,内核

      上一节讲了内核如何管理物理内存,其实内核除了管理本身的内存外,还必须管理用户空间中进程的内存,这就是进程地址空间,也就是系统中每个用户空间进程所看到的内存。

      Linux采用虚拟内存技术,系统中所有进程之间以虚拟方式共享内存,对一个进程而言,可以访问整个系统的所有物理内存,其拥有地址空间也可以远远大于系统物理内存。

      1.地址空间

      进程地址空间由进程可寻址的虚拟内存组成。每个进程都有一个32bit或64bit的平坦地址空间,空间具体大小取决于体系结构。平坦(flat)指的是地址空间范围是一个独立的连续空间(比如,地址从0~2​32-1).一些操作系统提供了段地址空间,这种地址空间并不是一个独立的线性区域,而是被分段的。现代采用虚拟内存的操作系统都使用平坦地址空间。

      每个进程都有唯一的这种平坦地址空间,并且不同进程之间,彼此互不相干,地址空间完全独立。

      尽管一个进程可以寻址4GB虚拟内存(32bit),但这并不代表它就有权访问所有虚拟内存,在地址空间中,更关心的是一些可以合法访问的虚拟内存的地址空间,这个空间称为“内存区域(memory areas)”,进程只能访问有效内存区域内的内存地址,每个内存区域也具有相关权限,如可读、可写,可执行属性。如果一个进程访问了不在有效范围的内存区域,或以不正确的方式访问了有效地址,内核就会终止该进程,并返回“段错误”。

      内存区域可以包含各种内存对象,比如:

      代码段(text section):可执行文件代码的内存映射

      数据段(data section): 可执行文件的已初始化全局变量的内存映射

      bss段:包含未初始化全局变量,也就是bss段的零页 (页面中的信息全部为0,可用于映射bss段等目的) 的内存映射

      栈:用户进程用户空间的栈(不要和进程内核栈混淆,进程的内核栈独立存在并且由内核维护)每个诸如C库或动态链接程序等共享库的代码段、数据段和BSS也会被载入进程的地址空间。

      任何内存映射文件;

      任何共享内存段;

      任何匿名的内存映射,比如malloc()分配的内存;

      进程地址空间中的任何有效地址都只能位于唯一的区域,这些内存区域不能相互覆盖;在执行的进程中,每个不同的内存片段都对应一个独立的内存区域:栈、对象代码、全局变量、被映射的文件等。

      2.内存描述符

      内核使用内存描述符来表示进程的地址空间,该结构体包含了和进程地址空间有关的官不信息,mm_struct结构体,定义在

      此处)折叠或打开

      1. struct mm_struct {
      2. struct vm_area_struct * mmap; /* 内存区域链表list of VMAs */
      3. struct rb_root mm_rb; /* VMA形成的红黑树*/
      4. struct vm_area_struct * mmap_cache; /* 最近使用的内存区域last find_vma result */
      5. #ifdef CONFIG_MMU
      6. unsigned long (*get_unmapped_area) (struct file *filp,
      7. unsigned long addr, unsigned long len,
      8. unsigned long pgoff, unsigned long flags);
      9. void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
      10. #endif
      11. unsigned long mmap_base; /* base of mmap area */
      12. unsigned long task_size; /* size of task vm space */
      13. unsigned long cached_hole_size; /* if non-zero, the largest hole below free_area_cache */
      14. unsigned long free_area_cache; /* 地址空间第一个空洞first hole of size cached_hole_size or larger */
      15. pgd_t * pgd; /*页全局目录*/
      16. atomic_t mm_users; /* 使用地址空间的用户数How many users with user space? */
      17. atomic_t mm_count; /* 主使用计数器How many references to "struct mm_struct" (users count as 1) */
      18. int map_count; /* number of VMAs */
      19. struct rw_semaphore mmap_sem; /*内存区域的信号量*/
      20. spinlock_t page_table_lock; /* 页表锁Protects page tables and some counters */
      21.
      22. struct list_head mmlist; /*所有mm_struct形成的双向链表 List of maybe swapped mm's. These are globally strung
      23. * together off init_mm.mmlist, and are protected
      24. * by mmlist_lock
      25. */
      26.
      27.
      28. unsigned long hiwater_rss; /* High-watermark of RSS usage */
      29. unsigned long hiwater_vm; /* High-water virtual memory usage */
      30.
      31. /*全部页面数目,上锁的页面数目*/
      32. unsigned long total_vm, locked_vm, shared_vm, exec_vm;
      33. unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
      34. /*代码段的开始地址,结束地址;数据段的首地址和结束地址*/
      35. unsigned long start_code, end_code, start_data, end_data;
      36. /*堆的首地址,堆的尾地址*/
      37. unsigned long start_brk, brk, start_stack;
      38. /*命令行参数的首地址和结束地址,环境变量的首地址和结束地址*/
      39. unsigned long arg_start, arg_end, env_start, env_end;
      40.
      41. unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
      42.
      43. /*
      44. * Special counters, in some configurations protected by the
      45. * page_table_lock, in other configurations by being atomic.
      46. */
      47. struct mm_rss_stat rss_stat;
      48.
      49. struct linux_binfmt *binfmt;
      50.
      51. cpumask_t cpu_vm_mask;
      52.
      53. /* Architecture-specific MM context */
      54. mm_context_t context;
      55.
      56. /* Swap token stuff */
      57. /*
      58. * Last value of global fault stamp as seen by this process.
      59. * In other words, this value gives an indication of how long
      60. * it has been since this task got the token.
      61. * Look at mm/thrash.c
      62. */
      63. unsigned int faultstamp;
      64. unsigned int token_priority;
      65. unsigned int last_interval;
      66.
      67. unsigned long flags; /* Must use atomic bitops to access the bits */
      68.
      69. struct core_state *core_state; /* coredumping support */
      70. #ifdef CONFIG_AIO
      71. spinlock_t ioctx_lock;
      72. struct hlist_head ioctx_list;
      73. #endif
      74. #ifdef CONFIG_MM_OWNER
      75. /*
      76. * "owner" points to a task that is regarded as the canonical
      77. * user/owner of this mm. All of the following must be true in
      78. * order for it to be changed:
      79. *
      80. * current == mm->owner
      81. * current->mm != mm
      82. * new_owner->mm == mm
      83. * new_owner->alloc_lock is held
      84. */
      85. struct task_struct *owner;
      86. #endif
      87.
      88. #ifdef CONFIG_PROC_FS
      89. /* store ref to file /proc/<pid>/exe symlink points to */
      90. struct file *exe_file;
      91. unsigned long num_exe_file_vmas;
      92. #endif
      93. #ifdef CONFIG_MMU_NOTIFIER
      94. struct mmu_notifier_mm *mmu_notifier_mm;
      95. #endif
      96. };

       

      mm_users域记录正在使用该地址的进程数目,mm_count表示mm_struct结构体的主引用计数,当mm_users值减少为0时(所有使用该地址空间的线程都退出),mm_count变为0;当mm_count等于0,说明已经咩有人和指向该mm_stuct结构体的引用了,这时该结构体会被撤销。

      mmap和mm_rb描述同一个对象:该地址空间中的全部内存区域。Mmap以链表形式存放,mm_rb以红-黑树形式存放。内核通常会避免用两种数据结构组织同一种数据,但此处这种冗余派的上用场,mmap链表,利于简单、高效地遍历所有元素;而mm_rb结构更适合搜索指定的元素。覆盖树上的链表并用这两个结构体同时访问相同的数据集,有时候这种操作称为线索树。

      所有mm_stuct都通过自身的mmlist域链接在一个双向链表中,该链表首元素是init_mm内存描述符,它代表init进程的地址空间,另外注意,操作该链表是需要使用mmlist_lock来防止并发访问。

      2.1 分配内存描述符

      在进程的进程描述符task_struct中,mm域存放着该进程使用的内存描述符,所以current->mm指向当前进程的内存描述符。

      fork()函数利用copy_mm()函数复制父进程的内存描述符,而子进程中的mm_struct结构体实际是通过文件kernel/fork.c中的allocate_mm()宏从mm_cachep slab缓存中分配得到的。通常每个进程都有唯一的mm_struct结构体,即唯一的进程地址空间。

      如果父进程希望和子进程共享地址空间,可以调用clone()时,设置CLONE_VM标志,这样的进程称作线程,Linux中所谓的线程和进程的本质区别,就是是否共享地址空间。

      当CLONE_VM被指定后,内核就不再需要调用allocate_mm()函数,而仅仅需要在调用copy_mm()函数中将mm域指向其父进程的内存描述符就可以了:

      此处)折叠或打开

      1. if (clone_flags & CLONE_VM) {
      2. //current 是父进程,而tsk在fork()指向期间是子进程
      3. atomic_inc(¤t->mm->mm_users);
      4. tsk->mm = current->mm;
      5. }

       

      2.2 撤销内存描述符

      进程退出时,内核会调用exit_mm(),该函数执行一些常规撤销工作,同时更新一些统计量。

      该函数会调用mmput()减少内存描述符中的mm_users用户基数,如果用户计数降到0,将调用mmdrop()函数,减少mm_count使用计数。如果使用计数也等于零,说明内存描述符不再有任何使用者了,那么调用free_mm()宏通过kmem_cache_free()将mm_struct结构体归还到mm_cachep slab缓存中。

      2.3 mm_struct与内核线程

      内核线程没有进程地址空间,也没有相关的内存描述符,所以内核线程对应的进程描述符mm域为空,事实上,这也正是内核线程的真实含义—它们没有用户上下文。

      为了避免内核线程为内存描述符和页表浪费内存,也为了当新内核线程运行时,避免浪费处理器周期向新地址空间进行切换,内核线程将直接使用前一个进程的内存描述符。

      当一个进程被调度时,该进程的mm域指向的地址空间被载入内存,进程描述符中的active_mm域会被更新,指向新的地址空间。内核线程没有地址空间,mm域为NULL,于是,当一个内核线程被调度时,内核发现它的mm域为NULL,就会保留前一个进程的地址空间,随后更新内核线程的进程描述符的active_mm域,使其指向前一个进程的内存描述符。所以需要时,内核线程便可以使用前一个进程的页表。

      3.虚拟内存区域

      内存区域由vm_area_struct结构体描述,定义在 中,内存区域在Linux内核中经常称作虚拟内存区域(virtual memoryAreas, VMAs).

      vm_area_struct描述了指定地址空间内连续区间上的一个独立内存范围,内核将每个内存区域作为一个单独的内存对象管理,每个内存区域都拥有一致的属性,比如访问权限等,相应的操作也应该一致。按这种方式,每个VMA就可以代表不同类型的内存区域(比如内存映射文件或者进程用户空间栈)。

       

      此处)折叠或打开

      1. /*
      2. * This struct defines a memory VMM memory area. There is one of these
      3. * per VM-area/task. A VM area is any part of the process virtual memory
      4. * space that has a special rule for the page-fault handlers (ie a shared
      5. * library, the executable area etc).
      6. */
      7. struct vm_area_struct {
      8. struct mm_struct * vm_mm; /* 相关的mm_struct结构体The address space we belong to. */
      9. unsigned long vm_start; /* 区间首地址Our start address within vm_mm. */
      10. unsigned long vm_end; /* 区间尾地址The first byte after our end address
      11. within vm_mm. */
      12.
      13. /* linked list of VM areas per task, sorted by address */
      14. struct vm_area_struct *vm_next, *vm_prev; //VMA链表
      15.
      16. pgprot_t vm_page_prot; /* 访问控制权限Access permissions of this VMA. */
      17. unsigned long vm_flags; /* 标志:内存区域标志的信息和行为Flags, see mm.h. */
      18. struct rb_node vm_rb; //树上该VMA的节点
      19.
      20. /*
      21. * For areas with an address space and backing store,
      22. * linkage into the address_space->i_mmap prio tree, or
      23. * linkage to the list of like vmas hanging off its node, or
      24. * linkage of vma in the address_space->i_mmap_nonlinear list.
      25. */
      26. union {
      27. struct {
      28. struct list_head list;
      29. void *parent; /* aligns with prio_tree_node parent */
      30. struct vm_area_struct *head;
      31. } vm_set;
      32.
      33. struct raw_prio_tree_node prio_tree_node;
      34. } shared;
      35.
      36. /*
      37. * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
      38. * list, after a COW of one of the file pages. A MAP_SHARED vma
      39. * can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack
      40. * or brk vma (with NULL file) can only be in an anon_vma list.
      41. */
      42. struct list_head anon_vma_chain; /* Serialized by mmap_sem &
      43. * page_table_lock */
      44. struct anon_vma *anon_vma; /* Serialized by page_table_lock */
      45.
      46. /* Function pointers to deal with this struct. */
      47. const struct vm_operations_struct *vm_ops; //相关的操作链表
      48.
      49. /* Information about our backing store: */
      50. unsigned long vm_pgoff; /*文件中偏移量 Offset (within vm_file) in PAGE_SIZE
      51. units, *not* PAGE_CACHE_SIZE */
      52. struct file * vm_file; /* 被映射的文件File we map to (can be NULL). */
      53. void * vm_private_data; /* 私有数据was vm_pte (shared mem) */
      54. unsigned long vm_truncate_count;/* truncate_count or restart_addr */
      55.
      56. #ifndef CONFIG_MMU
      57. struct vm_region *vm_region; /* NOMMU mapping region */
      58. #endif
      59. #ifdef CONFIG_NUMA
      60. struct mempolicy *vm_policy; /* NUMA policy for the VMA */
      61. #endif
      62. };

       

      每个内存描述符都对应于进程地址空间中的唯一区间,vm_end-vm_start大小就是内存区间的长度。在同一地址空间内的不同内存区间不能重叠。

      vm_mm域指向和VMA相关的mm_struct结构体,每个VMA对其相关的mm_struct结构体来说都是唯一的,如果两个线程共享一个地址空间,那么它们也同时共享其中所有的vm_area_struct结构体。

      3.1VMA标志

      VMA是一种位标志,它包含在vm_flags域内,标志了内存区域所包含的页面行为和信息。VMA标识反映了内核处理页面所需遵守的行为准则,而不是硬件要求。vm_flags包含了内存区域中每个页面的信息或内存区域的整体信息,而不是具体的独立页面。

      几个重要的标志(这些标志可以按需求组合):

      VM_READ,VM_WRITE和VM_EXEC标志了区域中页面的读、写和执行权限。

      VM_SHARD:指明内存区域包含的映射是否可以在多进程间共享,

      VM_IO:标志内存区域中按对设备I/O空间的映射,该标志通常在设备驱动程序执行mmap()函数进行I/O空间映射时才被设置。

      VM_SEQ_READ:标志内核应用程序对映射内容执行有序的(线性和连续的)读操作,这样内核可以有选择地执行预读文件。

      3.2 VMA操作

      Vm_area_struct结构体中的vm_ops域指向与指定内存区域相关的操作函数

      此处)折叠或打开

       

      1. struct vm_operations_struct {
      2. //当指定的内存区域被加入到一个地址空间时,该函数被调用
      3. void (*open)(struct vm_area_struct * area);
      4. //当指定的内存区域从地址空间删除时,该函数调用
      5. void (*close)(struct vm_area_struct * area);
      6. //当没有出现在屋里内存中的页面被访问时,该函数被页面故障处理调用
      7. int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf);
      8.
      9. /* notification that a previously read-only page is about to become
      10. * writable, if an error is returned it will cause a SIGBUS */
      11. //当某个页面为只读页面时,该函数被页面故障处理调用
      12. int (*page_mkwrite)(struct vm_area_struct *vma, struct vm_fault *vmf);
      13.
      14. /* called by access_process_vm when get_user_pages() fails, typically
      15. * for use by special VMAs that can switch between memory and hardware
      16. */
      17. //当get_user_pages()函数调用失败时,该函数被access_process_vm()函数调用
      18. int (*access)(struct vm_area_struct *vma, unsigned long addr,
      19. void *buf, int len, int write);
      20. …
      21. };

       

      3.3 内存区域的树型结构和内存区域的链表结构

      mmap和mm_rb,独立地指向与内存描述符相关的全体内存区域对象,它们包含完全相同的vm_area_struct结构体指针,仅仅方法不同。

      mmap域使用单独的链表链接所有的内存区域对象,每个vm_area_struct结构体通过自身vm_next域被连入链表,mmap域指向链表中的一个内存区域,链中最后一个结构体指针指向空

      mm_rb域使用红-黑树链接所有内存区域对象,mm_rb指向红-黑树根节点,地址空间中每个vm_area_struct通过自身的vm_rb连接到树中。

      链表用于需要遍历全部节点的时候,而红黑树适用于在地址空间中定位特定内存区域的时候,内核为了内存区域上的各种不同操作都能获得高性能,所以同时使用了这两种数据结构。

      3.4 实际使用中的内存区域

      可使用/proc文件系统和pmap工具查看给定进程的内存空间和其中所含的内存区域。

       

      此处)折叠或打开

       

      1. #include <stdio.h>
      2. int main(void)
      3. {
      4. printf("hello,world!\r\n");
      5. while (1);
      6. return 0;
      7. }

       

      该程序执行pid是2874,那么

      leon@ubuntu:~$ cat /proc/2874/maps

      08048000-08049000 r-xp 00000000 08:01 131477     /home/leon/a.out

      08049000-0804a000 r--p 00000000 08:01 131477     /home/leon/a.out

      0804a000-0804b000 rw-p 00001000 08:01 131477     /home/leon/a.out

      b7589000-b758a000 rw-p 00000000 00:00 0

      b758a000-b772e000 r-xp 00000000 08:01 524970     /lib/i386-linux-gnu/libc-2.15.so

      b772e000-b7730000 r--p 001a4000 08:01 524970     /lib/i386-linux-gnu/libc-2.15.so

      b7730000-b7731000 rw-p 001a6000 08:01 524970     /lib/i386-linux-gnu/libc-2.15.so

      b7731000-b7734000 rw-p 00000000 00:00 0

      b7744000-b7747000 rw-p 00000000 00:00 0

      b7747000-b7748000 r-xp 00000000 00:00 0          [vdso]

      b7748000-b7768000 r-xp 00000000 08:01 524935     /lib/i386-linux-gnu/ld-2.15.so

      b7768000-b7769000 r--p 0001f000 08:01 524935     /lib/i386-linux-gnu/ld-2.15.so

      b7769000-b776a000 rw-p 00020000 08:01 524935     /lib/i386-linux-gnu/ld-2.15.so

      bfb5b000-bfb7c000 rw-p 00000000 00:00 0          [stack]

      每行数据格式如下:

      内存地址开始-结束 访问权限 偏移 主设备号:次设备号 i节点 文件

      或者用pmap命令查看

      leon@ubuntu:~$ pmap 2874

      2874:   ./a.out

      08048000      4K r-x--  /home/leon/a.out

      08049000      4K r----  /home/leon/a.out

      0804a000      4K rw---  /home/leon/a.out

      b7589000      4K rw---    [ anon ]

      b758a000   1680K r-x--  /lib/i386-linux-gnu/libc-2.15.so

      b772e000      8K r----  /lib/i386-linux-gnu/libc-2.15.so

      b7730000      4K rw---  /lib/i386-linux-gnu/libc-2.15.so

      b7731000     12K rw---    [ anon ]

      b7744000     12K rw---    [ anon ]

      b7747000      4K r-x--    [ anon ]

      b7748000    128K r-x--  /lib/i386-linux-gnu/ld-2.15.so

      b7768000      4K r----  /lib/i386-linux-gnu/ld-2.15.so

      b7769000      4K rw---  /lib/i386-linux-gnu/ld-2.15.so

      bfb5b000    132K rw---    [ stack ]

       total     2004K

      分别表示程序和C库的代码段、数据段、bss段

      进程全都地址空间大约2004KB,但只有大概不到200KB的内存区域是可写或私有的。如果一片内存范围是共享的或不可写的,那么内核只需要在内存中为文件保留一份映射,比如C库的代码,只读入一次是安全的。

      由于内存未被共享,所以只要一有进程写该处数据,那么该处数据就将被拷贝出来(写时拷贝),然后才被更新。

      每个和进程相关的内存区域都对应于一个vm_area_strcut结构体。

      4.操作内存区域

      内核时常需要在某个内存区域上执行一些操作,这些操作非常频繁,它们也是mmap()例程的基础,为了方便这类对内存区域的操作,内核定义了许多辅助函数声明在

      4.1 查找一个给定的内存地址属于哪一个内存区域: find_vma()

       

      此处)折叠或打开

       

       

      1. /* Look up the first VMA which satisfies addr < vm_end, NULL if none. */
      2. struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
      3. {
      4. struct vm_area_struct *vma = NULL;
      5.
      6. if (mm) {
      7. /* Check the cache first. */
      8. /* (Cache hit rate is typically around 35%.) */
      9. vma = mm->mmap_cache;
      10. if (!(vma && vma->vm_end > addr && vma->vm_start <= addr)) {
      11. struct rb_node * rb_node;
      12.
      13. rb_node = mm->mm_rb.rb_node;
      14. vma = NULL;
      15.
      16. while (rb_node) {
      17. struct vm_area_struct * vma_tmp;
      18.
      19. vma_tmp = rb_entry(rb_node,
      20. struct vm_area_struct, vm_rb);
      21.
      22. if (vma_tmp->vm_end > addr) {
      23. vma = vma_tmp;
      24. if (vma_tmp->vm_start <= addr)
      25. break;
      26. rb_node = rb_node->rb_left;
      27. } else
      28. rb_node = rb_node->rb_right;
      29. }
      30. if (vma)
      31. mm->mmap_cache = vma;
      32. }
      33. }
      34. return vma;
      35. }

       

      该函数在指定地址空间中搜索的一个vm_end大于addr的内存区域,这样返回的VMA首地址可能大于addr,所以指定的地址并不一定就包含在返回的VMA中。

      因为很有可能在执行某个VMA操作后,其他操作还会对该VMA进行操作,所以find_vma()函数返回的结果被缓存在内存描述符的mmap_cache域中,实践证明,被缓存的VMA有相当好的命中率(30~40%),检查被缓存的VMA速度会很快,如果指定的地址不在缓存中,那么必须搜索和内存描述符相关的所有内存区域,这种搜索通过红黑树进行。

      4.2 查找第一个和指定地址区间相交的VMA:find_vma_intersection()

       

      此处)折叠或打开

       

       

      1. /* Look up the first VMA which intersects the interval start_addr..end_addr-1,
      2. NULL if none. Assume start_addr < end_addr. */
      3. static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
      4. {
      5. struct vm_area_struct * vma = find_vma(mm,start_addr);
      6.
      7. if (vma && end_addr <= vma->vm_start)
      8. vma = NULL;
      9. return vma;
      10. }

       

      mm:要搜索的地址空间

      start_addr:区间的起始地址

      end_addr:区间尾地址

      如果find_vma()返回NULL,那么find_vma_intersection()返回NULL;

      如果find_vma()返回有效VMA,find_vma_intersection()只有在该VMA的起始位置于给定的地址区间结束位置之前,才将其返回,否者返回NULL

      5.mmap()和do_mmap():创建地址区间

      内核使用do_mmap()函数创建一个新的线性地址区间。如果这个新的VMA与相邻地址区间具有相同访问权限的话,将合并为一个VMA,如果不能合并,就确实需要创建一个新的VMA了。

      无论如何,do_mmap()函数都会将一个地址区间加入到进程的地址空间中,无论是扩展已存在的内存区域还是创建一个新的区域。

      在 中定义

      static inline unsigned long do_mmap(struct file *file, unsigned long addr,

             unsigned long len, unsigned long prot,

             unsigned long flag, unsigned long offset)

      该函数映射由file指定的文件,具体映射从文件偏移ofset开始,长度为len字节。如果file参数是NULL并且offset是0,那么代表这次映射没有和文件相关,这叫做匿名映射(anonymous mapping)。否则叫文件映射(file-backed mapping)。

      addr是可选参数,它指定搜索空闲区域的起始位置。

      prot参数指定内存区域中页面的访问权限。

       

       

      flag参数指定VMA标志,这些标志指定类型并改变映射的行为

       

       

      如果系统调用do_mmap()的参数中有无效参数,它返回一个负值;否者,就会在虚拟内存中分配额一个合适的新内存区域(有可能从slab中获取)。

      在用户空间通过调用mmap()系统调用获取内核do_mmap()的功能。

      void *mmap2(void *addr,

      size_t length,

      int prot,

                           int flags,

      int fd,

      off_t pgoffset)

      该系统调用是mmap()调用的第二种变种,所以起名为mmap2(),原始的mmap()方法的调用最后一个参数是字节偏移量,而mmap2()使用页面偏移量;mmap()调用由POSIX定义,C库中任然作为mmap()方法使用,但新内核中已经没有对应实现了,mmap()方法的调用是通过将字节偏移转化为页面偏移,从而转化为对mmap2()函数的调用来实现的。

      6.mummap()和do_mummap():删除地址区间

      do_mummap()从特定的进程地址空间中删除指定地址区间,定义在

      int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)

      从mm指定的用户空间,删除从地址start开始,长度为len字节的地址区间。

      Itn munmap(void *start, size_t length)

      该系统调用定义在文件mm/mmap.c中,它是对do_mummap()函数的一个简单封装:

       

      此处)折叠或打开

       

       

      1. asmlinkage long sys_munmap(unsigned long addr,size_t len)
      2. {
      3. int ret;
      4. struct mm_struct *mm;
      5.
      6. mm = current->mm;
      7. down_write(&mm->mmap_sem);
      8. ret = do_munmap(mm,addr,len);
      9. up_write(&mm->mmap_sem);
      10.
      11. return ret;
      12. }

      7.页表

      虽然应用程序操作的对象是对应虚拟内存,但处理器直接操作的却是物理内存,当应用程序访问一个虚拟地址时,首先必须将虚拟地址转化成物理地址,然后处理器才能解析地址访问请求。

      Linux使用三级页表管理完成地址转换,可按需求在编译简化使用两级,用三级是利用“最大公约数”的思想--- 一种设计简单的体系结构。

      每个进程都有自己的页表(线程会共享页表),内存描述符的pgd域指向的就是进程的页全局目录,注意,操作和检索页表时必须使用page_table_lock锁,该锁在相应进程的内存描述符中,防止竞争条件。

      页表对应的结构体依赖于具体的体系结构,定义在

      由于几乎每次对虚拟内存的页面访问都必须先解析它,从而得到物理地址,所以页表操作的性能非常关键。但不幸的是搜索内存中的物理地址速度很有限,为了加快搜索,多数体系结构实现了一个翻译后缓存器(translate lookaside buffer, TLB)。TLB缓存虚拟地址到物理地址的映射,如果访问的虚拟地址在缓存中命中,物理地址立刻返回;否者就需要再通过页表搜索需要的物理地址。

      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://blog.51cto.com/u_7784550/5677563,作者:luteresa,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:自定义注解

      下一篇:Oracle体系结构之内存结构(SGA、PGA)

      相关文章

      2025-05-14 10:33:31

      计算机小白的成长历程——数组(1)

      计算机小白的成长历程——数组(1)

      2025-05-14 10:33:31
      strlen , 个数 , 元素 , 内存 , 十六进制 , 地址 , 数组
      2025-05-14 10:33:25

      超级好用的C++实用库之网络

      在网络相关的项目中,我们经常需要去获取和设置设备的IP地址、子网掩码、网关地址、MAC地址等信息。这些信息一般与操作系统相关,在Windows系统和Linux系统上调用的接口是不一样的。

      2025-05-14 10:33:25
      Linux , 参数 , 地址 , 接口 , 网卡 , 返回值
      2025-05-14 10:33:25

      超级好用的C++实用库之环形内存池

      环形内存池是一种高效的内存管理技术,特别适合于高并发、实时性要求高的系统中,比如:网络服务器、游戏引擎、实时音视频等领域。

      2025-05-14 10:33:25
      buffer , CHP , 内存 , 分配 , 加锁
      2025-05-14 10:07:38

      30天拿下Rust之所有权

      在编程语言的世界中,Rust凭借其独特的所有权机制脱颖而出,为开发者提供了一种新颖而强大的工具来防止内存错误。这一特性不仅确保了代码的安全性,还极大地提升了程序的性能。

      2025-05-14 10:07:38
      data , Rust , 内存 , 函数 , 变量 , 数据
      2025-05-14 10:03:13

      超级好用的C++实用库之线程基类

      在C++中,线程是操作系统能够进行运算调度的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源,比如:内存空间和系统资源,但它们有自己的指令指针、堆栈和局部变量等。

      2025-05-14 10:03:13
      Linux , void , Windows , 函数 , 操作系统 , 线程
      2025-05-14 10:02:58

      Linux top 命令使用教程

      Linux top 是一个在Linux和其他类Unix 系统上常用的实时系统监控工具。它提供了一个动态的、交互式的实时视图,显示系统的整体性能信息以及正在运行的进程的相关信息。

      2025-05-14 10:02:58
      CPU , 信息 , 内存 , 占用 , 备注 , 进程
      2025-05-14 10:02:48

      使用JavaScript打印网页占用内存:详细指南

      在前端开发中,了解网页的内存占用情况对于优化性能和提高用户体验至关重要。

      2025-05-14 10:02:48
      JavaScript , 内存 , 占用 , 泄漏 , 浏览器 , 监听器 , 示例
      2025-05-14 09:51:15

      java循环创建对象内存溢出怎么解决

      在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError)。

      2025-05-14 09:51:15
      内存 , 占用 , 对象 , 引用 , 循环 , 次数 , 溢出
      2025-05-13 09:53:13

      计算机萌新的成长历程18——指针

      计算机要存储数据的话有以下几种途径,按访问速度由快到慢来排列分别是:寄存器>高速缓存>内存>硬盘。它们的存储空间大小是依次增大的,寄存器的存储空间大小最小,硬盘存储空间大小最大。

      2025-05-13 09:53:13
      内存 , 变量 , 地址 , 寄存器 , 指针
      2025-05-13 09:51:17

      使用Kernel 2.6版本的Linux系统运行dbca创建数据库实例时报错ORA-27125

      使用Kernel 2.6版本的Linux系统运行dbca创建数据库实例时报错ORA-27125

      2025-05-13 09:51:17
      dbca , Linux , ORA
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5255077

      查看更多

      最新文章

      30天拿下Rust之所有权

      2025-05-14 10:07:38

      Linux top 命令使用教程

      2025-05-14 10:02:58

      java循环创建对象内存溢出怎么解决

      2025-05-14 09:51:15

      m3db调优踩坑问题总结

      2025-05-08 09:03:57

      Redis多级缓存指南:从前端到后端全方位优化!

      2025-04-15 09:24:56

      php phpexcel导文件爆500(爆内存) && 应对

      2025-03-31 08:57:48

      查看更多

      热门文章

      获取服务器CPU、内存等各类信息工具类

      2023-04-27 08:00:25

      linux的存储管理

      2023-03-14 11:10:02

      libcurl第十二课 内存分析

      2023-03-15 09:25:34

      Linux中使用gdb dump内存

      2023-05-15 10:03:24

      JS 变量、作用域与内存

      2023-05-19 03:35:34

      Confluence 7 如何修改启动内存

      2023-05-31 08:47:59

      查看更多

      热门标签

      存储 缓存 内存 数据库 数据 redis mysql 服务器 数据恢复 Redis linux java 链表 MySQL sql
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      如何排查内存溢出问题

      linux中,如何看内存的使用情况呢?

      swap分区爆了解决办法(查看swap分区进程占用的内存)

      找出占用cpu内存过高的进程

      每日学习一个数据结构-Ziplist压缩表

      Linux内核之内存2: 内存的动态申请、释放的原理和细节

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号