内存访问分为两种体系结构:一致性内存访问(UMA)和非一致性内存访问(NUMA)。NUMA指CPU对不同内存单元的访问时间可能不一样,因而这些物理内存被划分为几个节点,每个节点里的内存访问时间一致,NUMA体系结构主要存在大型机器、alpha等,嵌入式的基本都是UMA。UMA也使用了节点概念,只是永远都只有1个节点。本文讲的是UMA模型的嵌入式平台,linux版本:3.10.y。

每个节点又将物理内存划分为3个管理区,在x86机器上管理区如下:

ZONE_DMA:0~16MB

ZONE_NORMAL:16MB~896MB

ZONE_HIGHMEM:896~末尾(对于64位,则不需要高端内存,虚拟地址足够直接映射)

而在hi3536嵌入式平台上实际的使用时由于系统mem没有超过896M,因此只有一个管理区ZONE_NORMAL(cat /proc/buddyinfo):

Node 0, zone   Normal     70    70     36     22    11     10     2      2      3     2     37

每个管理区管理该内存区域内的所有页面(linux中每个页面的大小为4KB)。

以下linux物理内存组织结构标准的关系图:

在Documentation/arm/memory.txt文件中定义了arm平台的线性地址空间布局情况,其与物理内存关系图:

当用户空间和内核空间比例3:1时,PAGE_OFFSET=0xC0000000。上面橙色虚线地址是一一映射关系,而高于highmem则需要vmalloc申请使用。

PHYS_OFFSET=0x40000000

ZRELADDR == virt_to_phys(PAGE_OFFSET +TEXT_OFFSET) = virt_to_phys (TEXT_ADDR) = 0x40008000

INITRD_PHYS= 0x00800000

PARAMS_PHYS= 0x00000100

high_memory和VMALLOC_START之间保留了8M空间间隙

high_memory=PAGE_OFFSET+ highmem

当系统内存在0~896MB时,highmem=系统内存

当系统内存>896MB时,highmem=896MB,多余的内存需要vmalloc映射访问

VMALLOC_START和VMALLOC_END-1之间用于vmalloc() / ioremap() space映射。

cat /proc/vmallocinfo:

0xfb000000-0xfe190000 51970048iotable_init+0x0/0xb0 phys=10000000 ioremap

映射了CPU寄存器空间

1、节点

每个节点由struct pglist_data定义(include/linux/mmzone.h):

typedef struct pglist_data {

#define MAX_NR_ZONES 3

struct zonenode_zones[MAX_NR_ZONES]; //分别代表3个管理区

#define MAX_ZONELISTS 1

struct zonelistnode_zonelists[MAX_ZONELISTS]; //按照分配时的管理区顺序排列,如果在ZONE_HIGHMEM中分配失败,就有可能还原成ZONE_NORMAL或ZONE_DMA。

int nr_zones;   //表示管理区的数目,值为1、2、3。

struct page *node_mem_map; //指向该节点第一个物理页面

struct bootmem_data*bdata; //指向内存引导程序

unsigned longnode_start_pfn; //该节点的起始页编号

//calculate_node_totalpages中对以下两个值进行计算

unsigned long node_present_pages;/* total number of physical pages */

unsigned longnode_spanned_pages; /* total size of physical pagerange, including holes */

int node_id;      //节点号,在嵌入式上一般为0

nodemask_treclaim_nodes;   /* Nodes allowed toreclaim from */

wait_queue_head_tkswapd_wait;         //交换守护进程kswapd使用的等待队列

wait_queue_head_tpfmemalloc_wait;

struct task_struct*kswapd;  //指向交换守护进程描述符

int kswapd_max_order;

enum zone_type classzone_idx; //管理区类型

} pg_data_t;

include/linux/mmzone.h中定义了全局节点:

extern struct pglist_data contig_page_data;

#define NODE_DATA(nid)     (&contig_page_data)

mm/bootmem.c进行全局节点定义:

struct pglist_data __refdata contig_page_data = {

.bdata =&bootmem_node_data[0]

};

EXPORT_SYMBOL(contig_page_data);

2、管理区

每个管理区由structzone描述(include/linux/mmzone.h):

struct zone {

unsigned longwatermark[NR_WMARK]; //该管理区的三个水平线值,min, low, high

unsigned long percpu_drift_mark;

unsigned long       lowmem_reserve[MAX_NR_ZONES]; //每个管理区必须保留的页框数

unsigned long       dirty_balance_reserve;

struct per_cpu_pageset __percpu*pageset;         //CPU的页面缓存

spinlock_t      lock;     //保护该管理区的自旋锁

int            all_unreclaimable; /* All pagespinned */

struct free_area    free_area[MAX_ORDER]; //通过伙伴算法管理的空闲页面

ZONE_PADDING(_pad1_)

spinlock_t      lru_lock;

struct lruvec       lruvec;

unsigned long       pages_scanned; //管理区回收页框时使用的计数器,记录上一次回收一同扫描过的页框

unsigned long       flags;         /* zone flags, see below */

/* Zone statistics */

atomic_long_t       vm_stat[NR_VM_ZONE_STAT_ITEMS];

unsigned int inactive_ratio;

ZONE_PADDING(_pad2_)

wait_queue_head_t   * wait_table; //进程等待的散列表,这些进程正在等待管理区中的某页

unsigned long       wait_table_hash_nr_entries; //散列表数组的大小

unsigned long       wait_table_bits; //散列表数组的大小对2取log的结果

struct pglist_data  *zone_pgdat; //管理区属于的节点

/* zone_start_pfn ==zone_start_paddr >> PAGE_SHIFT */

unsigned long       zone_start_pfn;  //管理区的起始页号

unsigned long       spanned_pages; //管理区的大小包括洞

unsigned long       present_pages; //管理区的大小不包括洞

unsigned long       managed_pages;

const char      *name; //管理区名字,DMA、NORMAL orHIGHMEM

}

当系统中的可用内存很少时,守护程序kswapd被唤醒释放页面。每个管理区通过数组watermark来决定唤醒还是睡眠kswapd守护进程,这个数组通过下面的枚举来分别代表page_min, page_low, page_high,他们的之间的关系图如下图:

enum zone_watermarks {

WMARK_MIN,

WMARK_LOW,

WMARK_HIGH,

NR_WMARK

};

page_low:当空闲页面数达到pages_low时,伙伴算法分配器就会唤醒kswapd释放页面。

page_min:当达到pages_min时,kswap没唤醒则唤醒,同时同步进程释放内存,如果申请内存远远超过实际内存,就会出现out_of_memory。page_min包含里管理区最小保留内存,因此这是GFP_ATOMIC还可以分配出内存。

page_high:当释放页面达到这个值,认为该管理区已经平衡,kswapd开始睡眠。

当多个进程对同一个页面进行IO操作(个人理解觉得更应该是有写操作)时,比如页面换入或换出,为了防止访问数据的不一致性,该页面会被锁住,而其他进程则通过wait_on_page()函数被添加到等待队列中。如果每个页面都有等待队列则系统会花费大量的内存存放,linux则是将等待队列存储在管理区的wait_table散列表中,这样就只有一个等待队列。其简单流程图如下:

当申请分配标志为GFP_ATOMIC时则不会睡眠,因为该标志不能睡眠的内存分配标志,用在中断处理程序、下半部、持有自旋锁以及其他不能睡眠的地方。

3、物理页面

系统中的每个物理页面都由structpage用以记录该页面的状态,结构体包含了很多union,因为现在有slab/slob/slub三种分配器使用其中一种即可,定义如下(include/linux/mm_types.h):

struct page {

/* First double word block */

unsigned long flags;         //存放页的状态

struct address_space*mapping; //如果低位是clear的,则内存映射到文件或设备,它指向文件或设备节点inode的address_space,或者NULL;如果内存映射到匿名则低位被设置,指向匿名对象objects。

/* Second double word*/

struct {

union {

pgoff_tindex;     //页面是文件映射的一部分,它就是页面在文件中的偏移;页面是交换高速缓存一部分,它就是在交换地址空间中address_space的偏移量

void*freelist;     //指向slub/slob第一个空闲对象

boolpfmemalloc;

};

union {

unsigned counters;

struct {

union {

atomic_t_mapcount;

struct {/* SLUB */

unsigned inuse:16;

unsigned objects:15;

unsigned frozen:1;

};

intunits;  /* SLOB */

};

atomic_t _count; //页的引用计数

};

};

};

/* Third double word block*/

union {

struct list_headlru;   //最近最少使用(LRU)链表的链表首部

struct {        /* slub per cpu partial pages */

struct page*next;  /* Next partial slab */

short int pages;

short intpobjects;

};

struct list_headlist;  //slob列表

struct slab *slab_page;/* slab fields */

};

/* Remainder is not doubleword aligned */

union {

unsigned long private;

struct kmem_cache*slab_cache;  /* SL[AU]B: Pointer to slab*/

struct page*first_page;    /* Compound tail pages */

};

#if defined(WANT_PAGE_VIRTUAL)

void *virtual;          /* Kernel virtual address (NULL if

notkmapped, ie. highmem) */

#endif /* WANT_PAGE_VIRTUAL */

}

Flags的最高ZONES_SHIFT位记录该页面所属的管理区。set_page_zone函数设置页面的管理区。

4、源码解析

linux内核的内存管理分三个阶段。
A. 启动---->bootmem初始化完成为第一阶段。此阶段只能使用memblock_reserve函数分配内存。
B. bootmem初始化完--->buddy完成前。该阶段使用引导内存分配器(boot memoryallocator)分配内存。
C. 全部内存初始化完毕,可以用cache和buddy分配内存。

(1)~(4)点完成第一阶段,该阶段主要在start_kernel—> setup_arch函数实现:

(1)获取总内存

parse_early_param—> early_mem函数根据uboot传递进来的命令行参数mem=size@start计算出起始和内存大小通过arm_add_memory添加到meminfo全局数组里。

这里需要注意parse_early_param之前setup_machine_tags里parse_tags中的parse_tag_mem32也会解析uboot通过tags方法传递进来的系统内存,但是被early_mem覆盖重新计算。

(2)计算高端内存

sanity_check_meminfo(arch/arm/mm/mmu.c)对meminfo里的内存进行判断,是否需要划分为高端内存。

(3)计算保留内存块

保留内存块包括内核(数据段,代码段等)、页目录等占用的内存块。通过arm_memblock_init(arch/arm/mm/init.c)实现:

A. 将meminof内存通过memblock_add加到memblock.memory类型内存块里(包含全部物理内存)

B.通过memblock_reserve将保留内存添加到memblock.reserved类型内存块里

memblock_reserve(__pa(_stext), _end -_stext); 内核通过查看System.map可以看到

arm_mm_memblock_reserve(); //页表

最终结果显示如下:

MEMBLOCK configuration:

memory size = 0xfa00000(250M) reservedsize = 0x6ba3cc

memory.cnt = 0x1

memory[0x0]   [0x00000040000000-0x0000004f9fffff], 0xfa00000 bytes

reserved.cnt = 0x2

reserved[0x0] [0x00000040004000-0x00000040007fff], 0x4000 bytes //页表

reserved[0x1] [0x000000400081c0-0x000000406be58b], 0x6b63cc bytes //内核

在伙伴算法完成之前,内存的分配通过该方式进行。

(4)paging_init

该函数arch/arm/mm/mmu.c实现,完成页表设置、管理区zone、设置零页等。

/*

*paging_init() sets up the page tables, initialises the zone memory

*maps, and sets up the zero page, bad page and bad page tables.

*/

void __init paging_init(struct machine_desc*mdesc)

{

void *zero_page;

memblock_set_current_limit(arm_lowmem_limit);

build_mem_type_table();//建立各种类型页表的属性(内存MEMORY类型,设备DEVICE,中断向量表是HIGH_VECTORS),根据不同arm体系进行初始化,这是因为地址转换由MMU硬件单元根据页表处理,每个体系的MMU单元有差别(不是很明白)。

/*以下见第二章页表管理*/

prepare_page_table(); //清除在这之前建立的临时页表,以便下面建立正式链表

map_lowmem(); //为低端内存建立一一的映射表(0~ arm_lowmem_limit),存放在swapper_pg_dir位置,用到前面的页表类型:MT_MEMORY

dma_contiguous_remap(); //建立DMA映射表,类型为:MT_MEMORY_DMA_READY

devicemaps_init(mdesc); //完成ffff0000开始的中断向量映射表,完成CPU IO映射表(从0x10000000~0x13190000就是控制器地址空间)、刷新一下TLB

kmap_init(); //申请kmap高端内存永久映射的页表项,虚拟空间为2M(0xbfe00000 - 0xc0000000),如果未定义高端内存(CONFIG_HIGHMEM)什么也不做。

tcm_init(); //do nothing

//关于页表见第二章,此处返回中断向量表0xffff0000在pgd中的偏移量

top_pmd = pmd_off_k(0xffff0000);

/* allocate the zero page. */

zero_page = early_alloc(PAGE_SIZE);

bootmem_init(); //见下一节重点分析

//一种特殊的页,供初始化为0的数据和写时复制使用

empty_zero_page = virt_to_page(zero_page);

__flush_dcache_page(NULL, empty_zero_page);

}

(4-1)bootmem_init

在arch/arm/mm/init.c中定义,完成buddy管理内存需要的工作。

void __init bootmem_init(void)

{

unsigned longmin, max_low, max_high;

max_low = max_high = 0;

find_limits(&min,&max_low, &max_high); //最小物理页号,低端内存最大物理页号,高端内存最大物理页号

/* 申请低端内存所需位图的空间,然后赋值给节点pgdat->bdata(pgdat = NODE_DATA(0);只有一个节点,实际为一个全局变量contig_page_data)并将位图所在内存空间保留(memblock.reserved),bdata->node_bootmem_map指向位图空间(bdata 为struct bootmem_data);先将memblock.memory内存(所有物理内存)的位图清零—表示未使用,再将memblock.reserved 内存的位图置1—表示使用

node_bootmem_map:cf9f7000,该部分内存空间在建立伙伴系统的时候释放,所以放在内存的末端

*/

arm_bootmem_init(min, max_low);

arm_memory_present();  //do nothing

sparse_init();//do nothing

arm_bootmem_free(min, max_low, max_high); //见下一节介绍

max_low_pfn =max_low - PHYS_PFN_OFFSET; //低端内存的物理页数目

max_pfn = max_high- PHYS_PFN_OFFSET; //高端内存的物理页数目

}

(4-2)arm_bootmem_free

该函数(arch/arm/mm/init.c)先计算zone_size和zhole_size,然后调用free_area_init_node(mm/page_alloc.c),因此这里主要分析free_area_init_node函数:

void __paginginit free_area_init_node(intnid, unsigned long *zones_size,

unsigned long node_start_pfn, unsigned long *zholes_size)

{

pg_data_t *pgdat = NODE_DATA(nid);

pgdat->node_id = nid;

pgdat->node_start_pfn = node_start_pfn;

init_zone_allows_reclaim(nid);

//计算节点的node_spanned_pages和node_present_pages,没有高端内存两者相等

calculate_node_totalpages(pgdat, zones_size, zholes_size);

//申请所有物理页描述结构体(structpage)所需要的内存空间,mem_map= NODE_DATA(0)->node_mem_map指向该空间

alloc_node_mem_map(pgdat); //空间大小:page size:200000//2M

printk("free_area_init_node: node %d, pgdat %08lx, node_mem_map%08lx\n",

nid, (unsigned long)pgdat,  (unsigned long)pgdat->node_mem_map);

free_area_init_node:node 0, pgdat c06535c0(内核数据区), node_mem_map c06bf000(内核bss后面申请的一个内存地址)

free_area_init_core(pgdat, zones_size, zholes_size); //见下节

}

(4-3)free_area_init_core

该函数(mm/page_alloc.c)实现如下:

static void __paginginitfree_area_init_core(struct pglist_data *pgdat,

unsigned long *zones_size, unsigned long *zholes_size)

{

enum zone_type j;

int nid = pgdat->node_id;

unsigned long zone_start_pfn = pgdat->node_start_pfn;

int ret;

//初始化自旋锁和相关等待队列

pgdat_resize_init(pgdat);

init_waitqueue_head(&pgdat->kswapd_wait);

init_waitqueue_head(&pgdat->pfmemalloc_wait);

pgdat_page_cgroup_init(pgdat);

//初始化各个管理区

for (j = 0; j < MAX_NR_ZONES; j++) {

struct zone *zone = pgdat->node_zones + j;

unsigned long size, realsize, freesize, memmap_pages;

/*重新计算管理区的大小(减去struct page所占用的内存空间)*/

size = zone_spanned_pages_in_node(nid, j, zones_size);

realsize = freesize = size - zone_absent_pages_in_node(nid, j,

zholes_size);

memmap_pages = calc_memmap_size(size, realsize);

if (freesize >= memmap_pages) {

freesize -= memmap_pages;

}

if (!is_highmem_idx(j))

nr_kernel_pages += freesize;

/* Charge for highmem memmap if there are enough kernel pages */

else if (nr_kernel_pages > memmap_pages * 2)

nr_kernel_pages -= memmap_pages;

nr_all_pages += freesize;

zone->spanned_pages = size;

zone->present_pages = realsize;

/*

* Set an approximate value for lowmem here, it will be adjusted

* when the bootmem allocator frees pages into the buddy system.

* And all highmem pages will be managed by the buddy system.

*/

zone->managed_pages = is_highmem_idx(j) ? realsize : freesize;

zone->name = zone_names[j];

//初始化其他自旋锁

spin_lock_init(&zone->lock);

spin_lock_init(&zone->lru_lock);

zone_seqlock_init(zone);

//指向节点

zone->zone_pgdat = pgdat;

zone_pcp_init(zone); //初始化CPU页面缓存

lruvec_init(&zone->lruvec); //初始化lru

if (!size)

continue;

set_pageblock_order();

setup_usemap(pgdat, zone, zone_start_pfn, size);

//初始化该管理区的wait_table和free_area(伙伴算法)

ret = init_currently_empty_zone(zone, zone_start_pfn,

size, MEMMAP_EARLY);

BUG_ON(ret);

//初始化该管理区的所有物理页结构体struct page

memmap_init(size, nid, j, zone_start_pfn);

zone_start_pfn += size;

}

}

(5)setup_per_cpu_areas

在init/main.c的start_kernel调用,在mm/percpu.c中定义,只有在SMP系统下才有用,在UP不做任何处理。

该函数主要是为每个处理器设置per-cpu数据区域,per_cpu数据由各个CPU独立使用,即使不锁访问,十分有效。per-cpu数据按照不同的CPU类型使用,以将性能低下引发的缓存一致性问题减小到最小。

(6)build_all_zonelists

在init/main.c的start_kernel调用,在mm/page_alloc.c中定义。初始化每个节点内的zonelists。

(7)mm_init 设置内存分配器

在init/main.c中定义,主要用于设设置伙伴算法内存分配器。源码如下:

static void __init mm_init(void)

{

/*

* page_cgroup requires contiguous pages,

* bigger than MAX_ORDER unless SPARSEMEM.

*/

page_cgroup_init_flatmem();

mem_init(); //见下面

kmem_cache_init(); //建立kmem_cache和kmem_cache_node两个高速缓存(slab分配器使用)。

percpu_init_late();

pgtable_cache_init(); //do nothing

vmalloc_init(); //vmalloc分配的内存虚拟地址连续,而物理地址无需连续,这里初始vmalloc要用的相关链表等准备工作。

}

void __init mem_init(void)

{

unsigned long reserved_pages, free_pages;

struct memblock_region *reg;

int i;

max_mapnr   = pfn_to_page(max_pfn+ PHYS_PFN_OFFSET) - mem_map;

//以下两者都是释放空闲内存到伙伴系统:memblock的空闲内存,后者是bootmem的空闲内存,其实两者有交叠,伙伴系统会尝试与前后连续页框组成更大的页框块。

/* bootmem分配器核心就是node_bootmem_map这个位图,每一位代表这个node的一个页,当需要分配时就会扫描这个位图,然后获取一段物理页框进行分配,一般都会从开始处向后进行分配,并没有什么特殊的算法在其中。而伙伴系统初始化时页会根据这个位图,将位图中空闲的页释放回到伙伴系统中,而已经分配出去的页则不会在初始化阶段释放回伙伴系统,不过有可能会在系统运行过程中释放回伙伴系统中。*/

free_unused_memmap(&meminfo); //根据代码只有一个bank,所以没做啥事情

totalram_pages+= free_all_bootmem();

//释放高端内存到伙伴系统

free_highpages();

reserved_pages = free_pages = 0;

//统计空闲内存和保留内存并打印出来

for_each_bank(i, &meminfo) {

struct membank *bank = &meminfo.bank[i];

unsigned int pfn1, pfn2;

struct page *page, *end;

pfn1 = bank_pfn_start(bank);

pfn2 = bank_pfn_end(bank);

page = pfn_to_page(pfn1);

end  = pfn_to_page(pfn2 - 1) + 1;

do {

if (PageReserved(page))

reserved_pages++;

else if (!page_count(page))

free_pages++;

page++;

} while (page < end);

}

}

(8)伙伴算法建立后物理内存分布

经过上面源码分析后,下图是该阶段物理内存分布情况:

4000:内核全局页表swapper_pg_dir起始地址,共16K

8000~6be58c:内核存放空间(text\data\bss等)

6bf000~6bf000+200000:存放所有页框描述数据结构structpage

f9f7000:存放早期bootmem分配内存使用的位图空间起始地址,建立伙伴算法后会释放掉

916000~fa00000:空闲页表,全部有伙伴系统管理

前面的9304K保留内存永远不释放,也不归伙伴系统管理

内核信息打印如下:

Memory: 250MB = 250MB total

Memory: 246696k/246696k available, 9304k reserved, 0

Virtual kernel memory layout:

vector  : 0xffff0000 - 0xffff1000   (   4kB)

fixmap  : 0xfff00000 - 0xfffe0000   ( 896 kB)

vmalloc: 0xd0000000 - 0xff000000   ( 752 MB)

lowmem  : 0xc0000000 - 0xcfa00000   ( 250 MB)

pkmap   : 0xbfe00000 - 0xc0000000   (   2MB)

modules: 0xbf000000 - 0xbfe00000   (  14 MB)

.text: 0xc0008000 - 0xc05e85c8   (6018 kB)

.init: 0xc05e9000 - 0xc0617ec0   ( 188 kB)

.data: 0xc0618000 - 0xc06542c0   ( 241 kB)

.bss: 0xc06542c0 - 0xc06be58c   ( 425 kB)

linux内核虚拟内存之物理内存相关推荐

  1. Linux为什么虚拟内存大于物理内存?

    对于精通 CURD 的业务同学,内存管理好像离我们很远,但这个知识点虽然冷门(估计很多人学完根本就没机会用上)但绝对是基础中的基础. 这就像武侠小说中的内功修炼,学完之后看不到立竿见影的效果,但对你日 ...

  2. linux内核虚拟内存之slub分配器

    上一章主要讲述以页为最小单位进行内存分配的伙伴管理算法,较大程度上避免了内存碎片问题.而实际上对内存的申请却不是每次都申请一个页面的(比如文件节点,任务描述符等结构体内存),通常是远小于一个内存页面的 ...

  3. 谈谈Linux内核物理内存与虚拟内存之间的映射(超详细~)

    1.用户编制程序时使用的地址称为虚地址或逻辑地址,其对应的存储空间称为虚存空间或逻辑地址空间:而计算机物理内存的访问地址则称为实地址或物理地址,其对应的存储空间称为物理存储空间或主存空间. 2.虚拟存 ...

  4. Linux内核虚拟地址空间,-3G的由来。各个进程的虚拟内存4G,内核总在3-4G。内核的虚拟空间地址-3G,总是指向物理内存的0-1G地址,各个进程的虚拟内核共享这个物理内存

    Linux内核地址空间划分 通常 32 位 Linux 内核地址空间划分 0~3G 为用户空间,3~4G 为内核空间.64 位内核地址空间划分是不同的. Linux内核高端内存 当内核模块代码或线程访 ...

  5. linux内核dma内存分配,Linux 4.x 内核空间 DMA 虚拟内存地址

    Architecture: i386 32bit Machine Ubuntu 16.04 Linux version: 4.15.0-39-generic 目录 DMA 虚拟内存区 在 IA32 体 ...

  6. linux 物理内存用完了_调整linux内核尽量用内存,而不用swap

    线上一台服务器kswapd0占用大量的cpu资源,导致负载过高,什么是kswapd0? Linux uses kswapd for virtual memory management such tha ...

  7. 操作系统——Linux 虚拟内存和物理内存的理解

    虚拟内存: 第一层理解 1..每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构: 2.一个新进程建立的时候,将会建立起自己的内存空间,此进程的数据,代码等从磁盘拷贝到自己的进程空间 ...

  8. linux的虚拟内存是4G,而每个进程都有自己独立的4G内存空间,怎么理解?进程虚拟地址4G指拥有4G的寻址能力,需要页表转换为实际物理地址,每个进程用到的内核是直接映射,地址的进程地址-3G的关系

    linux的虚拟内存是4G,而每个进程都有自己独立的4G内存空间,怎么理解? 问: linux的虚拟内存是4G,而每个进程都有自己独立的4G内存空间,怎么理解? 每个进程所拥有的4G独立的虚拟内存空间 ...

  9. linux内核x86内存,Linux内核在X86上的虚拟内存管理

    前言 Linux支持很多硬件运行平台,常用的有:Intel X86,Alpha,Sparc等.对于不能够通用的一些功能,Linux必须依据硬件平台的特点来具体实现.本文的目的是简要探讨Linux在X8 ...

  10. Linux 内存管理 详解(虚拟内存、物理内存,进程地址空间)

    Linux -操作系统内存管理 存储系统 存储器的层次结构 Linux的内存管理 物理内存 物理内存管理 虚拟内存 虚拟地址空间 (写时拷贝) 和物理地址映射关系 页表 虚拟内存优缺点 「在 4GB ...

最新文章

  1. pod setup慢的解决方法
  2. docker查询占用端口号服务命令
  3. V100服务器和T4服务器的性能指标
  4. ci phpexcel mysql_PHPExcel导入数据到mysql数据库
  5. java定时任务中使用多线程_java项目中如何利用多线程实现一个定时器任务
  6. 64bit centos 如何通过yum安装32bit的程序
  7. Java swing 代码例子
  8. 又要头秃?2020 年七大 AI 编程语言大盘点
  9. python xlwt操作excel
  10. 浏览器野史 UserAgent列传
  11. python圣诞节快乐_圣诞节快乐,利用Python给自己的微信头像添加一个圣诞帽
  12. 各类编程语言教程合集
  13. lenovo启动热键_联想启动热键
  14. 调试的时候没有在断点处停止的原因
  15. STM32 - 解决一次下位机偶校验收包乱码的问题
  16. MATLAB实现正弦频谱分析学习笔记
  17. 常用淘宝api 地址
  18. 2012 比较全的工资待遇搜集
  19. 云计算机房标准,主编解读Vol.3|GB50174-2017《数据中心设计规范》:A级数据中心的性能要求...
  20. maxcms支持mysql吗_在自己电脑上使用马克斯maxcms程序制作电影网站

热门文章

  1. 读吴恩达算-EM算法笔记
  2. Web系统Login拦截器
  3. 20155213 实验三《敏捷开发与XP实践》实验报告
  4. windows命令行快速启动软件
  5. Slider 滑动条效果
  6. html中文本域选中后会出现蓝边框
  7. shell脚本使用getopts自定义传入参数选项
  8. H5 data-* 属性,设置获取方法总结
  9. how to get keyboard key with non blocking in terminal
  10. 《R in Action》读书笔记(2)