Linux进程地址空间之初探:一

摘要:
2)它为每个进程提供了一致的地址空间,从而简化了存储器管理。3)它保护了每个进程的地址空间不被其他进程破坏。一:Linux虚拟内存区域及地址空间进程地址空间由进程可寻址的虚拟内存组成,对于某个虚拟内存地址,它要在地址空间范围内,例如:0421f000,这个值表示的是进程32位地址空间中的一个特定的字节。Linux进程的虚拟内存区域一般有:代码段、数据段、堆、用户栈、共享段。

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

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

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

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

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

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

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

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

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

二:内存描述符

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

1 structmm_struct {
2     struct vm_area_struct * mmap;        /*list of VMAs */
3     structrb_root mm_rb;
4     struct vm_area_struct * mmap_cache;    /*last find_vma result */
5     unsigned long free_area_cache;        /*first hole */
6     pgd_t *pgd;
7     atomic_t mm_users;            /*How many users with user space? */
8     atomic_t mm_count;            /*How many references to "struct mm_struct" (users count as 1) */
9     int map_count;                /*number of VMAs */
10     structrw_semaphore mmap_sem;
11     spinlock_t page_table_lock;        /*Protects task page tables and mm->rss */
12 
13     struct list_head mmlist;        /*List of all active mm's.  These are globally strung
14 * together off init_mm.mmlist, and are protected
15 * by mmlist_lock
16                          */
17 
18     unsigned longstart_code, end_code, start_data, end_data;
19     unsigned longstart_brk, brk, start_stack;
20     unsigned longarg_start, arg_end, env_start, env_end;
21     unsigned longrss, total_vm, locked_vm;
22     unsigned longdef_flags;
23 
24     unsigned long saved_auxv[40]; /*for /proc/PID/auxv */
25 
26     unsigned dumpable:1;
27 cpumask_t cpu_vm_mask;
28 
29     /*Architecture-specific MM context */
30 mm_context_t context;
31 
32     /*coredumping support */
33     intcore_waiters;
34     struct completion *core_startup_done, core_done;
35 
36     /*aio bits */
37 rwlock_t        ioctx_list_lock;
38     struct kioctx        *ioctx_list;
39 
40     structkioctx        default_kioctx;
41 };
这里我们主要看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中,如下所示:
1 structvm_area_struct {
2     struct mm_struct * vm_mm;    /*The address space we belong to. */
3     unsigned long vm_start;        /*Our start address within vm_mm. */
4     unsigned long vm_end;        /*The first byte after our end address
5 within vm_mm. */
6 
7     /*linked list of VM areas per task, sorted by address */
8     struct vm_area_struct *vm_next;
9 
10     pgprot_t vm_page_prot;        /*Access permissions of this VMA. */
11     unsigned long vm_flags;        /*Flags, listed below. */
12 
13     structrb_node vm_rb;
14 
15     /*
16 * For areas with an address space and backing store,
17 * linkage into the address_space->i_mmap prio tree, or
18 * linkage to the list of like vmas hanging off its node, or
19 * linkage of vma in the address_space->i_mmap_nonlinear list.
20      */
21 union {
22         struct{
23             structlist_head list;
24             void *parent;    /*aligns with prio_tree_node parent */
25             struct vm_area_struct *head;
26 } vm_set;
27 
28         structprio_tree_node prio_tree_node;
29 } shared;
30 
31     /*
32 * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
33 * list, after a COW of one of the file pages.  A MAP_SHARED vma
34 * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack
35 * or brk vma (with NULL file) can only be in an anon_vma list.
36      */
37     struct list_head anon_vma_node;    /*Serialized by anon_vma->lock */
38     struct anon_vma *anon_vma;    /*Serialized by page_table_lock */
39 
40     /*Function pointers to deal with this struct. */
41     struct vm_operations_struct *vm_ops;
42 
43     /*Information about our backing store: */
44     unsigned long vm_pgoff;        /*Offset (within vm_file) in PAGE_SIZE
45 units, *not* PAGE_CACHE_SIZE */
46     struct file * vm_file;        /*File we map to (can be NULL). */
47     void * vm_private_data;        /*was vm_pte (shared mem) */
48 
49 #ifdef CONFIG_NUMA
50     struct mempolicy *vm_policy;    /*NUMA policy for the VMA */
51 #endif
52 };

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

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

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

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

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

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

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

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

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

hello.c
1
#include<stdio.h> 2 intmain() 3 { 4 5 printf("Hello\n"); 6 return 0; 7 }

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

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

每行数据格式为:

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

Linux进程地址空间之初探:一第1张

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

Linux进程地址空间之初探:一第2张

从上面可以看出:

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进程地址空间之初探:一》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Shell篇(三)TC Shell从IP层TTL递减看校验和及ICMP下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Linux系统下用find命令查找最近修改过的文件

Linux的终端上,没有windows的搜索那样好用的图形界面工具,但find命令确是很强大的。 比如按名字查找一个文件,可以用 find / -name targetfilename 。 唉,如果只知道名字,不知道地点,这样也不失为一个野蛮有效的方法。 按时间查找也有参数 -atime 访问时间 -ctime 改变状态的时间 -mtime修改的时间。但...

Windows 任务管理器中的几个内存概念

我们使用的大部分 PC 是基于 Intel微处理器的 x86 和 x64 架构计算机. 因此, 我们面对的 windows 避免不了和 Intel 架构有些设计上的契合. 比如接下来要说到的内存管理.为简单起见, 我们只讨论 x86 体系架构的内存管理. 不考虑换页文件影响. 进程的内存 图 1(本图摘自 ref 2) 对于系统中的每一个进程而言, 都...

linux下重新安装grub

介绍 本篇指南会告诉你如果不知什么缘故丢失了GRUB后如何用一张Arch安装光盘重装GRUB。 注意 在这篇指南,我将使用sda1作为我的根udev类型references。如果你使用0.7.1以前的安装光盘,你应该将udev references换成devfs references。如果不明白请参见Technical_Terms#Hard_Drives。...

opencv linux

http://docs.opencv.org/doc/tutorials/introduction/linux_install/linux_install.html https://www.google.com.hk/search?q=opencv+linux&ie=utf-8&oe=utf-8&gws_rd=cr&ei=f...

Android开发环境搭建详细图解

所谓Android的开发环境,主要是以下两个组件,Android Software Developer Kit(Android软件开发工具包)和Eclipse(编辑器,提供很多方便功能)两大块,下面分别介绍他们的安装方法。 安装Eclipse 要安装Eclipse,首先要安装Java Developer Kit(JDK,Java开发者工具包)...

Linux内核分析:实验八--Linux进程调度与切换

刘畅 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 概述 这篇文章主要分析Linux中,进程调度和上下文切换的过程,会涉及到进度调度的时机和进程的切换运行过程,并通过GDB跟踪Linux的schedule()函数来比較深入的理解一下这个过程。 进...