聊聊 linux 虚拟内存

        虚拟内存,是相对于物理内存而言的。虽然也冠以"内存",但它并不是一段存储空间,仅仅是对物理内存的一个抽象,是为了更加有效地管理物理内存而发明出来的一个玩意。

        那么,如果没有虚拟内存,会有哪些问题呢?在讨论问题之前要清楚两点:

物理内存基于成本的考虑,一般不会太大
系统可同时运行多个进程

        基于以上两点,如果没有虚拟内存,对物理内存直接操作,易产生以下问题:

由于程序是运行在内存中的,当运行的进程过多,会导致一些程序无法正常运行;
由于直接操作物理内存,a 进程可能会写入 b 进程正在用的内存空间,缺乏保护机制;
还有一点,如果在程序中直接操作物理内存,对开发人员也不太友好。

        为了解决以上问题,从而提出了虚拟内存机制,作为内存管理的工具,为每个进程提供独立的虚拟地址空间。

        进程虚拟内存很多地方都见过,从低地址到高地址分别是代码段、数据段、堆、共享库以及栈等。在进程虚拟内存上面是内核虚拟内存,包括内核中的代码和和数据结构、其他区域则包含每个进程都不相同的数据,如页表、内核在进程的上下文中执行代码时使用的栈,以及记录虚拟地址空间当前组织的各种数据结构等。

        对于进程虚拟内存,代码段、数据段、堆、共享库段,以及用户栈也可都称为区域,只是功能不同而已。区域内部是一段连续的存储空间,但区域之间不一定连续存储,可能存在空隙。那么进程的各个区域是怎么联系起来的呢?先看下区域在内核中是如何定义的。

/*
 * This struct defines a memory VMM memory area. There is one of these per VM-area/task. 
 * A VM area is any part of the process virtual memory space that has a special rule for the page-fault handlers.
 */
struct vm_area_struct {
    /* The first cache line has the info for VMA tree walking. */

    unsigned long vm_start;     /* Our start address within vm_mm. */
    unsigned long vm_end;       /* The first byte after our end address within vm_mm. */

    /* linked list of VM areas per task, sorted by address */
    struct vm_area_struct *vm_next, *vm_prev;
    ...
    struct mm_struct *vm_mm;    /* The address space we belong to. */
    pgprot_t vm_page_prot;      /* Access permissions of this VMA. */
    unsigned long vm_flags;     /* Flags, see mm.h. */
    ...
};

        可以看到区域在内核中被定义成 vm_area_struct 结构体,成员指针 *vm_next 指向下一个区域,*vm_prev 指向上一个区域,各区域之间连接成一个双向链表。vm_start 和 vm_end 标记该区域的首地址和尾地址,并且可以通过指针 *vm_mm 指向所属的地址空间。而进程也就是通过这个地址空间与各个区域连接起来的。

struct task_struct {
    volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
    ...
    struct mm_struct *mm, *active_mm;
    ...
}

struct mm_struct {
    struct vm_area_struct * mmap;       /* list of VMAs */
    ...
    unsigned long highest_vm_end;       /* highest vma end address */
    pgd_t * pgd;
    atomic_t mm_users;          /* How many users with user space? */
    atomic_t mm_count;          /* How many references to "struct mm_struct" (users count as 1) */
    atomic_long_t nr_ptes;      /* Page table pages */
    int map_count;              /* number of VMAs */
    ...
}

        内核为系统中的每个进程都维护了一个任务结构体 task_struct,该结构体记录了当前进程的pid、状态,以及指向虚拟内存的指针等等。虚拟内存被定义成 mm_struct 结构体,第一个成员便是指向各个区域组成的双向链表的指针 *mmap。大概如下图所示:

        回到开始的问题,从上述的各结构体定义可以看到,每个进程都有属于自己的一段地址空间,进程之间互不干涉,从而避免了 a 进程写入 b 进程地址空间的可能;开发者也只需调用 malloc 等方法即可申请内存空间,无需考虑如何与物理内存交互;如果运行进程过多,一定范围内可使用页面置换算法,将不常用的页面置换出去,从而腾出空间。

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值