9 进程地址空间
程序员文章站
2024-03-24 13:51:10
...
- 内核中的函数直接了当获得动态内存,
- 通过调用以下几种函数中的一个达到:
- __get_free_pages或alloc_pages从分区页框分配器中获得页框
- kmem_cache_alloc()或kmalloc()用slab分配器为专用或通用对象分配块,vmalloc()或vmalloc_32()获得一块非连续的内存区。
- 如果所请求的内存区得以满足,
- 返一个页描述符地址或线性地址(即所分配动态内存区的起始地址)
- 内核优先级最高
- 如果某个内核函数请求动态内存,没道理试图推退
- 内核信任自己。
- 所有内核函数都被假定没错,因此内核函数不必插入针对编程错误的任何保护措施。
- 当给用户态进程分配内存时,情况不同:
- 进程对动态内存的请求被认为是不紧迫的。
- 当进程的可执行文件被装入时,进程并不一定立即对所有的代码页进行访问。
- 进程调用ma1loc()以获得请求的动态内存时,也并不意味着进程很快就会访问所有所获得的内存。
- 内核总是尽量推迟给用户态进程分配动态内存。
- 用户进程是不可信任的,因此,内核必须能随时准备捕获用户态进程引起的所有寻址错误。
- 内核用一种新资源实现对进程动态内存的推迟分配。
- 用户态进程请求动态内存时,并没有获得请求的页框,
- 而仅获得对一个新的线性地址区间的使用权,
- 这一线性地址区间就成为进程地址空间的一部分。
- 这一区间叫“线性区”(译注1)。
- 进程怎样看待动态内存
- “线性区”描述进程地址空间的基本组成
- 缺页异常处理程序在推迟给进程分配页框中所起的作用。
- 内核怎样创建和删除进程的整个地址空间。
- 与进程的地址空间管理有关的API和系统调用
进程的地址空间
- 进程的地址空间由允许进程使用的全部线性地址组成。
- 每个进程所看到的线性地址集合不同,一个进程所使用的地址与另外一个进程所使用的地址之间没有什么关系。
- 内核可通过增或删某些线性地址区间来动态地修改进程的地址空间。
- 内核通过所谓线性区的资源来表示线性地址区间,线性区是由起始线性地址、长度和访问权限来描述。
- 起始地址和线性区的长度必须4096倍数,以便每个线性区所识别的数据完全填满分配给它的页框。
- 下面是进程获得新线性区的一些典型情况
- 控制台输入命令时,shell进程创建一个新的进程去执行这命令
- 一个全新的地址空间(一组线性区)分配给新进程(见本章后的“创建和删除进程的地址空间”和第二十章)。
- 正在运行的进程决定装入一个完全不同的程序。
- 进程标识符不变
- 装入这个程序以前所用的线性区被释放,
- 并有新的线性区分配给这个进程(见二十章中的“exec”)
- 正在运行的进程可能对一个文件(或它的一部分)执行“内存映射”。
- 内核给这个进程分配一个新的线性区来映射这个文件(见十六章“内存映射”)
- 进程可能持续向它的用户态堆栈增数据,直到映射这个堆栈的线性区用完,内核会决定扩展这个线性区大小(见本章后面“缺页异常处理程序
- 进程可能创建一个IPC共享线性区来与其他进程共享数据。
- 内核给这个进程分配一个新的线性区以实现这个方案(见十九章中的“IPC共享内存”)
- 调malloc()扩展自己的动态区(堆)
- 内核可能决定扩展给这个堆所分配的线性区(参见本章后面的“堆的管理”)。
- “缺页异常处理程序”看到,
- 确定一个进程的线性区(即进程的地址空间)是内核任务
- 这可让缺页异常处理程序有效区分引发这个异常处理程序的两种不同类型的无效线性地址
- 由编程错误引发的无效线性地址
- 由缺页引发的无效线性地址
- 即使这个线性地址属于进程的地址空间,
- 但这个地址的页框仍有待分配。
- 进程观点看,后一种地址不是无效的,
- 内核要用这种缺页以实现请求调顶:
- 内核通过提供页框来处理这种缺页,并让进程继续执行。
- 内核要用这种缺页以实现请求调顶:
内存描述符
- 与进程地址空间有关的全部信息都在做内存描述符的数据结构中(译注2)
- 如表9-2
struct mm_struct {
struct vm_area_struct * mmap; /* list of VMAs */
struct rb_root mm_rb;
struct vm_area_struct * mmap_cache; /* last find_vma result */
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; /* 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 rw_semaphore mmap_sem;
spinlock_t page_table_lock; /* Protects page tables, mm->rss, mm->anon_rss */
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 */
};
上一篇: MySQL迁移达梦DM8实战
下一篇: iOS KVC底层实现