RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux進(jìn)程地址空間詳解

馬哥Linux運(yùn)維 ? 來源:稀土掘金技術(shù)社區(qū) ? 2023-12-18 09:45 ? 次閱讀

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í)際上,整個系統(tǒng)的性能取決于如何有效地管理動態(tài)內(nèi)存. 因此, 現(xiàn)在所有多任務(wù)操作系統(tǒng)都在盡力優(yōu)化對動態(tài)內(nèi)存的使用, 也就是說, 盡可能做到當(dāng)需要時分配, 不需要時釋放.

0bfcda9a-9ccd-11ee-8b88-92fbcf53809c.jpg

當(dāng)給內(nèi)核分配動態(tài)內(nèi)存時, 是相對容易的, 有如下兩點(diǎn)原因:

內(nèi)核是操作系統(tǒng)中優(yōu)先級最高的成分. 如果某個內(nèi)核函數(shù)請求動態(tài)內(nèi)存, 那么, 必定有正當(dāng)?shù)睦碛砂l(fā)出這個請求, 因此, 沒有道理試圖推遲這個請求.

內(nèi)核信任自己. 所有的內(nèi)核函數(shù)都被假定是沒有錯誤的, 因此內(nèi)核函數(shù)不必針對程序錯誤施加任何保護(hù)措施.

而當(dāng)給用戶態(tài)進(jìn)程分配內(nèi)存時, 情況完全不同:

進(jìn)程對動態(tài)內(nèi)存的請求被認(rèn)為是不緊急的. 例如, 當(dāng)進(jìn)程對應(yīng)在磁盤上所存儲的可執(zhí)行文件被裝入內(nèi)存時, 進(jìn)程并不一定會立即對所有的代碼和數(shù)據(jù)進(jìn)行訪問. 類似地, 當(dāng)進(jìn)程調(diào)用malloc()以請求獲得額外的動態(tài)內(nèi)存時, 也并不意味著進(jìn)程很快就會訪問所獲得的額外的動態(tài)內(nèi)存.因此, 一般來說, 內(nèi)核總是盡量推遲給用戶態(tài)進(jìn)程分配動態(tài)內(nèi)存.

由于用戶進(jìn)程是不可信任的, 因此, 內(nèi)核必須能隨時準(zhǔn)備捕獲用戶態(tài)進(jìn)程引起的所有尋址錯誤.

為了使得動態(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)存時, 并沒有獲得請求的動態(tài)內(nèi)存, 而僅僅得到了對一個新的線性地址區(qū)間的使用權(quán), 這樣的線性地址區(qū)間有很多, 由允許進(jìn)程使用的全部線性地址區(qū)間所組成的集合就叫做進(jìn)程地址空間.

與進(jìn)程地址空間有關(guān)的全部信息都包含在一個叫做內(nèi)存描述符的數(shù)據(jù)結(jié)構(gòu)中 (實(shí)際上就是描述進(jìn)程虛擬內(nèi)存的數(shù)據(jù)結(jié)構(gòu)), 這個結(jié)構(gòu)的類型為mm_struct, 進(jìn)程描述符的mm字段就指向這個結(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ū)的字段如下:

0c149d2e-9ccd-11ee-8b88-92fbcf53809c.jpg

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)程地址空間的布局, 由一個一個的線性地址區(qū)間組成, 線性地址 (linear address), 也稱虛擬地址 (virtual address) 是一個 32 位無符號整數(shù) (unsigned long), 可以用來表示數(shù)值高達(dá) 4GB 的地址, 也就是 4,294,967,296 個內(nèi)存單元.線性地址通常用十六進(jìn)制數(shù)字表示, 值的范圍從 0x00000000 到 0xffffffff.

0c2a070e-9ccd-11ee-8b88-92fbcf53809c.jpg

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)程地址空間的布局相吻合.

0c3a7792-9ccd-11ee-8b88-92fbcf53809c.jpg

線性地址(虛擬地址)的集合稱為虛擬內(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). 每一個頁幀包含了一個頁, 也就是說一個頁幀的長度和一個頁的長度相同.頁和頁幀的區(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)稱為頁表.

0c5ca876-9ccd-11ee-8b88-92fbcf53809c.jpg

進(jìn)程地址空間, 頁表的存在有什么意義?

讓所有進(jìn)程以統(tǒng)一的視角看待內(nèi)存,進(jìn)程地址空間的存在讓我們在編寫程序的時候只需關(guān)注虛擬地址, 而無需關(guān)注數(shù)據(jù)在物理內(nèi)存當(dāng)中實(shí)際的存儲位置.

頁表的存在讓進(jìn)程在間接訪問內(nèi)存的時候, 增加一個轉(zhuǎn)換的過程, 在這個轉(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)行了解耦.

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • RAM
    RAM
    +關(guān)注

    關(guān)注

    8

    文章

    1368

    瀏覽量

    114640
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11292

    瀏覽量

    209322
  • 操作系統(tǒng)
    +關(guān)注

    關(guān)注

    37

    文章

    6801

    瀏覽量

    123283
  • 動態(tài)內(nèi)存
    +關(guān)注

    關(guān)注

    1

    文章

    24

    瀏覽量

    7974
  • 進(jìn)程
    +關(guān)注

    關(guān)注

    0

    文章

    203

    瀏覽量

    13960

原文標(biāo)題:Linux - 進(jìn)程 - 進(jìn)程地址空間

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    Linux如何證明線程共享進(jìn)程地址空間

    所有的書上都說,進(jìn)程中的所有線程共享進(jìn)程地址空間,如上圖中的藍(lán)框都在一個進(jìn)程中。那么該如何證明這個結(jié)論呢?
    發(fā)表于 08-25 16:22 ?480次閱讀
    <b class='flag-5'>Linux</b>如何證明線程共享<b class='flag-5'>進(jìn)程</b>的<b class='flag-5'>地址</b><b class='flag-5'>空間</b>

    Linux內(nèi)核地址映射模型與Linux內(nèi)核高端內(nèi)存詳解

    的數(shù)據(jù)可能不在內(nèi)存中。 Linux內(nèi)核地址映射模型 x86 CPU采用了段頁式地址映射模型。進(jìn)程代碼中的地址為邏輯
    發(fā)表于 05-08 10:33 ?3452次閱讀
    <b class='flag-5'>Linux</b>內(nèi)核<b class='flag-5'>地址</b>映射模型與<b class='flag-5'>Linux</b>內(nèi)核高端內(nèi)存<b class='flag-5'>詳解</b>

    Linux守護(hù)進(jìn)程詳解

    分享到:標(biāo)簽:進(jìn)程控制 Linux 守護(hù)進(jìn)程進(jìn)程 7.3 Linux守護(hù)進(jìn)程 7.3.1 守
    發(fā)表于 10-18 14:24 ?0次下載
    <b class='flag-5'>Linux</b>守護(hù)<b class='flag-5'>進(jìn)程</b><b class='flag-5'>詳解</b>

    linux進(jìn)程的深入理解

    每個進(jìn)程都有自己的堆棧,內(nèi)核在創(chuàng)建一個新的進(jìn)程時,在創(chuàng)建進(jìn)程控制塊 task struct 的同時,也為進(jìn)程創(chuàng)建堆棧。 一個進(jìn)程有 2個堆棧
    發(fā)表于 01-16 14:43 ?2次下載

    Linux進(jìn)程的內(nèi)存結(jié)構(gòu)

    Linux操作系統(tǒng)采用虛擬內(nèi)存管理技術(shù),使得每個進(jìn)程都有各自互不干涉的進(jìn)程地址空間。該地址
    發(fā)表于 06-01 09:17 ?1478次閱讀
    <b class='flag-5'>Linux</b>下<b class='flag-5'>進(jìn)程</b>的內(nèi)存結(jié)構(gòu)

    Linux進(jìn)程的創(chuàng)建、執(zhí)行和終止

     許多操作系統(tǒng)提供的都是產(chǎn)生進(jìn)程的機(jī)制,也就是說,首先在新的地址空間里創(chuàng)建進(jìn)程、讀入可執(zhí)行文件,后再開始執(zhí)行。Linux
    發(fā)表于 06-11 09:21 ?614次閱讀

    深入淺出Linux進(jìn)程地址空間

    我們知道,在32位機(jī)器上linux操作系統(tǒng)中的進(jìn)程地址空間大小是4G,其中0-3G是用戶空間,3G-4G是內(nèi)核
    的頭像 發(fā)表于 06-20 09:57 ?1940次閱讀

    Linux操作系統(tǒng)知識講解:走進(jìn)linux 內(nèi)存地址空間

    Linux操作系統(tǒng)知識講解:走進(jìn)linux 內(nèi)存地址空間
    的頭像 發(fā)表于 08-28 10:45 ?5038次閱讀
    <b class='flag-5'>Linux</b>操作系統(tǒng)知識講解:走進(jìn)<b class='flag-5'>linux</b> 內(nèi)存<b class='flag-5'>地址</b><b class='flag-5'>空間</b>

    Linux進(jìn)程

    內(nèi)核通過輕量級進(jìn)程 (lightweight process) 來支持多線程。1個輕量級進(jìn)程就對應(yīng)1個線程,輕量級進(jìn)程之間可以共享打開的文件、地址
    的頭像 發(fā)表于 11-29 09:51 ?2120次閱讀
    <b class='flag-5'>Linux</b>的<b class='flag-5'>進(jìn)程</b>

    mlock如何鎖住進(jìn)程地址空間關(guān)聯(lián)的物理內(nèi)存

    的應(yīng)用),Linux中提供了mlock相關(guān)的系統(tǒng)調(diào)用供用戶空間使用來鎖住部分或全部的地址空間關(guān)聯(lián)的物理頁面。 本文的分析基于arm64處理器架構(gòu),內(nèi)核版本為
    的頭像 發(fā)表于 03-14 09:36 ?1023次閱讀

    Linux進(jìn)程的內(nèi)存消耗和泄漏詳解

    當(dāng)我們評估進(jìn)程消耗多少內(nèi)存時,就是指在用戶空間消耗的內(nèi)存,即虛擬地址在0~3G的部分,對應(yīng)的物理地址內(nèi)存。內(nèi)核空間的內(nèi)存消耗屬于內(nèi)核,系統(tǒng)調(diào)
    的頭像 發(fā)表于 05-14 10:07 ?2679次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>進(jìn)程</b>的內(nèi)存消耗和泄漏<b class='flag-5'>詳解</b>

    Linux程序地址空間詳解

    在正式講程序[地址空間]前我們先來看一段簡單的代碼來分析分析。
    的頭像 發(fā)表于 03-26 10:39 ?656次閱讀

    為什么進(jìn)程地址空間中要包括操作系統(tǒng)(內(nèi)核)呢?

    這張圖就是Linux程序運(yùn)行起來后所謂的進(jìn)程地址空間,這里包括我們熟悉的代碼區(qū)、數(shù)據(jù)區(qū)、以及堆區(qū)和棧區(qū)。
    的頭像 發(fā)表于 04-18 09:09 ?1082次閱讀

    Linux系統(tǒng)為什么需要引入虛擬地址

    Linux 系統(tǒng)中,采用了虛擬內(nèi)存管理技術(shù),事實(shí)上大多數(shù)現(xiàn)在操作系統(tǒng)都是如此!在 Linux 系統(tǒng)中,每一個進(jìn)程都在自己獨(dú)立的地址空間
    的頭像 發(fā)表于 10-07 17:28 ?945次閱讀
    <b class='flag-5'>Linux</b>系統(tǒng)為什么需要引入虛擬<b class='flag-5'>地址</b>

    Linux虛擬地址空間和物理地址空間的關(guān)系

    過程,這其實(shí)也是MMU的工作原理。 我們知道,在Linux中,每個進(jìn)程都有自己獨(dú)立的地址空間,且互不干擾。每個進(jìn)程
    的頭像 發(fā)表于 10-08 11:40 ?1198次閱讀
    <b class='flag-5'>Linux</b>虛擬<b class='flag-5'>地址</b><b class='flag-5'>空間</b>和物理<b class='flag-5'>地址</b><b class='flag-5'>空間</b>的關(guān)系
    RM新时代网站-首页