RAM 的某些部分永久地分配給內(nèi)核, 并用來存放內(nèi)核代碼以及靜態(tài)內(nèi)核數(shù)據(jù)結(jié)構(gòu). RAM 的其余部分稱為動(dòng)態(tài)內(nèi)存 (dynamic memory). 動(dòng)態(tài)內(nèi)存不僅是進(jìn)程所需的寶貴資源, 也是內(nèi)核本身所需的寶貴資源. 實(shí)際上,整個(gè)系統(tǒng)的性能取決于如何有效地管理動(dòng)態(tài)內(nèi)存. 因此, 現(xiàn)在所有多任務(wù)操作系統(tǒng)都在盡力優(yōu)化對(duì)動(dòng)態(tài)內(nèi)存的使用, 也就是說, 盡可能做到當(dāng)需要時(shí)分配, 不需要時(shí)釋放.
當(dāng)給內(nèi)核分配動(dòng)態(tài)內(nèi)存時(shí), 是相對(duì)容易的, 有如下兩點(diǎn)原因:
內(nèi)核是操作系統(tǒng)中優(yōu)先級(jí)最高的成分. 如果某個(gè)內(nèi)核函數(shù)請(qǐng)求動(dòng)態(tài)內(nèi)存, 那么, 必定有正當(dāng)?shù)睦碛砂l(fā)出這個(gè)請(qǐng)求, 因此, 沒有道理試圖推遲這個(gè)請(qǐng)求.
內(nèi)核信任自己. 所有的內(nèi)核函數(shù)都被假定是沒有錯(cuò)誤的, 因此內(nèi)核函數(shù)不必針對(duì)程序錯(cuò)誤施加任何保護(hù)措施.
而當(dāng)給用戶態(tài)進(jìn)程分配內(nèi)存時(shí), 情況完全不同:
進(jìn)程對(duì)動(dòng)態(tài)內(nèi)存的請(qǐng)求被認(rèn)為是不緊急的. 例如, 當(dāng)進(jìn)程對(duì)應(yīng)在磁盤上所存儲(chǔ)的可執(zhí)行文件被裝入內(nèi)存時(shí), 進(jìn)程并不一定會(huì)立即對(duì)所有的代碼和數(shù)據(jù)進(jìn)行訪問. 類似地, 當(dāng)進(jìn)程調(diào)用malloc()以請(qǐng)求獲得額外的動(dòng)態(tài)內(nèi)存時(shí), 也并不意味著進(jìn)程很快就會(huì)訪問所獲得的額外的動(dòng)態(tài)內(nèi)存.因此, 一般來說, 內(nèi)核總是盡量推遲給用戶態(tài)進(jìn)程分配動(dòng)態(tài)內(nèi)存.
由于用戶進(jìn)程是不可信任的, 因此, 內(nèi)核必須能隨時(shí)準(zhǔn)備捕獲用戶態(tài)進(jìn)程引起的所有尋址錯(cuò)誤.
為了使得動(dòng)態(tài)內(nèi)存得到最大限度的使用, 內(nèi)核使用一種新的資源成功實(shí)現(xiàn)了對(duì)進(jìn)程動(dòng)態(tài)內(nèi)存的推遲分配. 當(dāng)用戶態(tài)進(jìn)程請(qǐng)求動(dòng)態(tài)內(nèi)存時(shí), 并沒有獲得請(qǐng)求的動(dòng)態(tài)內(nèi)存, 而僅僅得到了對(duì)一個(gè)新的線性地址區(qū)間的使用權(quán), 這樣的線性地址區(qū)間有很多, 由允許進(jìn)程使用的全部線性地址區(qū)間所組成的集合就叫做進(jìn)程地址空間.
與進(jìn)程地址空間有關(guān)的全部信息都包含在一個(gè)叫做內(nèi)存描述符的數(shù)據(jù)結(jié)構(gòu)中 (實(shí)際上就是描述進(jìn)程虛擬內(nèi)存的數(shù)據(jù)結(jié)構(gòu)), 這個(gè)結(jié)構(gòu)的類型為mm_struct, 進(jìn)程描述符的mm字段就指向這個(gè)結(jié)構(gòu).
struct mm_struct *mm;
如下為Linux 2.6.11版的內(nèi)核中mm_struct的實(shí)現(xiàn).
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; 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; * 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]; unsigned dumpable:1; cpumask_t cpu_vm_mask; mm_context_t context; unsigned long swap_token_time; char recent_pagein; int core_waiters; struct completion *core_startup_done, core_done; rwlock_t ioctx_list_lock; struct kioctx *ioctx_list; struct kioctx default_kioctx; unsigned long hiwater_rss; unsigned long hiwater_vm; };
其中用來標(biāo)識(shí)相應(yīng)進(jìn)程特定線性區(qū)的字段如下:
start_code, end_code
正文代碼的起始地址和終止地址.
start_data, end_data
已初始化數(shù)據(jù)的起始地址和終止地址.
start brk, brk
堆的起始地址和當(dāng)前終止地址.
start_stack
用戶態(tài)堆棧的起始地址.
arg_start, arg_end
命令行參數(shù)的起始地址和終止地址.
env_start, env_end
環(huán)境變量的起始地址和終止地址.
如下為進(jìn)程地址空間的布局, 由一個(gè)一個(gè)的線性地址區(qū)間組成, 線性地址 (linear address), 也稱虛擬地址 (virtual address) 是一個(gè) 32 位無符號(hào)整數(shù) (unsigned long), 可以用來表示數(shù)值高達(dá) 4GB 的地址, 也就是 4,294,967,296 個(gè)內(nèi)存單元.線性地址通常用十六進(jìn)制數(shù)字表示, 值的范圍從 0x00000000 到 0xffffffff.
0x00000000 ~ 0xbfffffff 這一線性地址區(qū)間被稱為用戶空間, 大小為 3GB; 而0xc0000000 ~ 0xffffffff 這一線性地址區(qū)間被稱為內(nèi)核空間, 大小為 1GB.
可以通過以下代碼對(duì)進(jìn)程地址空間的布局圖進(jìn)行驗(yàn)證.
#include#include int uninitialized_global_var; int initialized_global_var = 100; int main(int argc, char *argv[], char *envp[]) { printf("Code address:%p ", main); printf("Initialized Data address:%p ", &initialized_global_var); printf("Uninitialized Data address:%p ", &uninitialized_global_var); int *p = (int*)malloc(sizeof(int)); printf("Heap address:%p ", p); printf("Stack address:%p ", &p); for (int i = 0; i < argc; i++) { printf("Command-line Arguments address:%p ", argv[i]); } for (int i = 0; envp[i]; i++) { printf("Environment Variables address:%p ", envp[i]); } return 0; }
運(yùn)行結(jié)果如下, 與進(jìn)程地址空間的布局相吻合.
線性地址(虛擬地址)的集合稱為虛擬內(nèi)存, 物理地址的集合稱為物理內(nèi)存, 進(jìn)程對(duì)于內(nèi)存訪問的終點(diǎn)是物理內(nèi)存而不是虛擬內(nèi)存,所以必然存在一種將虛擬內(nèi)存轉(zhuǎn)化為物理內(nèi)存的結(jié)構(gòu), 這種結(jié)構(gòu)被稱為頁表.
頁 (Page) && 頁幀 (Page Frame)
內(nèi)核使用struct page作為基本單位來管理物理內(nèi)存, 在內(nèi)核看來,所有的 RAM 都被劃分成了固定長度的頁幀 (頁幀也叫頁框, 通常大小為4KB). 每一個(gè)頁幀包含了一個(gè)頁, 也就是說一個(gè)頁幀的長度和一個(gè)頁的長度相同.頁和頁幀的區(qū)別在于, 頁是抽象的數(shù)據(jù)結(jié)構(gòu), 可以存放在任意地方, 而頁幀是真實(shí)的存儲(chǔ)區(qū)域, 屬于主存的一部分.
如下為Linux 2.6.11版的內(nèi)核中struct page的實(shí)現(xiàn).
struct page { page_flags_t flags; * updated asynchronously */ atomic_t _count; atomic_t _mapcount; * to show when page is mapped * & limit reverse map searches. */ unsigned long private; * usually used for buffer_heads * if PagePrivate set; used for * swp_entry_t if PageSwapCache * When page is free, this indicates * order in the buddy system. */ struct address_space *mapping; * inode address_space, or NULL. * If page mapped as anonymous * memory, low bit is set, and * it points to anon_vma object: * see PAGE_MAPPING_ANON below. */ pgoff_t index; struct list_head lru; * protected by zone->lru_lock ! */ * On machines where all RAM is mapped into kernel address space, * we can simply calculate the virtual address. On machines with * highmem some memory is mapped into kernel virtual memory * dynamically, so we need a place to store that address. * Note that this field could be 16 bits on x86 ... ;) * * Architectures with slow multiplication can define * WANT_PAGE_VIRTUAL in asm/page.h */ #if defined(WANT_PAGE_VIRTUAL) void *virtual; not kmapped, ie. highmem) */ #endif };
CPU 管理物理地址, 因而虛擬地址需要轉(zhuǎn)化為物理地址才能給 CPU 使用.用于將進(jìn)程(虛擬)地址空間映射成物理地址空間的數(shù)據(jù)結(jié)構(gòu)稱為頁表.
進(jìn)程地址空間, 頁表的存在有什么意義?
讓所有進(jìn)程以統(tǒng)一的視角看待內(nèi)存,進(jìn)程地址空間的存在讓我們?cè)诰帉懗绦虻臅r(shí)候只需關(guān)注虛擬地址, 而無需關(guān)注數(shù)據(jù)在物理內(nèi)存當(dāng)中實(shí)際的存儲(chǔ)位置.
頁表的存在讓進(jìn)程在間接訪問內(nèi)存的時(shí)候, 增加一個(gè)轉(zhuǎn)換的過程, 在這個(gè)轉(zhuǎn)換的過程中, 內(nèi)核對(duì)進(jìn)程的尋址請(qǐng)求進(jìn)行檢查, 如果該進(jìn)程的尋址請(qǐng)求異常, 則該請(qǐng)求被操作系統(tǒng)攔截, 從而實(shí)現(xiàn)對(duì)物理內(nèi)存的保護(hù).
進(jìn)程地址空間與頁表的存在, 讓內(nèi)核對(duì)于進(jìn)程管理模塊與內(nèi)存管理模塊進(jìn)行了解耦.
審核編輯:湯梓紅
-
RAM
+關(guān)注
關(guān)注
8文章
1364瀏覽量
114473 -
Linux
+關(guān)注
關(guān)注
87文章
11213瀏覽量
208735 -
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
6695瀏覽量
123147 -
動(dòng)態(tài)內(nèi)存
+關(guān)注
關(guān)注
1文章
24瀏覽量
7959 -
進(jìn)程
+關(guān)注
關(guān)注
0文章
201瀏覽量
13941
原文標(biāo)題:Linux - 進(jìn)程 - 進(jìn)程地址空間
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論