前言
上一篇解释了内存的整个流程。内核供给,用户使用,但是具体细节没有细说,这里我给出详细的解释。内核的供给也是一个挺复杂的问题。暂时我还没搞懂,只好慢慢的边看边记录。
正文
因为linux是支持多处理器的,所以会有不同cpu,不同内存的问题。也就是不平整内存模式,我们不能像处理一个cpu的模式下处理那3g内存和两个cpu的内存一样,我们最好不要把一个数据结构分成两部分存储,一部分存在那个cpu的内存,一部分存在这个cpu的内存中。为了处理这个问题。我门只好在原来的page的基础上搞出一个数据结构,用来存放当前操作系统的所有的不同介质上的内容。
这里是一个pglist_data数据结构。
typedef struct pglist_data {
zone_t node_zones[MAX_NR_ZONES]; //每个连续的存储区,最多分为三个区域,(这个先不解释)
zonelist_t node_zonelists[NR_GFPINDEX]; //这货是为了分配时候准备的,也就是上面说那个区域的排序
struct page *node_mem_map;
unsigned long *valid_addr_bitmap;
struct bootmem_data *bdata;
unsigned long node_start_paddr;
unsigned long node_start_mapnr;
unsigned long node_size;
int node_id;
struct pglist_data *node_next; //这里是为了维护一个单向队列,毕竟不知一个连续存储区。
} pg_data_t;
这货比较复杂,但是我仅仅看出这三个参数的意义。其他暂时没发现。以后总结。
其实大家还是很迷茫这是啥。最关键的是安格zone_t,我们看下
typedef struct zone_struct {
spinlock_t lock; //锁
unsigned long offset; //一个特定的page数组的下标值。
unsigned long free_pages; //空闲page的个数,为了分配
unsigned long inactive_clean_pages; //已经进入交换区的page
unsigned long inactive_dirty_pages; //每有进入交换区的page
unsigned long pages_min, pages_low, pages_high; //细节。
struct list_head inactive_clean_list; //已经进入交换区的page的list
free_area_t free_area[MAX_ORDER]; //这个是用来分配连续页面用的。MAX_ORDER =10;
char *name;
unsigned long size;
struct pglist_data *zone_pgdat; //指向上层的节点
unsigned long zone_start_paddr;
unsigned long zone_start_mapnr;
struct page *zone_mem_map;
} zone_t;
真正用来存放页面的是在free_area[MAX_ORDER]里面,那我们继续找free_area_t,
typedef struct free_area_struct {
struct list_head free_list; //这个就是连接page的结构
unsigned int *map;
} free_area_t;
既然可以连接page,那么page一定有个list_head,我们来看下page结构
typedef struct page {
struct list_head list;
struct address_space *mapping;
unsigned long index;
struct page *next_hash;
atomic_t count;
unsigned long flags; /* atomic flags, some possibly updated asynchronously */
struct list_head lru;
unsigned long age;
wait_queue_head_t wait;
struct page **pprev_hash;
struct buffer_head * buffers;
void *virtual; /* non-NULL if kmapped */
struct zone_struct *zone;
} mem_map_t;
终于我们找到我们的page了,其实page的结构对于整个逻辑理解不会产生很大的偏差,这里不再详细注释。总之就是可以通过pg_data_t整个链表找到我门的page。整体结构如图
关于内核管理内存的供给,那么进程对于内存的寻求也需要管理,我们的进程就像是占据整个操作系统一样(3g内存都是我的)。
在进程管理自己内存的时候。是需要几个数据结构。我们知道每个进程都有一个结构专门用来维护自己信息的结构task_struct,这个很复杂,就不细说。里面有个结构叫mm,类型中是
struct mm_struct {
struct vm_area_struct * mmap; //这是维护一个线性队列,vm_area_struct,这个才是最终的内存区间信息
struct vm_area_struct * mmap_avl; //数据过多。线性队列不行,就用avl树来维护。可以平衡搜说时间
struct vm_area_struct * mmap_cache; //上次使用的vm_area_struct
pgd_t * pgd;
atomic_t mm_users; /* How many users with user space? */
atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */
int map_count; /* number of VMAs */
struct semaphore mmap_sem;
spinlock_t page_table_lock;
struct list_head mmlist; /* List of all active mm's */
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, total_vm, locked_vm;
unsigned long def_flags;
unsigned long cpu_vm_mask;
unsigned long swap_cnt; /* number of pages to swap on next pass */
unsigned long swap_address;
/* Architecture-specific MM context */
mm_context_t context;
};
中文注释中其实一切都是为了vm_area_struct,我们继续看看vm_area_struct
struct vm_area_struct {
struct mm_struct * vm_mm; /* 上一层结构 */
unsigned long vm_start; //这才是关键。开始虚拟地址开始
unsigned long vm_end; //结束
struct vm_area_struct *vm_next; //vm_area_struct单向对列靠你来
pgprot_t vm_page_prot;
unsigned long vm_flags;
/* AVL 树的资料 */
short vm_avl_height;
struct vm_area_struct * vm_avl_left;
struct vm_area_struct * vm_avl_right;
struct vm_area_struct *vm_next_share;
struct vm_area_struct **vm_pprev_share;
struct vm_operations_struct * vm_ops;
unsigned long vm_pgoff; /* offset in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */
struct file * vm_file;
unsigned long vm_raend;
void * vm_private_data; /* was vm_pte (shared mem) */
};
这里面有几个关于文件的信息,这是为了mapping,或者交换时候使用,对于真个内存管理原理影响不大。可以忽视。结构如图:
下面说写几个函数,加强些记忆。
//找vm_area_struct,这里当然循环查找那个线性对列或者avl树
struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr)
//插入数据到线性对列或者avl树,这里细节就不写了。
void __insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vmp)
后记
这里无论是从内核空间和用户空间都完成了对于page的管理。但是真正的难题还是在具体操作,并且这些概念貌似比较容易理解,但是最难的是如何利用这些数据结构,来处理具体的问题。以后我们慢慢的来解决具体问题。