内核资料收集
0. 内核与用户进程动态内存分配的不同
内核中的函数以相当直接的方式获得动态内存:
a. 调用__get_free_pages()/alloc_pages()从分区页框分配器中获得页框
b. 调用kmem_cache_alloc()/kmalloc()使用slab分配器为专用或通用对象分配块
c. 用vmalloc()/vmalloc_32()获得非连续内存区.
如果所请求的内存区得以满足,这些函数都返回一个页框描述符地址或线性地址.
内核如此获得动态内存基于以下两个原因:
a. 内核是操作系统中优先级最高的部分.内核函数请求内存,必有正当合理的理由.
b. 内核信任自己.所有内核函数都被假定是没有错误的.因此内核函数不必插入针对编程错误的任何保护措施.
给用户态进程分配内存时,情况完全不同:
a. 进程对动态内存的请求被认为是不紧迫的.
如:进程的可执行文件被装入时,进程并不一定立即对所有代码页进行访问. 再如:进程调用malloc()获得请求的动态内存时,
也并不意味着进程很快就会访问所获得的内存.
因此,一般来说,内核总是尽量推迟给用户态进程分配动态内存.
b. 用户进程是不可信任的, 因此,内核必须能随时准备捕获用户态进程引起的所有寻址错误.
内核用一种新的资源实现对进程动态内存的推迟分配.用户态进程请求动态内存时,并没有获得请求的页框, 而仅仅获得对一个
新的线性地址区间的使用权,而这一线性地址区间就成为进程地址空间的一部分. 这一区间叫做"线性区(memory region)"
1. 进程地址空间
进程的地址空间(address space)由允许进程使用的全部线性地址组成. 每个进程所看到的线性地址集合是不同的, 一个进
程所使用的地址与另外一个进程所使用的地址之间没有什么关系. 内核可以通过增加或删除某些地址区间来动态地改进进程地
址空间.
内核通过所谓线性区的资源来表示线性地址区间, 线性区是由起始线性地址/长度/访问权限来描述的.为效率起见, 起始地址
各线性区长度者必须是4096的倍数, 以便每个线性区所识别的数据完全填满分配给它的页框.
下面是进程获得新线性区的一些典型情况:
a. 用户在控制台输入一条命令时, shell进程创建一个新的进程去执行这个命令. 结果是,一个全新的地址空间分配给新的进程
b. 正在运行的进程有可能决定装入一个完全不同的程序.在这种情况下,进程标识符仍然保持不变,可是在装入这个程序前所使用
的线性区却被释放,并有一组新的线性区被分配给这个进程(exec函数).
c. 进程可能对一个文件执行"内存映射". 在这种情况下,内核给这个进程分配一个新的线性区来映射这个文件(mmap吗?)
d. 进程可能持续向它的用户态堆栈增加数据,直到映射这个堆栈的线性区用完为止. 这种情况下,内核也许会决定扩展这个线性
区的大小
e. 进程可能创建一个IPC共享线性区来与其他合作进程共享数据.在这种情况下,内核给这个进程分配一个新的线性区以实现这个方案
f. 进程可能通过调用类似malloc()这样的函数扩展自己的动态区. 这种情况下,内核可能决定扩展给这个堆所分配的线性区
2. 相关系统调用
brk() 改变进程堆的大小
execve() 装入一个新的可执行文件,从而改变进程的地址空间
_exit() 结束当前进程并撤销它的地址空间
fork() 创建一个新进程,并为它创建新的地址空间
mmap()/mmap2() 为文件创建一个内存映射,从而扩大进程的地址空间
mremap() 扩大或缩小线性区
remap_file_pages() 为文件创建非线性映射
munmap() 撤销对文件的内存映射,从而缩小进程的地址空间
shmat() 创建一个共享线性区
shmdt() 撤销一个共享线性区
内核确定一个进程当前所拥有的线性地址, 可以让缺页异常处理程序有效的区分引发这个异常处理程序的两种不同类型的无效
线性地址:
a. 由编程错误引发的无效线性地址
b. 由缺页引发的无效线性地址; 即使这个线性地址属于进程地址空间,但是对应于这个地址的页框仍然有待分配.
从进程的观点看,后一种地址不是无效的,内核要利用这种缺页以实现请求调页:内核通过提供页框来处理这种缺页,并让进程继续执行.
阅读(742) | 评论(0) | 转发(0) |