内存管理

一、程序的编译链接过程

1、预处理

主要处理一下几个方面内容:

①宏定义    ②文件包含    ③条件编译    ④去掉注释

2、编译

把源代码转换成相应的汇编语言的过程

3、汇编

把汇编语言转换成二进制代码,即目标程序

4、链接

将多个目标程序连同库文件(静态库、动态库)一起整合成一个可执行文件,可以被操作系统载入内存执行。

☆☆☆在这个过程有一个重要的过程:产生用来表示操作数或指令的地址逻辑地址    《深入理解计算机系统》

二、虚拟内存空间的映射

        当我们运行可执行文件时,操作系统把它加载到内存,经过一系列的初始化、设置相关寄存器的值等动作建立一个进程,放入队列等待内核调度被CPU执行。

        首先我们理解可执行文件中的逻辑地址是如何被映射到物理地址的?

   

逻辑地址由两部分组成:段标识符、偏移量

1、段标识符是一个16位长的字段

2、偏移量是一个32位长的字段

段标识符的信息被存储在段寄存器中,内核在建立一个进程时都要将其段寄存器设置好。

CPU中设置了6个段寄存器,其中3个有专门的用途:

cs    代码段寄存器,指向包含程序指令的段

ss    栈段寄存器,指向包含当前程序栈的段

ds    数据段寄存器,指向包含静态数据或全局数据的段

段寄存器的格式如下:


各字段的含义:

Index:指明段描述符项的索引号

TI:指明段描述符是在全局描述符表GDT中还是局部描述表LDT中

RPL:CPU当前的特权级(0——内核态    3——用户态)


逻辑地址转换为线性地址的过程:(如下图示意过程)

①首先从段寄存器中取得Index

②从寄存器gdtr或ldtr取得GDT或LDT的地址(由TI字段指定,对Linux总是选择GDT),通过Index可以得到具体的段描述符项

③从段描述符可以得到线性地址的基址,基址+偏移量——>线性地址


对Linux而言,从段描述符得到的线性地址的基址为0X0,所以逻辑地址总是把自身映射到线性地址。

Linux采用页式内存管理,它的段式内存映射只是为了与兼容特定的硬件(如Intel的i386)

三、页式内存管理(线性地址——>物理地址)

对于32位系统来说,Linux采用两级分页


寻址过程:

①cr3寄存器保存页目录的基址,线性地址的DIRECTORY字段记录了目录项的偏移量,基址+偏移量就可以找到对应的目录项

②目录项里记录了页表的基址,线性地址的TABLE字段记录了表项的偏移量,基址+偏移量就可以找到对应的表项

③表项中记录了物理地址的基址,线性地址的OFFSET字段记录了物理地址的偏移量,基址+偏移量就可以找到对应的物理地址


对于64位系统来说,Linux采用三级或四级分页,依赖具体的硬件平台



以下是内核对物理空间的管理以及相应的数据结构

2、内核中把物理地址划分成许多物理页,每个物理页定义成一个page数据结构

struct page {
	page_flags_t flags;		/* Atomic flags, some possibly
					 * updated asynchronously */
	atomic_t _count;		/* Usage count, see below. */
	atomic_t _mapcount;		
	unsigned long private;		
	struct address_space *mapping;	
	pgoff_t index;			/* Our offset within mapping. */
	struct list_head lru;		
	
#if defined(WANT_PAGE_VIRTUAL)
	void *virtual;			/* Kernel virtual address (NULL if
					   not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
};
3、物理内存就被表示成一个很大的page结构数组,并定义一个全局指针变量指向该数组。类似于下面这样:
struct page *mem_map;
struct page physical_page[PAGE_NUM];
mem_map = physical_page;

系统中的每一个物理页都有一个page结构。系统在初始化时根据物理内存的大小建立一个page结构数组,作为物理页面的“仓库”,里面的每个page结构元素都代表着一个物理页面。

4、在多核处理器体系结构中,整个物理内存又被分为很多节点,每个CPU都有自己的专属的内存节点

节点对应的数据结构为pglist_data

typedef struct pglist_data {
	struct zone node_zones[MAX_NR_ZONES];
	struct zonelist node_zonelists[GFP_ZONETYPES];
	int nr_zones;
	struct page *node_mem_map;
	struct bootmem_data *bdata;
	unsigned long node_start_pfn;
	unsigned long node_present_pages; /* total number of physical pages */
	unsigned long node_spanned_pages; /* total size of physical page
					     range, including holes */
	int node_id;
	struct pglist_data *pgdat_next;
	wait_queue_head_t kswapd_wait;
	struct task_struct *kswapd;
	int kswapd_max_order;
} pg_data_t;

5、内核又把属于某个CPU的内存区域分为两个管理区,ZONE_DMA和ZONE_NORMAL

对应的数据结构为zone_struct

struct zone {
	/* Fields commonly accessed by the page allocator */
	unsigned long		free_pages;
	unsigned long		pages_min, pages_low, pages_high;

	unsigned long		lowmem_reserve[MAX_NR_ZONES];

	struct per_cpu_pageset	pageset[NR_CPUS];

	/*
	 * free areas of different sizes
	 */
	spinlock_t		lock;
	struct free_area	free_area[MAX_ORDER];

	ZONE_PADDING(_pad1_)

	/* Fields commonly accessed by the page reclaim scanner */
	spinlock_t		lru_lock;	
	struct list_head	active_list;
	struct list_head	inactive_list;
	unsigned long		nr_scan_active;
	unsigned long		nr_scan_inactive;
	unsigned long		nr_active;
	unsigned long		nr_inactive;
	unsigned long		pages_scanned;	   /* since last reclaim */
	int			all_unreclaimable; /* All pages pinned */

	int temp_priority;
	int prev_priority;

	ZONE_PADDING(_pad2_)
	/* Rarely used or read-mostly fields */

	wait_queue_head_t	* wait_table;
	unsigned long		wait_table_size;
	unsigned long		wait_table_bits;

	/*
	 * Discontig memory support fields.
	 */
	struct pglist_data	*zone_pgdat;
	struct page		*zone_mem_map;
	/* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */
	unsigned long		zone_start_pfn;

	unsigned long		spanned_pages;	/* total size, including holes */
	unsigned long		present_pages;	/* amount of memory (excluding holes) */

	/*
	 * rarely used fields:
	 */
	char			*name;
};

整个结构大概如下图所示:



接下来研究内核对虚拟内存空间的管理

6、一个进程的(虚拟)用户空间被分成了许多离散的“区间”,内核定义了vm_area_struct结构来抽象这种“区间”

struct vm_area_struct {
	struct mm_struct * vm_mm;	/* 属于哪个虚拟内存空间,结构mm_struct是对虚拟内存空间的抽象,每个进程拥有一个,也就是每个进程都有自己的用户空间 */
	unsigned long vm_start;		/* 区间的起始*/
	unsigned long vm_end;		/* 区间结束后的第一个地址,不包含在区间内 */

	/* 一个进程虚拟内存空间的所有区间构成一个链表 */
	struct vm_area_struct *vm_next; /* 指向下一个区间 */

	pgprot_t vm_page_prot;		/* Access permissions of this VMA. 这两个跟内存的访问权限相关 */
	unsigned long vm_flags;		/* Flags, listed below. */

	struct rb_node vm_rb; /* 内核中给定一个虚拟地址找出它属于哪个区间是一个频繁的操作,如果在链表中作线性搜索的话可能影响效率,所以内核还把虚拟内存的所有区间建立了一棵红黑树 */

	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;

	struct list_head anon_vma_node;	/* Serialized by anon_vma->lock */
	struct anon_vma *anon_vma;	/* Serialized by page_table_lock */

	/* Function pointers to deal with this struct. */
	struct vm_operations_struct * vm_ops; /* 定义了该区间的一些操作,打开、关闭、建立映射、异常等*/

	/* Information about our backing store: */
	unsigned long vm_pgoff;		/* Offset (within vm_file) in PAGE_SIZE
					   units, *not* PAGE_CACHE_SIZE  该区间在页全局目录的偏移量 */
	struct file * vm_file;		/* File we map to (can be NULL). */
	void * vm_private_data;		/* was vm_pte (shared mem) */
	unsigned long vm_truncate_count;/* truncate_count or restart_addr */

#ifndef CONFIG_MMU
	atomic_t vm_usage;		/* refcount (VMAs shared if !MMU) */
#endif
#ifdef CONFIG_NUMA
	struct mempolicy *vm_policy;	/* NUMA policy for the VMA */
#endif
}
/*
 * These are the virtual MM functions - opening of an area, closing and
 * unmapping it (needed to keep files on disk up-to-date etc), pointer
 * to the functions called when a no-page or a wp-page exception occurs. 
 */
struct vm_operations_struct {
	void (*open)(struct vm_area_struct * area);
	void (*close)(struct vm_area_struct * area);
	struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int *type);
	int (*populate)(struct vm_area_struct * area, unsigned long address, unsigned long len, pgprot_t prot, unsigned long pgoff, int nonblock);
#ifdef CONFIG_NUMA
	int (*set_policy)(struct vm_area_struct *vma, struct mempolicy *new);
	struct mempolicy *(*get_policy)(struct vm_area_struct *vma,
					unsigned long addr);
#endif
};

7、每个进程都有自己的(虚拟)用户空间,内核中用mm_struct结构来描述

struct mm_struct {
	struct vm_area_struct * mmap;		/* 指向用户空间中所有区间构成的链表(表头结点)*/
	struct rb_root mm_rb; /* 用户空间中所有区间构成的红黑树的根节点 */
	struct vm_area_struct * mmap_cache;	/* 高速缓冲,指向最近一次被访问的区间 */
        /* 函数指针 */
	unsigned long (*get_unmapped_area) (struct file *filp,
				unsigned long addr, unsigned long len,
				unsigned long pgoff, unsigned long flags);
	void (*unmap_area) (struct vm_area_struct *area);
	unsigned long mmap_base;		/* base of mmap area */
	unsigned long free_area_cache;		/* first hole */
	pgd_t * pgd; /* 指向进程的页全局目录 */
	atomic_t mm_users;			/* 虽然每个进程拥有一个mm_struct结构,但是同一个mm_struct可能被多个进程共享,比如父子进程 */
	atomic_t mm_count;			/* mm_users和mm_count记录了该mm_struct结构被几个进程共享 */
	int map_count;				/* 区间链表中的结点个数 */
	struct rw_semaphore mmap_sem; /* 信号量,几个进程可以共享一个mm_struct结构,那么对同一个vm_area_struct的访问必须互斥 */
	spinlock_t page_table_lock;		/* 跟信号量作用类似 */

	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
						 */
        /* 以下这些成员定义了进程用户空间的代码段,数据段,堆栈等 */
	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 rss, anon_rss, total_vm, locked_vm, shared_vm;
	unsigned long exec_vm, stack_vm, reserved_vm, def_flags, nr_ptes;

	unsigned long saved_auxv[42]; /* for /proc/PID/auxv */

	unsigned dumpable:1;
	cpumask_t cpu_vm_mask;

	/* Architecture-specific MM context */
	mm_context_t context;

	/* Token based thrashing protection. */
	unsigned long swap_token_time;
	char recent_pagein;

	/* coredumping support */
	int core_waiters;
	struct completion *core_startup_done, core_done;

	/* aio bits */
	rwlock_t		ioctx_list_lock;
	struct kioctx		*ioctx_list;

	struct kioctx		default_kioctx;

	unsigned long hiwater_rss;	/* High-water RSS usage */
	unsigned long hiwater_vm;	/* High-water virtual memory usage */
};

下图说明了进程的虚拟内存管理和各种数据结构之间的关系


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值