linux获取进程首地址,Linux内核设计与实现(13)--进程地址空间

上一节讲了内核如何管理物理内存,其实内核除了管理本身的内存外,还必须管理用户空间中进程的内存,这就是进程地址空间,也就是系统中每个用户空间进程所看到的内存。

Linux采用虚拟内存技术,系统中所有进程之间以虚拟方式共享内存,对一个进程而言,可以访问整个系统的所有物理内存,其拥有地址空间也可以远远大于系统物理内存。

1.地址空间

进程地址空间由进程可寻址的虚拟内存组成。每个进程都有一个32bit或64bit的平坦地址空间,空间具体大小取决于体系结构。平坦(flat)指的是地址空间范围是一个独立的连续空间(比如,地址从0~232-1).一些操作系统提供了段地址空间,这种地址空间并不是一个独立的线性区域,而是被分段的。现代采用虚拟内存的操作系统都使用平坦地址空间。

每个进程都有唯一的这种平坦地址空间,并且不同进程之间,彼此互不相干,地址空间完全独立。

尽管一个进程可以寻址4GB虚拟内存(32bit),但这并不代表它就有权访问所有虚拟内存,在地址空间中,更关心的是一些可以合法访问的虚拟内存的地址空间,这个空间称为“内存区域(memory areas)”,进程只能访问有效内存区域内的内存地址,每个内存区域也具有相关权限,如可读、可写,可执行属性。如果一个进程访问了不在有效范围的内存区域,或以不正确的方式访问了有效地址,内核就会终止该进程,并返回“段错误”。

内存区域可以包含各种内存对象,比如:

代码段(text

section):可执行文件代码的内存映射

数据段(data

section):可执行文件的已初始化全局变量的内存映射

bss段:包含未初始化全局变量,也就是bss段的零页(页面中的信息全部为0,可用于映射bss段等目的)的内存映射

栈:用户进程用户空间的栈(不要和进程内核栈混淆,进程的内核栈独立存在并且由内核维护)每个诸如C库或动态链接程序等共享库的代码段、数据段和BSS也会被载入进程的地址空间。

任何内存映射文件;

任何共享内存段;

任何匿名的内存映射,比如malloc()分配的内存;

进程地址空间中的任何有效地址都只能位于唯一的区域,这些内存区域不能相互覆盖;在执行的进程中,每个不同的内存片段都对应一个独立的内存区域:栈、对象代码、全局变量、被映射的文件等。

2.内存描述符

内核使用内存描述符来表示进程的地址空间,该结构体包含了和进程地址空间有关的官不信息,mm_struct结构体,定义在

点击(此处)折叠或打开

struct mm_struct{

struct vm_area_struct*mmap;/*内存区域链表list of VMAs*/

struct rb_root mm_rb;/*VMA形成的红黑树*/

struct vm_area_struct*mmap_cache;/*最近使用的内存区域last find_vma result*/

#ifdef CONFIG_MMU

unsigned long(*get_unmapped_area) (struct file*filp,

unsigned long addr,unsigned longlen,

unsigned long pgoff,unsigned long flags);

void(*unmap_area) (struct mm_struct*mm,unsigned long addr);

#endif

unsigned long mmap_base;/*base of mmap area*/

unsigned long task_size;/*size of task vmspace */

unsigned long cached_hole_size;/* ifnon-zero,the largest hole below free_area_cache*/

unsigned long free_area_cache;/*地址空间第一个空洞first hole of size cached_hole_sizeorlarger*/

pgd_t*pgd; /*页全局目录*/

atomic_t mm_users;/*使用地址空间的用户数How many users with userspace? */

atomic_t mm_count;/*主使用计数器How many referencesto "struct mm_struct" (users count as 1) */

intmap_count;/*number of VMAs*/

struct rw_semaphore mmap_sem; /*内存区域的信号量*/

spinlock_t page_table_lock;/*页表锁Protects page tablesandsome counters*/

struct list_head mmlist;/*所有mm_struct形成的双向链表 List of maybe swapped mm's.These are globally strung

*together off init_mm.mmlist, andare protected

*by mmlist_lock

*/

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*/

/*

*Special counters, insome configurations protected by the

*page_table_lock, inother configurations by being atomic.

*/

struct mm_rss_stat rss_stat;

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.

* Inother words,this value gives an indication of how long

*it has been since this task got the token.

*Look at mm/thrash.c

*/

unsignedintfaultstamp;

unsignedinttoken_priority;

unsignedintlast_interval;

unsigned long flags; /*Must use atomic bitopstoaccess 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"pointstoa task thatisregarded as the canonical

*user/owner of this mm.All of the following must betrue in

*orderforittobe changed:

*

*current==mm->owner

*current->mm!=mm

*new_owner->mm==mm

*new_owner->alloc_lockisheld

*/

struct task_struct*owner;

#endif

#ifdef CONFIG_PROC_FS

/*store reftofile/proc//exe symlink pointsto */

struct file*exe_file;

unsigned long num_exe_file_vmas;

#endif

#ifdef CONFIG_MMU_NOTIFIER

struct mmu_notifier_mm*mmu_notifier_mm;

#endif

};

mm_users域记录正在使用该地址的进程数目,mm_count表示mm_struct结构体的主引用计数,当mm_users值减少为0时(所有使用该地址空间的线程都退出),mm_count变为0;当mm_count等于0,说明已经咩有人和指向该mm_stuct结构体的引用了,这时该结构体会被撤销。

mmap和mm_rb描述同一个对象:该地址空间中的全部内存区域。Mmap以链表形式存放,mm_rb以红-黑树形式存放。内核通常会避免用两种数据结构组织同一种数据,但此处这种冗余派的上用场,mmap链表,利于简单、高效地遍历所有元素;而mm_rb结构更适合搜索指定的元素。覆盖树上的链表并用这两个结构体同时访问相同的数据集,有时候这种操作称为线索树。

所有mm_stuct都通过自身的mmlist域链接在一个双向链表中,该链表首元素是init_mm内存描述符,它代表init进程的地址空间,另外注意,操作该链表是需要使用mmlist_lock来防止并发访问。

2.1分配内存描述符

在进程的进程描述符task_struct中,mm域存放着该进程使用的内存描述符,所以current->mm指向当前进程的内存描述符。

fork()函数利用copy_mm()函数复制父进程的内存描述符,而子进程中的mm_struct结构体实际是通过文件kernel/fork.c中的allocate_mm()宏从mm_cachep slab缓存中分配得到的。通常每个进程都有唯一的mm_struct结构体,即唯一的进程地址空间。

如果父进程希望和子进程共享地址空间,可以调用clone()时,设置CLONE_VM标志,这样的进程称作线程,Linux中所谓的线程和进程的本质区别,就是是否共享地址空间。

当CLONE_VM被指定后,内核就不再需要调用allocate_mm()函数,而仅仅需要在调用copy_mm()函数中将mm域指向其父进程的内存描述符就可以了:

点击(此处)折叠或打开

if (clone_flags&CLONE_VM) {

//current 是父进程,而tsk在fork()指向期间是子进程

atomic_inc(&current->mm->mm_users);

tsk->mm=current->mm;

}

2.2撤销内存描述符

进程退出时,内核会调用exit_mm(),该函数执行一些常规撤销工作,同时更新一些统计量。

该函数会调用mmput()减少内存描述符中的mm_users用户基数,如果用户计数降到0,将调用mmdrop()函数,减少mm_count使用计数。如果使用计数也等于零,说明内存描述符不再有任何使用者了,那么调用free_mm()宏通过kmem_cache_free()将mm_struct结构体归还到mm_cachep slab缓存中。

2.3 mm_struct与内核线程

内核线程没有进程地址空间,也没有相关的内存描述符,所以内核线程对应的进程描述符mm域为空,事实上,这也正是内核线程的真实含义—它们没有用户上下文。

为了避免内核线程为内存描述符和页表浪费内存,也为了当新内核线程运行时,避免浪费处理器周期向新地址空间进行切换,内核线程将直接使用前一个进程的内存描述符。

当一个进程被调度时,该进程的mm域指向的地址空间被载入内存,进程描述符中的active_mm域会被更新,指向新的地址空间。内核线程没有地址空间,mm域为NULL,于是,当一个内核线程被调度时,内核发现它的mm域为NULL,就会保留前一个进程的地址空间,随后更新内核线程的进程描述符的active_mm域,使其指向前一个进程的内存描述符。所以需要时,内核线程便可以使用前一个进程的页表。

3.虚拟内存区域

内存区域由vm_area_struct结构体描述,定义在中,内存区域在Linux内核中经常称作虚拟内存区域(virtual memoryAreas,

VMAs).

vm_area_struct描述了指定地址空间内连续区间上的一个独立内存范围,内核将每个内存区域作为一个单独的内存对象管理,每个内存区域都拥有一致的属性,比如访问权限等,相应的操作也应该一致。按这种方式,每个VMA就可以代表不同类型的内存区域(比如内存映射文件或者进程用户空间栈)。

点击(此处)折叠或打开

/*

*This struct defines a memory VMM memory area.Thereisone of these

*per VM-area/task.A VM areaisany part of the process virtual memory

* spacethat has a special ruleforthe page-fault handlers(ie a shared

*library,the executable area etc).

*/

struct vm_area_struct{

struct mm_struct*vm_mm;/*相关的mm_struct结构体The addressspacewe belongto. */

unsigned long vm_start;/*区间首地址Our start address within vm_mm. */

unsigned long vm_end;/*区间尾地址The first byte after ourendaddress

within vm_mm. */

/*linked list of VM areas per task,sorted by address*/

struct vm_area_struct*vm_next, *vm_prev; //VMA链表

pgprot_t vm_page_prot;/*访问控制权限Access permissions of this VMA. */

unsigned long vm_flags;/*标志:内存区域标志的信息和行为Flags,see mm.h. */

struct rb_node vm_rb; //树上该VMA的节点

/*

* Forareas with an addressspace andbacking store,

*linkage into the address_space->i_mmap prio tree, or

*linkagetothe list of like vmas hanging off its node, or

*linkage of vmainthe 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 raw_prio_tree_node prio_tree_node;

}shared;

/*

*A file's MAP_PRIVATE vma can beinboth i_mmap treeandanon_vma

*list,after a COW of one of the file pages.A MAP_SHARED vma

*can only beinthe i_mmap tree.An anonymous MAP_PRIVATE,stack

* orbrk vma(withNULLfile)can only beinan anon_vma list.

*/

struct list_head anon_vma_chain; /*Serialized by mmap_sem&

*page_table_lock*/

struct anon_vma*anon_vma;/*Serialized by page_table_lock*/

/* Functionpointerstodeal with this struct. */

conststruct vm_operations_struct*vm_ops; //相关的操作链表

/*Information about our backing store: */

unsigned long vm_pgoff;/*文件中偏移量 Offset(within vm_file) inPAGE_SIZE

units, *not*PAGE_CACHE_SIZE*/

struct file*vm_file;/*被映射的文件File we mapto (can beNULL). */

void*vm_private_data;/*私有数据was vm_pte(shared mem) */

unsigned long vm_truncate_count;/*truncate_countorrestart_addr*/

#ifndef CONFIG_MMU

struct vm_region*vm_region;/*NOMMU mapping region*/

#endif

#ifdef CONFIG_NUMA

struct mempolicy*vm_policy;/*NUMA policyforthe VMA*/

#endif

};

每个内存描述符都对应于进程地址空间中的唯一区间,vm_end-vm_start大小就是内存区间的长度。在同一地址空间内的不同内存区间不能重叠。

vm_mm域指向和VMA相关的mm_struct结构体,每个VMA对其相关的mm_struct结构体来说都是唯一的,如果两个线程共享一个地址空间,那么它们也同时共享其中所有的vm_area_struct结构体。

3.1VMA标志

VMA是一种位标志,它包含在vm_flags域内,标志了内存区域所包含的页面行为和信息。VMA标识反映了内核处理页面所需遵守的行为准则,而不是硬件要求。vm_flags包含了内存区域中每个页面的信息或内存区域的整体信息,而不是具体的独立页面。

cc634293896d3174ab84ea4a4da510ed.png

几个重要的标志(这些标志可以按需求组合):

VM_READ,VM_WRITE和VM_EXEC标志了区域中页面的读、写和执行权限。

VM_SHARD:指明内存区域包含的映射是否可以在多进程间共享,

VM_IO:标志内存区域中按对设备I/O空间的映射,该标志通常在设备驱动程序执行mmap()函数进行I/O空间映射时才被设置。

VM_SEQ_READ:标志内核应用程序对映射内容执行有序的(线性和连续的)读操作,这样内核可以有选择地执行预读文件。

3.2 VMA操作

Vm_area_struct结构体中的vm_ops域指向与指定内存区域相关的操作函数

点击(此处)折叠或打开

struct vm_operations_struct{

//当指定的内存区域被加入到一个地址空间时,该函数被调用

void(*open)(struct vm_area_struct*area);

//当指定的内存区域从地址空间删除时,该函数调用

void(*close)(struct vm_area_struct*area);

//当没有出现在屋里内存中的页面被访问时,该函数被页面故障处理调用

int (*fault)(struct vm_area_struct*vma,struct vm_fault*vmf);

/*notification that a previously read-only pageisabouttobecome

*writable, ifanerror isreturned it will cause a SIGBUS*/

//当某个页面为只读页面时,该函数被页面故障处理调用

int (*page_mkwrite)(struct vm_area_struct*vma,struct vm_fault*vmf);

/*called by access_process_vm when get_user_pages()fails,typically

* foruse by special VMAs that can switch between memoryandhardware

*/

//当get_user_pages()函数调用失败时,该函数被access_process_vm()函数调用

int (*access)(struct vm_area_struct*vma,unsigned long addr,

void*buf, int len, intwrite);

};

3.3内存区域的树型结构和内存区域的链表结构

mmap和mm_rb,独立地指向与内存描述符相关的全体内存区域对象,它们包含完全相同的vm_area_struct结构体指针,仅仅方法不同。

mmap域使用单独的链表链接所有的内存区域对象,每个vm_area_struct结构体通过自身vm_next域被连入链表,mmap域指向链表中的一个内存区域,链中最后一个结构体指针指向空

mm_rb域使用红-黑树链接所有内存区域对象,mm_rb指向红-黑树根节点,地址空间中每个vm_area_struct通过自身的vm_rb连接到树中。

链表用于需要遍历全部节点的时候,而红黑树适用于在地址空间中定位特定内存区域的时候,内核为了内存区域上的各种不同操作都能获得高性能,所以同时使用了这两种数据结构。

3.4实际使用中的内存区域

可使用/proc文件系统和pmap工具查看给定进程的内存空间和其中所含的内存区域。

点击(此处)折叠或打开

#include

intmain(void)

{

printf("hello,world!\r\n");

while (1);

return 0;

}

该程序执行pid是2874,那么

leon@ubuntu:~$ cat /proc/2874/maps

08048000-08049000 r-xp 00000000 08:01

131477     /home/leon/a.out

08049000-0804a000 r--p 00000000 08:01 131477     /home/leon/a.out

0804a000-0804b000 rw-p 00001000 08:01 131477     /home/leon/a.out

b7589000-b758a000 rw-p 00000000 00:00 0

b758a000-b772e000

r-xp 00000000 08:01 524970

/lib/i386-linux-gnu/libc-2.15.so

b772e000-b7730000 r--p001a4000 08:01 524970     /lib/i386-linux-gnu/libc-2.15.so

b7730000-b7731000 rw-p001a6000 08:01 524970     /lib/i386-linux-gnu/libc-2.15.so

b7731000-b7734000 rw-p 00000000 00:00 0

b7744000-b7747000 rw-p 00000000 00:00 0

b7747000-b7748000 r-xp 00000000 00:00

0          [vdso]

b7748000-b7768000 r-xp 00000000 08:01

524935     /lib/i386-linux-gnu/ld-2.15.so

b7768000-b7769000 r--p0001f000 08:01 524935     /lib/i386-linux-gnu/ld-2.15.so

b7769000-b776a000 rw-p 00020000 08:01 524935     /lib/i386-linux-gnu/ld-2.15.so

bfb5b000-bfb7c000 rw-p 00000000 00:00 0          [stack]

每行数据格式如下:

内存地址开始-结束 访问权限 偏移 主设备号:次设备号i节点 文件

或者用pmap命令查看

leon@ubuntu:~$ pmap 2874

2874:

./a.out

08048000      4K r-x--

/home/leon/a.out

08049000      4K r----

/home/leon/a.out

0804a000      4K rw---  /home/leon/a.out

b7589000      4K rw---    [ anon ]

b758a000   1680K r-x--

/lib/i386-linux-gnu/libc-2.15.so

b772e000      8K r----

/lib/i386-linux-gnu/libc-2.15.so

b7730000      4K rw---

/lib/i386-linux-gnu/libc-2.15.so

b7731000

12K rw---    [ anon ]

b7744000

12K rw---    [ anon ]

b7747000      4K r-x--    [ anon ]

b7748000

128K r-x--

/lib/i386-linux-gnu/ld-2.15.so

b7768000      4K r----

/lib/i386-linux-gnu/ld-2.15.so

b7769000      4K rw---

/lib/i386-linux-gnu/ld-2.15.so

bfb5b000    132K rw---

[ stack ]

total

2004K

分别表示程序和C库的代码段、数据段、bss段

进程全都地址空间大约2004KB,但只有大概不到200KB的内存区域是可写或私有的。如果一片内存范围是共享的或不可写的,那么内核只需要在内存中为文件保留一份映射,比如C库的代码,只读入一次是安全的。

由于内存未被共享,所以只要一有进程写该处数据,那么该处数据就将被拷贝出来(写时拷贝),然后才被更新。

每个和进程相关的内存区域都对应于一个vm_area_strcut结构体。

4.操作内存区域

内核时常需要在某个内存区域上执行一些操作,这些操作非常频繁,它们也是mmap()例程的基础,为了方便这类对内存区域的操作,内核定义了许多辅助函数声明在

4.1查找一个给定的内存地址属于哪一个内存区域: find_vma()

点击(此处)折叠或打开

/*Look up the first VMA which satisfies addr

struct vm_area_struct*find_vma(struct mm_struct*mm,unsigned long addr)

{

struct vm_area_struct*vma= NULL;

if (mm) {

/*Check the cache first. */

/* (Cache hit rateistypically around 35%.) */

vma=mm->mmap_cache;

if (!(vma&&vma->vm_end>addr&&vma->vm_start<=addr)) {

struct rb_node*rb_node;

rb_node=mm->mm_rb.rb_node;

vma= NULL;

while (rb_node) {

struct vm_area_struct*vma_tmp;

vma_tmp=rb_entry(rb_node,

struct vm_area_struct,vm_rb);

if (vma_tmp->vm_end>addr) {

vma=vma_tmp;

if (vma_tmp->vm_start<=addr)

break;

rb_node=rb_node->rb_left;

} else

rb_node=rb_node->rb_right;

}

if (vma)

mm->mmap_cache=vma;

}

}

return vma;

}

该函数在指定地址空间中搜索的一个vm_end大于addr的内存区域,这样返回的VMA首地址可能大于addr,所以指定的地址并不一定就包含在返回的VMA中。

因为很有可能在执行某个VMA操作后,其他操作还会对该VMA进行操作,所以find_vma()函数返回的结果被缓存在内存描述符的mmap_cache域中,实践证明,被缓存的VMA有相当好的命中率(30~40%),检查被缓存的VMA速度会很快,如果指定的地址不在缓存中,那么必须搜索和内存描述符相关的所有内存区域,这种搜索通过红黑树进行。

4.2查找第一个和指定地址区间相交的VMA:find_vma_intersection()

点击(此处)折叠或打开

/*Look up the first VMA which intersects the interval start_addr..end_addr-1,

NULL ifnone.Assume start_addr

static inline struct vm_area_struct*find_vma_intersection(struct mm_struct*mm,unsigned long start_addr,unsigned long end_addr)

{

struct vm_area_struct*vma=find_vma(mm,start_addr);

if (vma&&end_addr<=vma->vm_start)

vma= NULL;

return vma;

}

mm:要搜索的地址空间

start_addr:区间的起始地址

end_addr:区间尾地址

如果find_vma()返回NULL,那么find_vma_intersection()返回NULL;

如果find_vma()返回有效VMA,find_vma_intersection()只有在该VMA的起始位置于给定的地址区间结束位置之前,才将其返回,否者返回NULL

5.mmap()和do_mmap():创建地址区间

内核使用do_mmap()函数创建一个新的线性地址区间。如果这个新的VMA与相邻地址区间具有相同访问权限的话,将合并为一个VMA,如果不能合并,就确实需要创建一个新的VMA了。

无论如何,do_mmap()函数都会将一个地址区间加入到进程的地址空间中,无论是扩展已存在的内存区域还是创建一个新的区域。

在中定义

static inline unsigned long do_mmap(struct

file *file, unsigned long addr,

unsigned

long len, unsigned long prot,

unsigned

long flag, unsigned long offset)

该函数映射由file指定的文件,具体映射从文件偏移ofset开始,长度为len字节。如果file参数是NULL并且offset是0,那么代表这次映射没有和文件相关,这叫做匿名映射(anonymous mapping)。否则叫文件映射(file-backed mapping)。

addr是可选参数,它指定搜索空闲区域的起始位置。

prot参数指定内存区域中页面的访问权限。

58df4792a6f8c025fd4e4432b714f279.png

flag参数指定VMA标志,这些标志指定类型并改变映射的行为

cb6363ce933595b4d0628f1f234e05bb.png

如果系统调用do_mmap()的参数中有无效参数,它返回一个负值;否者,就会在虚拟内存中分配额一个合适的新内存区域(有可能从slab中获取)。

在用户空间通过调用mmap()系统调用获取内核do_mmap()的功能。

void *mmap2(void *addr,

size_t length,

int prot,

int flags,

int fd,

off_t pgoffset)

该系统调用是mmap()调用的第二种变种,所以起名为mmap2(),原始的mmap()方法的调用最后一个参数是字节偏移量,而mmap2()使用页面偏移量;mmap()调用由POSIX定义,C库中任然作为mmap()方法使用,但新内核中已经没有对应实现了,mmap()方法的调用是通过将字节偏移转化为页面偏移,从而转化为对mmap2()函数的调用来实现的。

6.mummap()和do_mummap():删除地址区间

do_mummap()从特定的进程地址空间中删除指定地址区间,定义在

int do_munmap(struct mm_struct *mm,

unsigned long start, size_t len)

从mm指定的用户空间,删除从地址start开始,长度为len字节的地址区间。

Itn munmap(void *start, size_t length)

该系统调用定义在文件mm/mmap.c中,它是对do_mummap()函数的一个简单封装:

点击(此处)折叠或打开

asmlinkage long sys_munmap(unsigned long addr,size_tlen)

{

intret;

struct mm_struct*mm;

mm=current->mm;

down_write(&mm->mmap_sem);

ret=do_munmap(mm,addr,len);

up_write(&mm->mmap_sem);

return ret;

}

7.页表

虽然应用程序操作的对象是对应虚拟内存,但处理器直接操作的却是物理内存,当应用程序访问一个虚拟地址时,首先必须将虚拟地址转化成物理地址,然后处理器才能解析地址访问请求。

Linux使用三级页表管理完成地址转换,可按需求在编译简化使用两级,用三级是利用“最大公约数”的思想---一种设计简单的体系结构。

每个进程都有自己的页表(线程会共享页表),内存描述符的pgd域指向的就是进程的页全局目录,注意,操作和检索页表时必须使用page_table_lock锁,该锁在相应进程的内存描述符中,防止竞争条件。

页表对应的结构体依赖于具体的体系结构,定义在

c980d0ca510636c233d389563679567e.png

由于几乎每次对虚拟内存的页面访问都必须先解析它,从而得到物理地址,所以页表操作的性能非常关键。但不幸的是搜索内存中的物理地址速度很有限,为了加快搜索,多数体系结构实现了一个翻译后缓存器(translate lookaside buffer, TLB)。TLB缓存虚拟地址到物理地址的映射,如果访问的虚拟地址在缓存中命中,物理地址立刻返回;否者就需要再通过页表搜索需要的物理地址。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值