http://blog.csdn.net/qq_26768741/article/details/54375524

前言

上一篇我们谈论了task_struct这个结构体,它被叫做进程描述符,内部成员包含了很多与进程相关的信息,今天我们来看一下其中一个被叫做内存描述符的结构体——mm_struct,抽象的来描述linux下进程的地址空间的所有的信息。

1.概述


一个进程的虚拟地址空间主要由两个数据结来描述。一个是最高层次的:mm_struct,一个是较高层次的:vm_area_structs。最高层次的mm_struct结构描述了一个进程的整个虚拟地址空间。较高层次的结构vm_area_truct描述了虚拟地址空间的一个区间(简称虚拟区)。每个进程只有一个mm_struct结构,在每个进程的task_struct结构中,有一个指向该进程的结构。可以说,mm_struct结构是对整个用户空间的描述。

首先,我们来定位mm_struct文件所在位置和task_struct所在路径是一样的,不过他们所在文件是不一样的,mm_struct所在的文件是mm_types.h,接下来我们就来分析这个结构好了。

首先我们来看下这个: 

这就是我们所说的由task_struct到mm_struct,进程的地址空间的分布。

每一个进程都会有自己独立的mm_struct,这样每一个进程都会有自己独立的地址空间,这样才能互不干扰。当进程之间的地址空间被共享的时候,我们可以理解为这个时候是多个进程使用一份地址空间,这就是线程。

其实多个进程的地址空间分布就是上面这张图一样,每一个进程的用户空间在32位的平台上就是上面这个图的情况,对于物理内存当中的内核kernel,是只存在一份,所有的进程是用来共享的,内核当中会利用PCB(进程控制块)来管理不同的进程。对于linux的体系结构来说,linux当中为了保护虚拟内核空间不被修改,所以linux体系结构是这样的:

这种三层的体系结构,保证进程只能对最外面的应用程序进行修改,保证了内存的安全性。

另外,我们从第一张图上可以发射线,每个区域是依靠着两个指针进行维护的,比如[start_data,end_data)是用来维护data段,[start_code,end_data)用来维护code段,[start_brk,brk),用来维护heap和heap的指针。[start_stack,end_stack)是用来维护stack段空间范围。mmap_base是维护共享映射区的起始地址。bss段表示的是所有的未初始化的全局变量,为了效率,对处在bss段的变量,将它们匿名映射到“零页”,这样提高了程序的加载效率。

    //指向线性区对象的链表头
struct vm_area_struct * mmap; /* list of VMAs */
//指向线性区对象的红黑树
struct rb_root mm_rb;

在地址空间中,mmap为地址空间的内存区域(用vm_area_struct结构来表示)链表,mm_rb用红黑树来存储,链表表示起来更加方便,红黑树表示起来更加方便查找。区别是,当虚拟区较少的时候,这个时候采用单链表,由mmap指向这个链表,当虚拟区多时此时采用红黑树的结构,由mm_rb指向这棵红黑树。这样就可以在大量数据的时候效率更高。所有的mm_struct结构体通过自身的mm_list域链接在一个双向链表上,该链表的首元素是init_mm内存描述符,代表init进程的地址空间。

    atomic_t mm_users;
atomic_t mm_count;

这两个内容表示的各有不同。

成员 内容
mm_users 进程数量值(在多线程的情况下尤为适用)
mm_count 引用计数(当计数为0的时候表示没有再被使用)

使用mm_users和mm_count两个计数器是为了区别主使用计数器和使用该地址空间的进程的数目。

每一个进程都可以被别的进程来共享,也就是和别的进程来共享mm_struct.

所有的mm_struct结构以链表的形式存在mm_struct的。

另外需要说明的就是kernel线程是没有地址空间的,也就没有对应的mm_struct,kernel线程使用之前运行的进程的内存描述符。

程序中通常用到的地址常常具有局部性,当前最近一次用蛋糕的虚拟地址区间很可能下一次还是需要用到,所以我们采用局部性原理,通常时候我们去吧当前地址周围一个区间的内存放入高速缓存当中,这个区间在mm_struct当中就是由mmap_cache来维护。

2.关于页表

linux kernel 使用内存管理的时候,采取的是页式的管理方式,应用程序给出的内存地址是虚拟地址,是经过若干层的页表的转换才能得到真正的物理地址,所以相对来说,进程的地址空间是一份虚拟的地址空间,每一个地址通过页表的转换映射到所谓的物理地址空间上。在这里所共享的1G的kernel在内存地址是只存一份的,但是对于每一个进程其他的3G的空间,是存储其他不同的东西,另外,页表具有权限限定,这样也就提供给了每块内存区域,比如我定义了:

char * p="12342";

这里的“12342”是一个常量字符串,它被存放在只读常量存储区,所以这个区域的页表的属性就是只读,这样就可以高效的维护整个进程的地址空间。

每一个进程都会有一个进程描述符,task_struct,task_strust当中的mm指针指向每个进程的内存描述符,而对于每个mm,有都会有单独的页表,

pgt区间是用来维护页表的目录,每一个进程的都有自己的页表目录,需要注意进程的页目录和内核的页目录是不一样的,当程序调度器调度程序运行的时候,这个时候这个地址就会转换成为物理地址,linux一般采用三级页表进行转换。

3.task_struct和mm_strcuct的联系

不知道你是否还记得在task_struct当中的

    //关于进程的地址空间,指向进程的地址空间。(链表和红黑树)
struct mm_struct *mm, *active_mm;

task_struct和mm_strcut通过这两个成员进行和mm_struct联系,每一个进程都会有唯一的mm_struct结构体。



struct mm_struct {

    //指向线性区对象的链表头
struct vm_area_struct * mmap; /* list of VMAs */
//指向线性区对象的红黑树
struct rb_root mm_rb;
//指向最近找到的虚拟区间
struct vm_area_struct * mmap_cache; /* last find_vma result */ //用来在进程地址空间中搜索有效的进程地址空间的函数
unsigned long (*get_unmapped_area) (struct file *filp,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags); unsigned long (*get_unmapped_exec_area) (struct file *filp,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags); //释放线性区时调用的方法,
void (*unmap_area) (struct mm_struct *mm, unsigned long addr); //标识第一个分配文件内存映射的线性地址
unsigned long mmap_base; /* base of mmap area */ unsigned long task_size; /* size of task vm space */
/*
* RHEL6 special for bug 790921: this same variable can mean
* two different things. If sysctl_unmap_area_factor is zero,
* this means the largest hole below free_area_cache. If the
* sysctl is set to a positive value, this variable is used
* to count how much memory has been munmapped from this process
* since the last time free_area_cache was reset back to mmap_base.
* This is ugly, but necessary to preserve kABI.
*/
unsigned long cached_hole_size; //内核进程搜索进程地址空间中线性地址的空间空间
unsigned long free_area_cache; /* first hole of size cached_hole_size or larger */ //指向页表的目录
pgd_t * pgd; //共享进程时的个数
atomic_t mm_users; /* How many users with user space? */ //内存描述符的主使用计数器,采用引用计数的原理,当为0时代表无用户再次使用
atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */ //线性区的个数
int map_count; /* number of VMAs */ struct rw_semaphore mmap_sem; //保护任务页表和引用计数的锁
spinlock_t page_table_lock; /* Protects page tables and some counters */ //mm_struct结构,第一个成员就是初始化的mm_struct结构,
struct list_head mmlist; /* List of maybe swapped mm's. These are globally strung
* together off init_mm.mmlist, and are protected
* by mmlist_lock
*/ /* Special counters, in some configurations protected by the
* page_table_lock, in other configurations by being atomic.
*/ mm_counter_t _file_rss;
mm_counter_t _anon_rss;
mm_counter_t _swap_usage; //进程拥有的最大页表数目
unsigned long hiwater_rss; /* High-watermark of RSS usage */、
//进程线性区的最大页表数目
unsigned long hiwater_vm; /* High-water virtual memory usage */ //进程地址空间的大小,锁住无法换页的个数,共享文件内存映射的页数,可执行内存映射中的页数
unsigned long total_vm, locked_vm, shared_vm, exec_vm;
//用户态堆栈的页数,
unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
//维护代码段和数据段
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; unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ struct linux_binfmt *binfmt; cpumask_t cpu_vm_mask; /* Architecture-specific MM context */
mm_context_t context; /* Swap token stuff */
/*
* Last value of global fault stamp as seen by this process.
* In other words, this value gives an indication of how long
* it has been since this task got the token.
* Look at mm/thrash.c
*/
unsigned int faultstamp;
unsigned int token_priority;
unsigned int last_interval; //线性区的默认访问标志
unsigned long flags; /* Must use atomic bitops to access the bits */ struct core_state *core_state; /* coredumping support */
#ifdef CONFIG_AIO
spinlock_t ioctx_lock;
struct hlist_head ioctx_list;
#endif
#ifdef CONFIG_MM_OWNER
/*
* "owner" points to a task that is regarded as the canonical
* user/owner of this mm. All of the following must be true in
* order for it to be changed:
*
* current == mm->owner
* current->mm != mm
* new_owner->mm == mm
* new_owner->alloc_lock is held
*/
struct task_struct *owner;
#endif #ifdef CONFIG_PROC_FS
/* store ref to file /proc/<pid>/exe symlink points to */
struct file *exe_file;
unsigned long num_exe_file_vmas;
#endif
#ifdef CONFIG_MMU_NOTIFIER
struct mmu_notifier_mm *mmu_notifier_mm;
#endif
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
pgtable_t pmd_huge_pte; /* protected by page_table_lock */
#endif
/* reserved for Red Hat */
#ifdef __GENKSYMS__
unsigned long rh_reserved[2];
#else
/* How many tasks sharing this mm are OOM_DISABLE */
union {
unsigned long rh_reserved_aux;
atomic_t oom_disable_count;
}; /* base of lib map area (ASCII armour) */
unsigned long shlib_base;
#endif
};
github注释源码链接:https://github.com/wsy081414/C_linux_practice/blob/master/mm_struct.c

进程—内存描述符(mm_struct)的更多相关文章

  1. Linux之内存描述符mm_struct

    Linux对于内存的管理涉及到非常多的方面,这篇文章首先从对进程虚拟地址空间的管理说起.(所依据的代码是2.6.32.60) 无论是内核线程还是用户进程,对于内核来说,无非都是task_struct这 ...

  2. Rootkit 核心技术——利用 nt!_MDL(内存描述符链表)突破 SSDT(系统服务描述符表)的只读访问限制 Part I

    -------------------------------------------------------- 在 rootkit 与恶意软件开发中有一项基本需求,那就是 hook Windows ...

  3. fork()父子进程文件描述符的关系

    父子进程共享文件描述符:此说法,其实是父子进程共享 文件表项(父进程和子进程共享同一个file table entry) 由于子进程是父进程的拷贝,子进程会拷贝父进程的进程描述符中的文件描述符表,可以 ...

  4. ZT 父子进程共享文件描述符

    转贴自倒霉熊的博客 [linux学习笔记-2]父子进程共享文件描述符 (2009-03-02 23:03:17) 转载▼ 标签: 学习 linux 子进程 文件描述符 杂谈 分类: 学习 #inclu ...

  5. Linux进程管理描述符 task_struct

    转:http://blog.csdn.net/hongchangfirst/article/details/7075026 大家都知道进程,可是知道linux是怎么管理其进程的吗?每一个进程都有一个进 ...

  6. 进程描述符task_struct

    1.进程状态 volatile long state; int exit_state; state成员的可能取值如下: #define TASK_RUNNING        0 #define TA ...

  7. 进程管理—进程描述符(task_struct)

    http://blog.csdn.net/qq_26768741/article/details/54348586 当把一个程序加载到内存当中,此时,这个时候就有了进程,关于进程,有一个相关的叫做进程 ...

  8. 进程描述符(PCB)

    进程描述符(PCB) 概述 CPU作为计算机的核心部件,我们当然希望它能一直工作,充分提高它的使用效率.对于上层软件来说,我们不可能直接去操控CPU(我们没这能力也没必要),因为操作系统是夹在计算机硬 ...

  9. [转] linux系统文件流、文件描述符与进程间关系详解

    http://blog.sina.com.cn/s/blog_67b74aea01018ycx.html linux(unix)进程与文件的关系错综复杂,本教程试图详细的阐述这个问题. 包括:     ...

随机推荐

  1. git 合并某个提交commit到指定的分支上

    https://blog.csdn.net/anhenzhufeng/article/details/77962943 git checkout master git cherry-pick 62ec ...

  2. 2.Spring的Bean生命周期和组装方式

    1.Spring IoC容器概述 Spring IoC容器: Spring容器即体现了IoC原理    Spring容器通过读取配置元数据负责对Beans实例化.配置和装配     配置元数据可以用X ...

  3. OS---文件结构

    1.概述 1.1 对于任何一个文件,都存在以下2种形式结构: 文件的逻辑结构: 从用户的角度出发所观察到的文件组织形式,独立于文件的物理特性: 文件的物理结构(文件存储结构): 文件在外存上的存储组织 ...

  4. Django-3 视图层

    5.1 视图函数 一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应.响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片 ...

  5. Selenium打开IE报错“Protected Mode settings...”解决方法

    最近在使用Selenium打开IE浏览器碰到以下报错:

  6. cannot focus element解决方案

    If you enconter error "cannot focus element" when using Selenium+Python in Chrome to input ...

  7. (转)CentOS下的trap命令

    trap命令用于指定在接收到信号后将要采取的动作.常见的用途是在脚本程序被中断时完成清理工作.不过,这次我遇到它,是因为客户有个需求:从终端访问服务器的用户,其登陆服务器后会自动运行某个命令,例如打开 ...

  8. 读<<programming ruby>> 7.6节 flip-flop 理解

    书中源码是这样的 File.foreach('1.txt') do |x| if(($. == 1) || x =~ /eig/) .. (($. == 3) || x =~ /nin/) then ...

  9. Smarty 限制foreach的循环次数

    <!--{foreach from=$news item=news name=foo}--> <!--{if $smarty.foreach.foo.index <7}--&g ...

  10. eclipse编码转换

    一般Java文件编码格式是UTF-8的.以下以默认GBK改为UTF-8为例. 1.改变整个工作空间的编码格式,这样以后新建的文件也是新设置的编码格式.     eclipse->window-& ...