2018-01-06 11:11:04 zhailihua 阅读数 1690
  • Linux0.11内核初始化讲解与调试

    结合bochs仿真调试工具边讲解边调试linux0.11版本内核初始化过程,通过该课程可以学习和理解关于linux内核在内存中的分布、分段及分页机制、逻辑地址线性地址及物理地址的区别,更系统的理解CPU实模式及保护模式等。课程会以下面几个部分逐次展开: 80386 CPU资源 80386 内存寻址机制 Linux多任务相关的TR寄存器和TSS任务状态段数据结构 中断和异常 Linux引导初始化--代码搬移过程 Bootsect代码详解 Bootsect代码详解续 bootsect里的设备号问题  bootsect代码解析之system模块搬移 setup.s程序详解 head.s程序详解 具体视频内容会根据讲解需要进行微调,详细内容参见csdn blog链接: https://blog.csdn.net/yuanfen5200/article/category/8692077

    344 人正在学习 去看看 靳庆庆
[appdeploy@CNSZ22PL0217:/home/appdeploy]$free -m
                  total             used         free        shared     buffers      cached
Mem:    258302          110198      148103          0        1001          79948
-/+ buffers/cache:        29248      229053

Swap:            0                  0               0

[appdeploy@CNSZ22PL0217:/home/appdeploy]$free -g
                  total       used       free     shared    buffers     cached
Mem:           252        107        144         0              0              78
-/+ buffers/cache:      28        223
Swap:               0          0          0

分析:以上数据说明该服务器的总物理内存为252G,已使用的内存107G,剩余144G为使用,其中物理内存使用=used-buffers-cached=107-0-78=29G,还剩252-29=223G可以使用,剩余较多


2019-09-07 14:14:31 qq_42685292 阅读数 339
  • Linux0.11内核初始化讲解与调试

    结合bochs仿真调试工具边讲解边调试linux0.11版本内核初始化过程,通过该课程可以学习和理解关于linux内核在内存中的分布、分段及分页机制、逻辑地址线性地址及物理地址的区别,更系统的理解CPU实模式及保护模式等。课程会以下面几个部分逐次展开: 80386 CPU资源 80386 内存寻址机制 Linux多任务相关的TR寄存器和TSS任务状态段数据结构 中断和异常 Linux引导初始化--代码搬移过程 Bootsect代码详解 Bootsect代码详解续 bootsect里的设备号问题  bootsect代码解析之system模块搬移 setup.s程序详解 head.s程序详解 具体视频内容会根据讲解需要进行微调,详细内容参见csdn blog链接: https://blog.csdn.net/yuanfen5200/article/category/8692077

    344 人正在学习 去看看 靳庆庆

 

方式一:free

直观的展示linux系统内存使用及交换区的容量大小等

方式二:top

动态实时查看系统中每一个进程的资源使用情况

方式三:cat /proc/meminfo

方式四:gnome-system-monitor 一个显示最近一段时间内的CPU、内存、交换区及网络使用情况的视图工具。

方式五:ps aux –sort -rss

各个线程的资源使用情况

方式六:vmstat -s

vmstat命令显示实时的和平均的统计,覆盖CPU、内存、I/O等内容。

2017-09-15 11:41:58 heliangbin87 阅读数 2577
  • Linux0.11内核初始化讲解与调试

    结合bochs仿真调试工具边讲解边调试linux0.11版本内核初始化过程,通过该课程可以学习和理解关于linux内核在内存中的分布、分段及分页机制、逻辑地址线性地址及物理地址的区别,更系统的理解CPU实模式及保护模式等。课程会以下面几个部分逐次展开: 80386 CPU资源 80386 内存寻址机制 Linux多任务相关的TR寄存器和TSS任务状态段数据结构 中断和异常 Linux引导初始化--代码搬移过程 Bootsect代码详解 Bootsect代码详解续 bootsect里的设备号问题  bootsect代码解析之system模块搬移 setup.s程序详解 head.s程序详解 具体视频内容会根据讲解需要进行微调,详细内容参见csdn blog链接: https://blog.csdn.net/yuanfen5200/article/category/8692077

    344 人正在学习 去看看 靳庆庆

内存访问分为两种体系结构:一致性内存访问(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->bdatapgdat = 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)

2018-07-26 22:02:24 kikilizhm 阅读数 10619
  • Linux0.11内核初始化讲解与调试

    结合bochs仿真调试工具边讲解边调试linux0.11版本内核初始化过程,通过该课程可以学习和理解关于linux内核在内存中的分布、分段及分页机制、逻辑地址线性地址及物理地址的区别,更系统的理解CPU实模式及保护模式等。课程会以下面几个部分逐次展开: 80386 CPU资源 80386 内存寻址机制 Linux多任务相关的TR寄存器和TSS任务状态段数据结构 中断和异常 Linux引导初始化--代码搬移过程 Bootsect代码详解 Bootsect代码详解续 bootsect里的设备号问题  bootsect代码解析之system模块搬移 setup.s程序详解 head.s程序详解 具体视频内容会根据讲解需要进行微调,详细内容参见csdn blog链接: https://blog.csdn.net/yuanfen5200/article/category/8692077

    344 人正在学习 去看看 靳庆庆

网上大部分的free 和cat /proc/meminfo 查看的是linux 可以分配的内存大小。

如果想查看linux整个系统分配的物理内存大小,可以通过cat /proc/iomem , 其中System Ram 就是uboot 给linux分配的物理内存空间大小。

root@localhost:/cfcard# cat /proc/iomem                                         
00000000-f7ffffff : System RAM    

2019-12-14 20:08:54 qq_39341113 阅读数 60
  • Linux0.11内核初始化讲解与调试

    结合bochs仿真调试工具边讲解边调试linux0.11版本内核初始化过程,通过该课程可以学习和理解关于linux内核在内存中的分布、分段及分页机制、逻辑地址线性地址及物理地址的区别,更系统的理解CPU实模式及保护模式等。课程会以下面几个部分逐次展开: 80386 CPU资源 80386 内存寻址机制 Linux多任务相关的TR寄存器和TSS任务状态段数据结构 中断和异常 Linux引导初始化--代码搬移过程 Bootsect代码详解 Bootsect代码详解续 bootsect里的设备号问题  bootsect代码解析之system模块搬移 setup.s程序详解 head.s程序详解 具体视频内容会根据讲解需要进行微调,详细内容参见csdn blog链接: https://blog.csdn.net/yuanfen5200/article/category/8692077

    344 人正在学习 去看看 靳庆庆

我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概念

Linux物理内存和虚拟内存

物理内存:系统硬件提供的真实内存大小
虚拟内存:为了解决物理内存的不足而提出的策略,它是利用磁盘空间虚拟出的一块逻辑内存,用作虚拟内存的磁盘空间称为交换空间
##物理内存和虚拟内存的关系
作为物理内存的扩展,Linux会在物理内存不足时,使用交换分区的虚拟内存。更详细说,内核会将暂时不用的内存块信息写到交换空间,这样,物理内存得到释放,这块内存就可以用于其它目的。当需要用到原始内容时,这些信息会被从新从交换空间读入物理内存
##内存分页存取机制
Linux的内存管理采用的是分页存取机制。为了保证物理能得到充分的利用,内核会在适当的时候将物理内存中不经常使用的数据库自动交换到虚拟内存中,而将经常使用的信息保留到物理内存中
Linux系统会不时进行页面交换操作,以保持尽可能多的空闲物理内存。即使并没有什么事情需要内存,Linux也会交换出暂时不用的内存页面,这可以避免等待交换所需时间

###页面交换条件
其次,Linux进行页面交换是有条件的,不是所有页面在不用时都交换到虚拟内存中,Linux内核根据“最近最经常使用”算法,仅仅将一些不经常使用的页面文件交换到虚拟内存中。有事我们会看到这么一个现象:Linux物理内存还有很多,但是交换空间也使用了很多。其实,这并不奇怪。例如,一个占用很大内存的进程运行时,需要耗费很多内存资源,因此就会有一些不常用页面文件被交换到虚拟内存中。但后来这个占用很多内存资源的进程技术并释放了很多内存时,刚才被交换出去的页面文件并不会自动交换进物理内存(除非有这个必要),那么此刻系统物理内存就会空闲很多,同时交换空间也在被使用,就出现了刚才所说的现象。关于这点,不用担心,只要知道是什么原因就行了

###页面交换错误情况
最后,交换空间的页面在使用时会首先被交换到物理内存中。如果此时没有足够的物理内存来容纳这些页面。它们又会被马上交换出去,如此一来,虚拟内存中可能没有足够空间来存储这些交换页面,最终导致Linux出现假死、服务异常等问题。Linux虽然可以在一段时间内自行恢复,但是恢复后的系统已经基本不可用了

因此,合理规划和设计linux内存的使用,是非常重要的

缓冲区(buffers)和缓存(cached)机制

在Linux 操作系统中,当应用程序需要读取文件中的数据时,操作系统先分配一些内存,将数据从磁盘读入到这些内存中,然后再将数据分发给应用程序;当需要往文件中写数据时,操作系统先分配内存接收用户数据,然后再将数据从内存写到磁盘上。然而,如果有大量数据需要从磁盘读取到内存或者由内存写入磁盘时,系统的读写性 能就变得非常低下,因为无论是从磁盘读数据,还是写数据到磁盘,都是一个很消耗时间和资源的过程,在这种情况下,Linux引入了缓冲区(buffers)和缓存(cached)机制

缓冲区与缓存都是内存操作,用来保存系统曾经打开过的文件以及文件属性信息,这样当操作系统需要读取某些文件时,会首先在buffers 与cached内存区查找,如果找到,直接读出传送给应用程序,如果没有找到需要数据,才从磁盘读取,这就是操作系统的缓存机制,通过缓存,大大提高了操 作系统的性能。但buffers与cached缓冲的内容却是不同的。

  • 缓冲区:缓冲区是用来缓冲块设备做的,它只记录文件系统的元数据(metadata)以及跟踪瞬时页面(tracking in-flight pages)
  • 缓存:缓存是用来给文件做缓冲。

更通俗一点说:缓冲区主要用来存放目录里面有什么内容,文件的属性以及权限等等。而缓存直接用来记忆我们打开过的文件和程序。

Linux操作系统的内存运行原理,很大程度上是根据服务器的需求来设计的,例如系统的缓冲机制会把经常使用到的文件和数据缓存在cached 中,linux总是在力求缓存更多的数据和信息,这样再次需要这些数据时可以直接从内存中取,而不需要有一个漫长的磁盘操作,这种设计思路提高了系统的整 体性能

物理内存和交换空间大小设置问题

没有更多推荐了,返回首页