RAM 的某些部分永久地分配給內(nèi)核, 并用來存放內(nèi)核代碼以及靜態(tài)內(nèi)核數(shù)據(jù)結(jié)構(gòu). RAM 的其余部分稱為動態(tài)內(nèi)存 (dynamic memory). 動態(tài)內(nèi)存不僅是進(jìn)程所需的寶貴資源, 也是內(nèi)核本身所需的寶貴資源. 實(shí)際上,整個(gè)系統(tǒng)的性能取決于如何有效地管理動態(tài)內(nèi)存. 因此, 現(xiàn)在所有多任務(wù)操作系統(tǒng)都在盡力優(yōu)化對動態(tài)內(nèi)存的使用, 也就是說, 盡可能做到當(dāng)需要時(shí)分配, 不需要時(shí)釋放.

當(dāng)給內(nèi)核分配動態(tài)內(nèi)存時(shí), 是相對容易的, 有如下兩點(diǎn)原因:
內(nèi)核是操作系統(tǒng)中優(yōu)先級最高的成分. 如果某個(gè)內(nèi)核函數(shù)請求動態(tài)內(nèi)存, 那么, 必定有正當(dāng)?shù)睦碛砂l(fā)出這個(gè)請求, 因此, 沒有道理試圖推遲這個(gè)請求.
內(nèi)核信任自己. 所有的內(nèi)核函數(shù)都被假定是沒有錯(cuò)誤的, 因此內(nèi)核函數(shù)不必針對程序錯(cuò)誤施加任何保護(hù)措施.
而當(dāng)給用戶態(tài)進(jìn)程分配內(nèi)存時(shí), 情況完全不同:
進(jìn)程對動態(tài)內(nèi)存的請求被認(rèn)為是不緊急的. 例如, 當(dāng)進(jìn)程對應(yīng)在磁盤上所存儲的可執(zhí)行文件被裝入內(nèi)存時(shí), 進(jìn)程并不一定會立即對所有的代碼和數(shù)據(jù)進(jìn)行訪問. 類似地, 當(dāng)進(jìn)程調(diào)用malloc()以請求獲得額外的動態(tài)內(nèi)存時(shí), 也并不意味著進(jìn)程很快就會訪問所獲得的額外的動態(tài)內(nèi)存.因此, 一般來說, 內(nèi)核總是盡量推遲給用戶態(tài)進(jìn)程分配動態(tài)內(nèi)存.
由于用戶進(jìn)程是不可信任的, 因此, 內(nèi)核必須能隨時(shí)準(zhǔn)備捕獲用戶態(tài)進(jìn)程引起的所有尋址錯(cuò)誤.
為了使得動態(tài)內(nèi)存得到最大限度的使用, 內(nèi)核使用一種新的資源成功實(shí)現(xiàn)了對進(jìn)程動態(tài)內(nèi)存的推遲分配. 當(dāng)用戶態(tài)進(jìn)程請求動態(tài)內(nèi)存時(shí), 并沒有獲得請求的動態(tài)內(nèi)存, 而僅僅得到了對一個(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)識相應(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 位無符號整數(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.
可以通過以下代碼對進(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)程對于內(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í)的存儲區(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)程地址空間的存在讓我們在編寫程序的時(shí)候只需關(guān)注虛擬地址, 而無需關(guān)注數(shù)據(jù)在物理內(nèi)存當(dāng)中實(shí)際的存儲位置.
頁表的存在讓進(jìn)程在間接訪問內(nèi)存的時(shí)候, 增加一個(gè)轉(zhuǎn)換的過程, 在這個(gè)轉(zhuǎn)換的過程中, 內(nèi)核對進(jìn)程的尋址請求進(jìn)行檢查, 如果該進(jìn)程的尋址請求異常, 則該請求被操作系統(tǒng)攔截, 從而實(shí)現(xiàn)對物理內(nèi)存的保護(hù).
進(jìn)程地址空間與頁表的存在, 讓內(nèi)核對于進(jìn)程管理模塊與內(nèi)存管理模塊進(jìn)行了解耦.
審核編輯:湯梓紅
-
RAM
+關(guān)注
關(guān)注
8文章
1399瀏覽量
120600 -
Linux
+關(guān)注
關(guān)注
88文章
11763瀏覽量
219090 -
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
7402瀏覽量
129342 -
動態(tài)內(nèi)存
+關(guān)注
關(guān)注
1文章
25瀏覽量
8241 -
進(jìn)程
+關(guān)注
關(guān)注
0文章
211瀏覽量
14542
原文標(biāo)題:Linux - 進(jìn)程 - 進(jìn)程地址空間
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
Linux如何證明線程共享進(jìn)程的地址空間
Linux內(nèi)核地址映射模型與Linux內(nèi)核高端內(nèi)存詳解
Linux守護(hù)進(jìn)程詳解
linux進(jìn)程的深入理解
Linux下進(jìn)程的內(nèi)存結(jié)構(gòu)
Linux下進(jìn)程的創(chuàng)建、執(zhí)行和終止
深入淺出Linux的進(jìn)程地址空間
Linux的進(jìn)程
mlock如何鎖住進(jìn)程地址空間關(guān)聯(lián)的物理內(nèi)存
Linux進(jìn)程的內(nèi)存消耗和泄漏詳解
為什么進(jìn)程地址空間中要包括操作系統(tǒng)(內(nèi)核)呢?
Linux系統(tǒng)為什么需要引入虛擬地址
Linux虛擬地址空間和物理地址空間的關(guān)系
Linux進(jìn)程地址空間詳解
評論