引言:现代操作系统提供了一种对内存的抽象概念,叫做虚拟存储器,它为每个进程提供了一个大的,一致的,和私有的地址空间。通过一个很清晰的机制,虚拟存储器提供了3个重要的能力:

1)它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,它高效的使用了主存。

2)它为每个进程提供了一致的地址空间,从而简化了存储器管理。

3)它保护了每个进程的地址空间不被其他进程破坏。

Linux操作系统同样也采用了虚拟内存技术,对一个进程而言,它好像可以访问整个系统的所有物理内存,更重要的是,即使单独一个进程,它拥有的地址空间也可以远远大于系统物理内存。

 

一:Linux虚拟内存区域及地址空间

       进程地址空间由进程可寻址的虚拟内存组成,对于某个虚拟内存地址,它要在地址空间范围内,例如: 0421f000,这个值表示的是进程32位地址空间中的一个特定的字节。尽管一个进程可以寻址4GB的虚拟内存(在32位的地址空间中),但是这并不代表它有权访问所有的虚拟地址。在地址空间中,我们更常用或者关心的是某个虚拟内存地址空间,比如 0848000-084c000,它们可以被进程访问。我们称这些可被访问的合法地址空间称为虚拟内存区域。通过内核,进程可以给自己的地址空间动态的增加或减少虚拟内存区域。

       Linux进程的虚拟内存区域一般有:代码段、数据段、堆、用户栈、共享段。每个存在的虚拟页面都保存在某个区域中,而不属于某个区域的虚拟页是不存在的,并且不能被进程访问。内核不用记录那些不存在的虚拟页,而这样的页也不占用存储器、磁盘或者内核本身的其他任何资源。

       进程只能访问有效内存区域的内存地址,每个内存区域也具有相关权限,如可读、可写、可执行性质。如果一个进程访问了无效范围中的内存区域或者以不正确的方式访问了有效地址,那么内核就会终止该进程,并返回  “段错误”信息。

     

二:内存描述符

    task_struct 中的一个条目 mm 指向mm_struct 即内存描述符,它描述了进程的虚拟内存当前状态。mm_struct 定义在linux/sched.h中:

 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 free_area_cache; /* first hole */
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) */
int map_count; /* number of VMAs */
struct rw_semaphore mmap_sem;
spinlock_t page_table_lock; /* Protects task page tables and mm->rss */ struct list_head mmlist; /* List of all active mm's. These are globally strung
* together off init_mm.mmlist, and are protected
* by mmlist_lock
*/ 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 rss, total_vm, locked_vm;
unsigned long def_flags; unsigned long saved_auxv[]; /* for /proc/PID/auxv */ unsigned dumpable:;
cpumask_t cpu_vm_mask; /* Architecture-specific MM context */
mm_context_t context; /* coredumping support */
int core_waiters;
struct completion *core_startup_done, core_done; /* aio bits */
rwlock_t ioctx_list_lock;
struct kioctx *ioctx_list; struct kioctx default_kioctx;
};
这里我们主要看3个字段:1:struct vm_area_struct * mmap;  2:struct rb_root mm_rb  3: pgd_t * pgd;
    其中 pgd 指向第一级页表即页全局目录的基址,当内核运行这个进程时,它就将pgd存放在CR3寄存器内,根据它来进行地址转换工作。
    mmap 和 mm_rb 这两个不同数据结构体描述的对象是相同的:该地址空间中的所有内存区域。
    mmap 指向一个 vm_area_struct 结构的链表,利于简单、高效地遍历所有元素。
    mm_rb 指向的是一个红-黑树结构节点,适合搜索指定元素。
   
    其中每个 vm_area_struct 描述了当前虚拟地址空间的一个内存区域。这个结构定义在 文件 linux/mm.h中,如下所示:
 struct vm_area_struct {
struct mm_struct * vm_mm; /* The address space we belong to. */
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; pgprot_t vm_page_prot; /* Access permissions of this VMA. */
unsigned long vm_flags; /* Flags, listed below. */ struct rb_node vm_rb; /*
* For areas with an address space and backing store,
* linkage into the address_space->i_mmap prio tree, or
* linkage to the list of like vmas hanging off its node, or
* linkage of vma in the address_space->i_mmap_nonlinear list.
*/
union {
struct {
struct list_head list;
void *parent; /* aligns with prio_tree_node parent */
struct vm_area_struct *head;
} vm_set; struct prio_tree_node prio_tree_node;
} shared; /*
* A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
* list, after a COW of one of the file pages. A MAP_SHARED vma
* can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack
* or brk vma (with NULL file) can only be in an anon_vma list.
*/
struct list_head anon_vma_node; /* Serialized by anon_vma->lock */
struct anon_vma *anon_vma; /* Serialized by page_table_lock */ /* Function pointers to deal with this struct. */
struct vm_operations_struct * vm_ops; /* Information about our backing store: */
unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE
units, *not* PAGE_CACHE_SIZE */
struct file * vm_file; /* File we map to (can be NULL). */
void * vm_private_data; /* was vm_pte (shared mem) */ #ifdef CONFIG_NUMA
struct mempolicy *vm_policy; /* NUMA policy for the VMA */
#endif
};

 每个内存区域描述符都对应于进程地址空间中的唯一地址空间:

 vm_start 域:指向区域的首地址(最低地址),它本身在区间内。

 vm_end 域:指向区域的尾地址(最高地址)之后的第一个字节,它本身在区间外。

 vm_end - vm_start 的大小就是这个内存区域的长度,另外注意,在同一个地址空间内的不同内存区域不能重叠。

 vm_next : 指向链表中下一个区域结构。

 vm_flags:描述这个区域内的页面是共享的还是私有的。

 vm_page_prot : 描述这个区域内包含的所有页的读写许可权限。

 

三:实际使用中的内存区域

下面我们分别用/proc 文件系统和 pmap工具查看下给定进程的内存空间及所包含的内存区域。

hello.c

1 #include<stdio.h>
int main()
{ printf("Hello\n");
return ;
}

下面列出该进程地址空间中的内存区域:

先通过/proc文件系统查看:

每行数据格式为:

起始地址     -     尾部地址 访问权限    偏移      主设备号:次设备号 i节点   文件名

下面是用pmap工具查看的结果:

 

 

 

从上面可以看出:

1:可执行对象hello的 代码段、数据段 、bss段的虚拟内存区间

2:C库中libc.so的代码段、数据段、bss段

3:动态链接程序ld.so的代码段、数据段、bss段

4:分配的内存段即相当于堆段   [anon] ,分配的栈段 [stack]

5:  mappded: 2004K 表示该进程的全部地址空间大约为2004K

6:writeable/private 172k  表示可读写的内存空间大小,即消耗的物理内存空间大小。

进程访问了2003KB的数据和代码空间,而仅仅消耗了172KB的物理内存,如果一片内存区域是共享的或者不可写的,那么内核只需要在内存中为此保留一份映射。可以看出利用这种共享不可写内存的方法节约了大量内存空间。

linux内核--进程地址空间(一)的更多相关文章

  1. linux内核--进程地址空间(三)

    引言:上篇博文中,我们简单的介绍了Linux虚拟存储器的概念及组成情况,下面来分析分析进程的创建和终结及跟进程地址空间的联系. 这里首先介绍一个比较重要的概念:存储器映射 在Linux系统中,通过将一 ...

  2. Linux计算机进程地址空间与内核装载ELF

    本文基于Linux™系统对进程创建与加载进行分析,文中实现了Linux库函数fork.exec,剖析内核态执行过程,并进一步展示进程创建过程中进程控制块字段变化信息及ELF文件加载过程. 一.初识Li ...

  3. linux内核--进程与线程

    http://blog.csdn.net/yusiguyuan/article/details/12154823 在<linux内核设计与实现>中第三章讲解了进程管理,在关于进程和线程的概 ...

  4. linux内核--进程空间(二)

        内核处理管理本身的内存外,还必须管理用户空间进程的内存.我们称这个内存为进程地址空间,也就是系统中每个用户空间进程所看到的内存.linux操作系统采用虚拟内存技术,因此,系统中的所有进程之间虚 ...

  5. Linux内核——进程管理与调度

    进程的管理与调度 进程管理 进程描写叙述符及任务结构 进程存放在叫做任务队列(tasklist)的双向循环链表中.链表中的每一项包括一个详细进程的全部信息,类型为task_struct,称为进程描写叙 ...

  6. 深入理解linux内核-进程和程序

    进程描述符task_struct task_struct { //进程基本信息 pid 进程id号 tgid 线程组id号,与线程组领头线程pid号相同   getpid()返回该值 tasks in ...

  7. linux内核 进程管理

    进程和线程 进程不单单包含可执行代码(代码段),好包含打开的文件,挂起的信号,处理器状态,虚拟内存地址等. 线程:从内核的角度来说,它并没有线程这个概念.Linux把所有线程都当做进程来实现.内核并没 ...

  8. Linux内核 ——进程管理之进程诞生(基于版本4.x)

    <奔跑吧linux内核>3.1笔记,不足之处还望大家批评指正 进程是Linux内核最基本的抽象之一,它是处于执行期的程序.它不仅局限于一段可执行代码(代码段),还包括进程需要的其他资源.在 ...

  9. Linux内核——进程管理之CFS调度器(基于版本4.x)

    <奔跑吧linux内核>3.2笔记,不足之处还望大家批评指正 建议阅读博文https://www.cnblogs.com/openix/p/3262217.html理解linux cfs调 ...

随机推荐

  1. 【转】如何设置无线路由器的信道以获得最佳WIFI体验?

    原文网址:http://jingyan.baidu.com/album/f25ef2546e28e4482c1b8225.html 现在随着移动互联网的发展,移动终端的普及,WIFI越来越必不可少,所 ...

  2. QT_编程基础

    简单介绍 Qt是一个由奇趣科技开发的跨平台C++图形用户界面应用程序开发框架.它既能够开发GUI程式,也可用于开发非GUI程式,比方控制台工具和server. Qt是面向对象语言,易于扩展,而且同意组 ...

  3. Android项目打包开启proguard的混淆优化带来的问题

    1.引入一个sdk以后.打包报错: [INFO] Unexpected error while evaluating instruction: [INFO]   Class       = [com/ ...

  4. Eclipse SVN插件的帐号、password改动

    问题描写叙述: Eclipse的SVN插件Subclipse做得非常好,在svn操作方面提供了非常强大丰富的功能.但到眼下为止,该插件对svn用户的概念极为淡薄,不但不能方便地切换用户,并且一旦用户的 ...

  5. FlashBack-SCN-TIMESTAMP

    一.基于时间(as of timestamp)的flashback1.创建表create table flash_tab(id,vl) as select rownum,oname from ( se ...

  6. 第四章 Linux环境

    程序参数 main函数声明: int main(int  argc, char  *argv[]) 其中argc是接受到的参数个数,argv是存储参数的字符串数组. 或者声明为: main() 这样也 ...

  7. asp.net 常用的3中身份验证

    1. windows验证: IIS根据应用程序的设置来进行身份验证,要使用这中验证方式,必须禁止使用匿名用户登录. 2. Forms验证: 通过Cookies来保存用户凭证,对未登录的用户 重定向到自 ...

  8. RDLC报表系列(一) 简单的动态数据绑定和配置

    RDLC系列链接 RDLC报表系列(一) 简单的动态数据绑定和配置  RDLC报表系列(二) 行分组 RDLC报表系列(三) 总计和折叠 RDLC报表系列(四) 矩阵 RDLC报表系列(五) 简单的图 ...

  9. ASP.NET属性之AssociatedControlID

    AssociatedControlID 是用在Asp.Net 中的 Label 控件上.给label控件关联一个ASP.NET的控件,在点击 这个 LABEL的时候,所关联的ASP.NET控件会获得焦 ...

  10. Android app去应用市场评分功能

    因为android市场很多,去各个应用市场评分的功能却不是很难实现. private void gotoRate(){ Uri uri = Uri.parse("market://detai ...