概述
Linux地址空间
- 进程地址空间
- 进程地址空间是内存地址吗?
- 什么是地址空间
- 地址空间提出的原因?
- 地址空间是怎么工作的?
- 接下来深刻理解一下什么是进程
- 地址空间的三种映射
- 页表映射
- 段表映射
- 段页式内存管理
进程地址空间
进程地址空间是内存地址吗?
#include<iostream>
#include<unistd.h>
using namespace std;
int g_val=100;
int main()
{
cout<<"this is my process"<<endl;
pid_t pd=fork();
if(pd<0)
{
cerr<<"failed"<<endl;
}
else if(pd==0)
{
g_val=1000;
cout<<"i am son"<<"my val is"<<g_val<<" my address "<<&g_val<<endl;
}
else
{
sleep(2);
cout<<"i am father"<<"my val is"<<g_val<<" my address "<<&g_val<<endl;
}
return 0;
}
实验结果:
结论:地址空间绝对不是数据存储的真实物理地址。它是一段虚拟地址。最终一定要以某种方式转化为物理地址。
由此可知,我们在任何情况下打印出来的地址都是虚拟地址,而不是其真实物理地址。
什么是地址空间
地址空间就是一个结构体,通过结构体将我们所虚拟化出的栈、堆、数据段、代码段等组织起来的一个结构体
其中进程的PCB中含有该结构体(mm_struct),因为内存的分配是离散分配的,所以我们不得不让该结构体将数据组织起来。
解释一下:为什么两个不一样的数据其地址是相同的?
1.因为子进程要拷贝父进程的PCB,所以它们是相同的。
2.因为子进程在执行时对全局变量g_val进行了修改,此时它们虽然看起来地址是一样的,但在物理内存中,一定存在额外的物理地址去存储子进程的数据。这是进程独立性的特点。
接下来回答一下到底什么是地址空间:
地址空间,是对物理内存的一段虚拟化表示,虚拟地址一定要通过某种映射关系将其转化为物理地址。 通常有页表、段表、段页式结合,来将其变为物理内存的地址,后面阐述。
地址空间提出的原因?
如果没有地址空间的话,我们访问的永远都是真实的物理地址,会有两个弊端。
1,保护内存。如果直接对物理内存进行操作的话,则有可能会对操作范围之外的数据进行错误的操作,比如野指针等概念。
2.数据在内存中的存储是离散分配的,如果采用直接访存的话,非常的不方便,增加了越界访存的概率。
地址空间是怎么工作的?
地址空间,利用虚拟内存将空间连续化处理了。
地址空间的描述:
通过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
};
地址空间的组织:
每个进程的PCB中都有一个mm_struct结构体,此时对虚拟地址进行映射,得到真实的物理地址。
接下来深刻理解一下什么是进程
进程就是,是程序的一次执行,是处理机调度和处理机资源分配的一个基本单位。由进程常见的数据结构(struct task_struct(进程控制块)&&struct mm_struct(地址空间))和代码、数据组成。
地址空间的三种映射
页表映射
将程序的逻辑地址空间划分为固定大小的页(page),而物理内存划分为同样大小的页框(page frame)。程序加载时,可将任意一页放人内存中任意一个页框,这些页框不必连续,从而实现了离散分配。在页式存储管理方式中地址结构由两部构成,前一部分是页号,后一部分为页内地址w(位移量);
需要几次访存?
两次,首先明确页表是位于内存中的,第一次访存是为了获取块号,通过的出的块号+偏移地址得出它的物理地址,第二次访存去取数据或代码
优缺点:
1.没有外碎片,每个内碎片不超过页大比前面所讨论的几种管理方式的最大进步是
2,一个程序不必连续存放。
3.便于改变程序占用空间的大小(主要指随着程序运行,动态生成的数据增多,所要求的地址空间相应增长)。
缺点:要求程序全部装入内存,没有足够的内存,程序就不能执行;
段表映射
在段式存储管理中,将程序的地址空间划分为若干个段(segment),这样每个进程有一个二维的地址空间。在前面所介绍的动态分区分配方式中,系统为整个进程分配一个连续的内存空间。而在段式存储管理系统中,则为每个段分配一个连续的分区,而进程中的各个段可以不连续地存放在内存的不同分区中。程序加载时,操作系统为所有段分配其所需内存,这些段不必连续,物理内存的管理采用动态分区的管理方法。
简单来说,对于一个进程而言,其中不同的数据可能在栈上,可能在堆上,于是基于这种特点引进了分段式内存管理。
当然, 虚拟地址和物理地址之间是通过段表来进行映射的。可以参考分页式内存管理。
分段分页的异同
1.需求 是信息的物理单位,分页是为了实现离散分配方式,以减少内存的碎片,提高内存的利用率。或者说,分页仅仅是由于系统管理的需要,而不是用户的需要。段是信息的逻辑单位,它含有一组其意义相对完整的信息。分段的目的是为了更好地满足用户的需要。
2.指令 一条指令或一个操作数可能会跨越两个页的分界处,而不会跨越两个段的分界处。
3.大小 页大小固定且由系统决定,把逻辑地址划分为页号和页内地址两部分,是由机器硬件实现的。段的长度不固定,且决定于用户所编写的程序,通常由编译系统在对源程序进行编译时根据信息的性质来划分。
4.逻辑地址表示 页式系统地址空间是一维的,即单一的线性地址空间,程序员只需利用一个标识符,即可表示一个地址。分段的作业地址空间是二维的,程序员在标识一个地址时,既需给出段名,又需给出段内地址。
5.查找 比页大,因而段表比页表短,可以缩短查找时间,提高访问速度。
需要几次访存?
两次,第一次访问的是段的起始地址,进而通过与偏移地址相加得到的是真实物理地址,第二次取出相应的数据或代码。
段页式内存管理
段页式内存管理,结合了分页分段的优点,在进行分段的基础上又进行了分页式的内存管理。
唯一缺点就是要求程序全部装入内存,没有足够的内存,程序就不能执行;
虚拟地址空间是一个mm_struct结构体,是操作系统为进程描述的一个完整线性以及连续的内存空间,实现了进程在物理地址的离散式存储,提高了内存访问率。通过页表段表对内存进行控制。
需要几次访存?
段页式系统中,须三次访问内存。第一次访问是访问内存中的段表,从中取得页表始址;第二次是访问内存中的页表,从中取出该页所在的物理块号,并将该块号与业内地址一起形成指令或数据的物理地址;第三次访问从第二次访问所得的地址中,取出指令或数据。
最后
以上就是淡定小刺猬为你收集整理的Linux地址空间进程地址空间什么是地址空间地址空间提出的原因?地址空间是怎么工作的?接下来深刻理解一下什么是进程地址空间的三种映射的全部内容,希望文章能够帮你解决Linux地址空间进程地址空间什么是地址空间地址空间提出的原因?地址空间是怎么工作的?接下来深刻理解一下什么是进程地址空间的三种映射所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复