Chinaunix首页 | 论坛 | 博客
  • 博客访问: 692148
  • 博文数量: 192
  • 博客积分: 1875
  • 博客等级: 上尉
  • 技术积分: 2177
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-23 23:21
个人简介

有时候,就是想窥视一下不知道的东东,因为好奇!

文章分类

全部博文(192)

文章存档

2024年(8)

2023年(3)

2020年(1)

2019年(1)

2018年(1)

2017年(2)

2016年(69)

2015年(53)

2014年(14)

2013年(1)

2012年(5)

2011年(25)

2010年(9)

分类: LINUX

2016-10-11 23:17:38

内核资料收集

1.内存描述符
        与进程地址空间有关的全部信息都包含在一个叫内存描述符(memory descriptor)的数据结构中, 这个结构的类型为mm_struct,
    进程描述符的mm字段就指向这个结构. 内存描述符表(不同版本有变化)中的字段(include/linux/mm_types.h):
        struct vm_area_struct *     mmap                              指向线性区对象的链表头
        struct rb_root                     mm_rb                             指向线性区对象的红黑树的根
        struct vm_area_struct *     mmap_cache                  指向最后一个引用的线性区对象
        unsigned long (*)()            get_unmapped_area       在进程地址空间中搜索有效性地址区间的方法
        void (*)()                            unmap_area                    释放线性地址区间时调用的方法
        unsigned long                   mmap_base                     标识第一个分配的匿名线性区或文件内存映射的线性地址
        unsigned long                   free_area_cache              内核从这个地址开始搜索进程地址空间中线性地址的空闲区间
        pgd_t *                              pgd                                   指向页全局目录
        atomic_t                            mm_users                        次使用计数器
        atomic_t                            mm_count                        主使用计数器
        int                                      map_count                      线性区个数
        struct rw_semaphore        mmap_sem                      线性区读/写信号量
        spinlock_t                          page_table_lock              线性区自旋锁和页表自旋锁
        struct list_head                 mmlist                              指向内存区描述符链表中的相邻元素
        unsigned long                   start_code                       可执行代码的起始地址
        unsigned long                   end_code                         可执行代码的最后地址
        unsigned long                   start_data                        已初始化数据的起始地址
        unsigned long                   end_data                          已初始化数据的最后地址
        unsigned long                   start_brk                          堆起始地址
        unsigned long                   end_brk                           堆的当前最后地址
        unsigned long                   start_stack                       用户态堆栈的起始地址
        unsigned long                   arg_start                          命令行参数起始地址
        unsigned long                   arg_end                           命令行参数的最后地址
        unsigned long                   env_start                         环境变量起始地址
        unsigned long                   env_end                          环境变量的最后地址
        unsigned long                   rss                                   分配给进程的页框数
        unsigned long                   anon_rss                          分配给匿名内存映射的页框数
        unsigned long                   total_vm                          进程地址空间的大小(页数)
        unsigned long                   locked_vm                      "锁住"而不能换出的页的个数
        unsigned long                   shared_vm                      共享文件内存映射中的页数
        unsigned long                   exec_vm                          可执行内存映射中的页数
        unsigned long                   stack_vm                         用户态堆栈中的页数
        unsigned long                   reserved_vm                   在保留区中的页数或在特殊线性区中的页数
        unsigned long                   def_flags                          线性区默认访问标志
        unsigned long                   nr_ptes this                     进程的页表数
        unsigned long []                saved_auxv                     开始执行ELF程序时使用
        unsigned int                      dumpable                        是否可产生内存信息转储标志
        cpumask_t                        cpu_vm_mask                 用于懒惰TLB交换的位掩码
        mm_context_t                   context                            指向有关特定体系结构信息的表(如x86平台上的LDT)
        unsigned long                   swap_token_time            进程在这个时间将有资格获得交换标记
        char                                   recent_pagein                最近发生缺页, 则设置该标记
        int                                      core_waiters                   正在把进程地址空间的内容卸载到转储文件中的轻量级进程的数量
        struct completion *            core_startup_done         指向创建内存转储文件时的补充原语
        struct completion              core_done                      创建内存转储文件时使用的补充原语
        rwlock_t                            ioctx_list_lock                 用于保护异步I/O上下文链表的锁
        struct kioctx *                    ioctx_list                         异步I/O上下文件链表
        struct kioctx                      default_ioctx                   默认的异步I/O上下文
        unsigned long                   hiwater_rss                    进程拥有的最大页框数
        unsigned long                   hiwater_vm                    进程线性区中的最大页数

2. 内存描述符中的一些字段
        所有内存描述符存放在一个双向链表中,每个描述符在mmlist字段存放相邻元素的地址. 链表的每一个元素是init_mm的
    mmlist字段, init_mm是初始化阶段进程0所使用的内存描述符. mmlist_lock自旋锁保护多处理器系统对链表的同时访问.
        mm_user字段存放共享mm_struct数据结构的轻量级进程的个数. 
        mm_count字段是内存描述符的主使用计数器, 在mm_users次使用计数器中的所有用户在mm_count中只作为一个单位. 
    每当mm_count递减时,内核都要检查它是否变为0,如果是,就要解除这个内存描述符,因为不再有用户使用它.
        以下例子解释mm_users和mm_count之间的不同. 考虑一个内存描述符由两个轻量级进程共享.它的mm_users字段通常
    存放的值为2, 而mm_count字段存放的值为1(两个所有者进程算作一个)
        如果把内存描述符暂时借给一个内核线程,那么,内核就增加mm_count. 这样即两个轻量级进程都死亡,且mm_users字段变
    为0, 这个内存描述符也不被释放,直到内核线程使用完为止, 因为mm_count字段仍然大于0.
        如果内核想确保内存描述符在一个长操作的中间不被释放,那么就应该增加mm_users字段,而不是mm_count字段的值. 最终
    的结果是相同的, 因为mm_users的增加确保了mm_count来变为0,即使拥有这个内存描述符的所有轻量级进程全部死亡.

        mm_alloc()函数用来从slab分配器高速缓存中获取一个新的内存描述符. mm_alloc()调用kmem_cache_alloc()来初始化新
    的内存描述符, 并把mm_count和mm_users字段都置为1. mm_put()函数递减内存描述符的mm_users字段. 如果该字段变为0,
    这个函数就释放局部描述符表/线性区描述符及由内存描述符所引用的页表,并调用mmdrop().后一个函数把mm_count字段减1, 如
    果该字段变为0, 就释放mm_struct数据结构.

3. 内核线程的内存描述符
        内核线程仅运行在内核态, 它们永远不会访问TASK_SIZE(等于PAGE_OFFSET, x86下通常为0xC0000000)以下的地址. 内核
    线程不用线性区, 因而内存描述符的很多字段对内核线程没有意义.
        大于TASK_SIZE线性地址的相应页表项都是相同的, 因此一个内核线程到底使用什么样的页表集根本没关系. 为了避免无用的
    TLB和高速缓存刷新,内核线程使用一组最近运行的普通进程的页表. 进程描述符用mm和active_mm处理此情况. mm字段指向进程
    所拥有的内存描述符, active_mm字段指向进程运行时所使用的内存描述符. 对普通进程而言, 这两个字段存放相同的指针. 但是,
    内核线程不拥有内存描述符,因此它们的mm字段总是NULL. 内核线程运行时,它的active_mm字段被初始化为前一个运行进程的
    active_mm值.
        内核态的进程为高于TASK_SIZE的线性地址修改页表项,那么它也就应当更新系统中所有进程页表集合中的相应表项. 事实上,一旦
    内核态的一个进程进行了设置. 映射应该对内核态的其他所有进程都有效.触及所有进程的页表集合是相当费时的操作, 因此,linux采
    用延迟方式. 每当一个高端地址必须被重新映射时, 内核就更新根目录在swapper_pg_dir主内存页全局目录中的常规页表集合. 这个
    页全局目录由主内存描述符的pgd字段指向,而主内存描述符存放于init_mm变量. 
阅读(2929) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~