[操作系統] 進程地址空間管理

虛擬地址空間的初始化

缺頁中斷

缺頁中斷的概念

缺頁中斷(Page Fault Interrupt) 是指當程序訪問的虛擬地址在頁表中不存在有效映射(即該頁未加載到內存中)時,CPU 會發出一個中斷信號,請求操作系統加載所需的頁面到內存。
這是現代操作系統實現 虛擬內存管理 的重要機制之一。


缺頁中斷的觸發條件

缺頁中斷通常在以下情況下觸發:

  1. 虛擬地址對應的頁面不在內存中:
    • 頁表中找不到對應的物理頁幀(頁表條目為空或無效)。
    • 常見于程序訪問未加載到內存的代碼段或數據段。
  2. 訪問非法地址:
    • 程序試圖訪問一個不存在的虛擬地址(如超出地址空間范圍)。
    • 操作系統會判斷訪問是否合法,非法訪問將觸發異常。

缺頁中斷的處理流程

以下是缺頁中斷的詳細處理流程:

  1. 程序訪問虛擬地址:
    • CPU 將虛擬地址拆分為頁號和頁內偏移量,根據頁號查詢頁表。
  2. 檢測頁表:
    • 如果頁表中沒有找到對應的物理頁幀(即頁表項無效),觸發缺頁中斷。
  3. 陷入內核:
    • 缺頁中斷引發陷入操作系統內核,操作系統負責處理。
  4. 檢查頁面是否合法:
    • 操作系統檢查虛擬地址是否屬于當前進程的合法地址范圍:
      • 如果地址非法(如訪問未分配的堆空間),操作系統會終止進程,拋出段錯誤(Segmentation Fault)。
      • 如果合法,進入下一步。
  5. 分配頁面:
    • 操作系統為該虛擬頁分配物理頁幀。
    • 如果內存不足,則觸發頁面置換算法(如LRU、FIFO),將某些頁面換出到硬盤(即交換分區或頁面文件)。
  6. 加載頁面:
    • 如果訪問的頁面是磁盤文件的一部分(如代碼或數據),則將頁面從磁盤加載到內存。
    • 將分配的物理頁幀的地址填入頁表,并將頁表項標記為有效。
  7. 恢復程序執行:
    • 操作系統返回用戶態,重新執行導致缺頁的指令。
    • 由于頁面已加載到內存,訪問可以正常完成。

缺頁中斷的示例

假設一個程序試圖訪問數組元素,但數組較大,未全部加載到內存。以下是可能的情景:

int arr[100000]; // 數組在內存中未完全加載
for (int i = 0; i < 100000; i++) {arr[i] = i;  // 順序訪問
}
  1. 程序首次訪問 arr[i] 時,CPU 查詢頁表,發現對應的虛擬頁面未映射到物理內存,觸發缺頁中斷。
  2. 操作系統加載對應頁面到內存,并更新頁表。
  3. 下一次訪問已加載的頁面時,程序可以直接讀取,無需觸發缺頁中斷。

示例總結

當一個應用程序數據過大的話,不會立即將所有的數據全部從硬盤上加載到物理內存中,會先加載一部分。但是在進程的虛擬地址空間中會將所有的數據對應的地址全部建立。于是當需要使用一個虛擬地址空間的時候會在頁表中進行查找映射的物理地址,但是沒有物理地址,還未加載進內存中。操作系統會動態加載數據,當需要的時候再申請物理空間,加載數據,然后建立映射關系。

即使加載到物理內存的數據是亂序存儲的,通過頁表的映射關系也可以進行有序的管理。

虛擬地址空間初始化

虛擬地址空間就是mm_struct,在task_struct中。作為對象,需要被初始化。

當一個新進程被創建(例如通過 forkexec 系統調用)時,操作系統會:

  1. 創建虛擬地址空間:
    • 為進程分配獨立的虛擬地址空間,確保不同進程之間的地址空間隔離。
  2. 設置頁表:
    • 頁表初始狀態為“未映射”(即頁面不在物理內存中),以支持按需加載。
  3. 加載程序的基礎信息:
    • 通過程序文件(如 ELF 文件)中的頭部信息,劃分代碼段、數據段等區域。
    • 堆區和棧區只初始化元信息(如起始地址),實際分配時動態增長。
    • 所以大部分會在程序加載的時候就被初始化。

為什么要有進程地址空間

將地址從無需變有序

數據從磁盤加載到物理內存是動態加載的,順序會變得無規則,甚至亂序。但是有了虛擬地址空間和頁表,虛擬地址空間中各個區域的地址是有序的,然后通過頁表進行映射,找到無序的物理內存地址,從而將物理地址進行有序管理。

地址轉換時的合法性判定

當地址轉換的時候,通過虛擬地址空間和頁表可以對地址和操作進行合法性判定,防止直接操作物理內存造成損壞,進而保護物理內存

頁表中對于每一個映射關系也會有權限(rwx...)存在,當進程又不合法操作的時候,操作系統會拒絕地址映射轉換,甚至殺死進程。

野指針

從操作系統層面理解野指針:

  • 未初始化指針與頁表:當一個指針未初始化時,它指向的虛擬地址是隨機的。這個隨機地址很可能在頁表中沒有對應的映射項。因為正常的內存分配(如通過mallocnew等操作)會由操作系統分配一段合法的虛擬地址,并在頁表中建立映射。而未初始化的指針所指向的地址沒有經過這樣的分配過程,所以在頁表中找不到對應的物理地址。
  • 已釋放指針與頁表:指針所指向的內存被釋放后,操作系統會將這塊內存對應的頁表項標記為未使用或者分配給其他進程。如果繼續使用這個指針訪問內存,操作系統在查找頁表時會發現這個虛擬地址對應的頁表項已經不再有效。例如,一個動態分配的內存塊被deletefree后,它所占用的虛擬地址范圍在頁表中的映射會被撤銷或者改變,再次訪問這個地址就會引發錯誤。
  • 越界指針與頁表:當指針越界時,它可能會指向虛擬地址空間中未分配的區域。比如,一個數組指針越界后指向了數組之外的地址,這個地址可能超出了操作系統為該數組分配的合法虛擬地址范圍。在頁表中,這個越界的地址沒有合法的物理地址映射,因為操作系統只會在合法的內存分配范圍內建立頁表映射。
  • 錯誤賦值指針與頁表:如果指針被錯誤地賦值為一個非法的地址,這個地址在頁表中很可能沒有對應的映射。因為操作系統在進行正常的內存分配時,會確保分配的虛擬地址在頁表中有合法的映射。而人為錯誤地給指針賦一個非法地址,打破了這種正常的映射關系。

查頁表失敗后,會反饋給操作系統,操作系統會處理進程,所以野指針會導致操作系統殺死進程,導致進程崩潰。

字符串常量為什么無法修改

常量字符串字面量(如<font style="color:rgb(6, 6, 7);">char* ptr = "Hello, World!"</font>)通常被存儲在代碼段(Text Segment)中。這是因為常量字符串在程序的整個運行過程中不需要修改,將它們放在代碼段可以利用代碼段的只讀特性來保護這些字符串不被意外修改。

這就是在地址轉換的時候權限拒絕了對數據的寫操作,所以無法修改。

解耦合!

簡單回顧一下程序加載進內存的過程:

  1. 在虛擬地址空間申請指定大小的空間(調整區域劃分)。
  2. 加載程序,申請物理空間。
  3. 在頁表中進行虛擬地址和物理地址的映射構建。

完成后,此時的物理地址就轉化為了虛擬地址。提供給上層使用,用戶無需關心底層的物理地址是什么,物理內存中是如何加載的,只是使用虛擬地址就可以了。

作為進程也只是關心對于虛擬地址的使用,而不關心實際物理內存的存儲。

如圖所示。task_struct和其中管理的mm_struct所形成的關系對于進程來說只是負責進程的調度,而不關心如何管理調度的數據存儲和加載。

而對于物理內存部分來說,只進行內存的管理,加載物理內存。

二者通過頁表的映射關系來解耦合,讓進程管理和內存管理進行一定程度的解耦合!

Tips

  1. 可以不加載代碼和數據,只加載task_structmm_struct(只拿到main代碼的起始地址),頁表。

CPU在拿到起始地址后,當訪問虛擬地址時,會有標識證明沒有加載過物理內存,所以會缺頁中斷,然后開始加載物理內存。

  1. 創建進程的時候,先有task_struct,mm_struct等,還是先加載代碼和數據?

先有內核數據結構,然后再陸續加載物理內存。

  1. 如何理解進程掛起?

進程進入掛起狀態時,操作系統找到對應的進程,清空頁表的物理地址部分,將物理地址對應的數據全部換入磁盤swap分區。只保留虛擬地址空間中的虛擬地址和頁表的虛擬地址部分。

當掛起狀態結束時,將swap分區的數據全部換出加載到物理內存中,然后再頁表中建立映射。這就是解耦的好處,將進程調度與內存管理完全解耦。

vm_area_struct

對于在程序中動態申請的空間來說,一般會申請在堆區中。但是在程序中可能會頻繁的申請,難道對于虛擬地址空間中的堆區來說,不只有一個起始地址嗎?可能是離散分布的嗎?

虛擬空間的組織?式有兩種:

  1. 當虛擬區較少時采取單鏈表,由mmap指針指向這個鏈表;
  2. 當虛擬區間多時采取紅?樹進?管理,由mm_rb指向這棵樹。

mm_struct 并不是直接就是整個虛擬地址空間,而是包含了一個指向虛擬內存區域(VMA)列表的指針,這個列表是由 vm_area_struct組成的。每個 vm_area_struct 表示進程地址空間中的一個連續區域,具有相同的權限和映射類型。所以離散申請的堆空間可以用vm_area_struct進行管理,并且所有的區域都可以統一使用vm_area_struct進行管理。

為了高效查找區域,所以用紅黑樹來進行管理——struct rb_root mm_rb

struct mm_struct {// ... 其他成員 ...struct vm_area_struct *mmap; // 指向虛擬內存區域列表的指針struct rb_root mm_rb;         // 紅黑樹,用于快速查找虛擬內存區域// ... 其他成員 ...
};
struct vm_area_struct {struct mm_struct *vm_mm;         // 指向所屬進程的mm_structunsigned long vm_start;          // 虛擬內存區域的起始地址unsigned long vm_end;            // 虛擬內存區域的結束地址pgprot_t vm_page_prot;           // 頁面保護標志unsigned long vm_flags;           // 標志位,如VM_READ, VM_WRITE等// ... 其他成員,如鏈表指針、紅黑樹節點等 ...
};

因為存在vm_area_struct這個數據結構,即使堆區頻繁申請,但是每一段申請的空間都可以使用vm_area_struct來進行管理,最后用建表進行管理所有的vm_area_struct,在mm_struct中用*mmap作為鏈表的頭指針。

進程地址空間管理(總結)

關于進程地址空間整體的管理結構如上圖所示(虛擬區間較少情況下)。

task_struct管理整體進程,其中包括管理進程地址空間的mm_struct。在虛擬區間較少的情況下用在mm_struct中的vm_area_struct *mmap;指向由vm_area_struct鏈接而成的鏈表。每個vm_area_struct對應一段虛擬區間,而對于堆這種可能頻繁申請的來說,堆這個區間會由多個vm_area_struct組成,而其他的區域使用一個vm_area_struct管理即可。

為什么要有進程地址空間(總結)

在早期的計算機中,程序直接操作物理內存,所有內存地址都是物理地址。當同時運行多個程序時,內存管理必須確保程序使用的內存總量小于物理內存。然而,這種直接操作物理內存的方式存在以下問題:


安全風險
  • 直接操作物理內存允許每個進程訪問任意內存空間。
  • 惡意程序(如木馬病毒)可以隨意修改系統內存區域,導致設備癱瘓。

地址不確定性
  • 編譯完成的程序存儲在硬盤上,運行時需加載到內存。
  • 由于內存分配動態變化,程序的物理內存地址在不同運行時可能不同。
    • 例如:第一次運行時,程序加載到地址0x00000000
    • 第二次運行時,內存已被占用,加載地址可能變為其他位置。

效率低下
  • 使用物理內存時,進程以整體內存塊操作。
  • 當物理內存不足時,將進程從內存拷貝到磁盤(交換分區)需要拷貝整個進程,耗時較長,效率低下。

虛擬地址空間與分頁機制的優勢

1. 內存安全
  • 地址空間和頁表由操作系統創建和維護。
  • 所有地址空間和頁表映射必須經過操作系統監管。
  • 保護物理內存中的合法數據,防止非法訪問。
2. 地址靈活性
  • 地址空間和頁表映射機制允許物理內存中的數據以任意位置加載。
  • 內存分配與進程管理解耦,進程不再直接依賴物理內存地址。
3. 延遲分配
  • 在C/C++中,通過newmalloc申請的空間實際上分配在虛擬地址空間。
  • 物理內存分配延遲到真正訪問內存時執行。
  • 操作系統自動完成內存分配和頁表映射,用戶或進程無需感知。
4. 提高效率
  • 程序在物理內存中的加載可以是任意位置,通過頁表實現虛擬地址與物理地址的映射。
  • 虛擬地址空間使得進程內存分布在邏輯上保持有序,簡化了程序管理。

總結

虛擬地址空間通過操作系統的地址空間管理和頁表機制,解決了直接操作物理內存帶來的安全性、靈活性和效率問題,使得內存管理更安全、更高效,同時簡化了程序開發與運行。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/67053.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/67053.shtml
英文地址,請注明出處:http://en.pswp.cn/web/67053.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HTML5 Web Worker 的使用與實踐

引言 在現代 Web 開發中&#xff0c;用戶體驗是至關重要的。如果頁面在執行復雜計算或處理大量數據時變得卡頓或無響應&#xff0c;用戶很可能會流失。HTML5 引入了 Web Worker&#xff0c;它允許我們在后臺運行 JavaScript 代碼&#xff0c;從而避免阻塞主線程&#xff0c;保…

Nginx配置中的常見錯誤:SSL參數解析

摘要 在高版本的Nginx中&#xff0c;用戶可能會遇到unknown directive “ssl”的錯誤提示。這是因為舊版本中使用的ssl on參數已被棄用。正確的配置SSL加密的方法是在listen指令中添加ssl參數。這一改動簡化了配置流程&#xff0c;提高了安全性。用戶應更新配置文件以適應新版本…

適用于IntelliJ IDEA 2024.1.2部署Tomcat的完整方法,以及筆者踩的坑,避免高血壓,保姆級教程

Tips:創建部署Tomcat直接跳轉到四 一、軟件準備 筆者用的是IntelliJ IDEA 2024.1.2和Tomcat 8.5。之前我使用的是Tomcat 10&#xff0c;但遇到了許多問題。其中一個主要問題是需要使用高于1.8版本的JDK&#xff0c;為此我下載了新的JDK版本&#xff0c;但這又引發了更多的兼容…

微信閱讀網站小程序的設計與實現(LW+源碼+講解)

專注于大學生項目實戰開發,講解,畢業答疑輔導&#xff0c;歡迎高校老師/同行前輩交流合作?。 技術范圍&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容&#xff1a;…

從零開始學 HTML:構建網頁的基本框架與技巧

系列文章目錄 01-從零開始學 HTML&#xff1a;構建網頁的基本框架與技巧 文章目錄 系列文章目錄前言一、HTML 文檔的基本框架1.1 <!DOCTYPE html>、<html>、<head>、<body> 標簽解析1.1.1 <!DOCTYPE html> 標簽1.1.2 <html> 標簽1.1.3 &l…

C#加密方式

using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Security.Cryptography;using System.Text;namespace PwdDemo{public class AESHelper{/// <summary>/// AES 加密/// </summary>/// <param name"str&qu…

【12】WLC配置internal DHCP服務器

1.概述 WLC無線控制器包含內部DHCP(internal DHCP)服務器。該功能通常用于尚未擁有DHCP服務器的分支機構中。 無線網絡通常包含最多10個AP或更少的AP,并且AP在與控制器的同一IP子網上。內部DHCP服務器為無線客戶端、直連AP和從AP中繼的DHCP請求提供了DHCP地址。 2.內部DHC…

vue2中trhee.js加載模型展示天空盒子

![在這里插入圖片描述](https://i-blog.csdnimg.cn/direct/13b9193d6738428791fc1ff112e03627.png 加載模型的時候需要把模型放在public文件下面 創建場景 this.scene new THREE.Scene()創建相機 this.camera new THREE.PerspectiveCamera(45,this.viewNode.clientWidth / t…

汽車免拆診斷案例 | 2007 款日產天籟車起步加速時偶爾抖動

故障現象  一輛2007款日產天籟車&#xff0c;搭載VQ23發動機&#xff08;氣缸編號如圖1所示&#xff0c;點火順序為1-2-3-4-5-6&#xff09;&#xff0c;累計行駛里程約為21萬km。車主反映&#xff0c;該車起步加速時偶爾抖動&#xff0c;且行駛中加速無力。 圖1 VQ23發動機…

對神經網絡基礎的理解

目錄 一、《python神經網絡編程》 二、一些粗淺的認識 1&#xff09; 神經網絡也是一種擬合 2&#xff09;神經網絡不是真的大腦 3&#xff09;網絡構建需要反復迭代 三、數字圖像識別的實現思路 1&#xff09;建立一個神經網絡類 2&#xff09;權重更新的具體實現 3&am…

新項目傳到git步驟

1.首先創建遠程倉庫,創建一個空白項目,即可生成一個克隆URL,可以是http也可以是SSH,copy下這個地址 2.找到項目的本機目錄,進入根目錄,打開git bash here命令行 3.初始化: git init 4.關聯遠程地址: git remote add origin "遠程倉庫的URL" 5.查看關聯 git re…

PAT甲級-1024 Palindromic Number

題目 題目大意 一個非回文數&#xff0c;加上它的翻轉數所得的和&#xff0c;進行k次&#xff0c;有可能會得到一個回文數。給出一個數n&#xff0c;限制相加次數為k次&#xff0c;如果小于k次就得到回文數&#xff0c;那么輸出該回文數和相加的次數&#xff1b;如果進行k次還…

appium自動化環境搭建

一、appium介紹 appium介紹 appium是一個開源工具、支持跨平臺、用于自動化ios、安卓手機和windows桌面平臺上面的原生、移動web和混合應用&#xff0c;支持多種編程語言(python&#xff0c;java&#xff0c;Ruby&#xff0c;Javascript、PHP等) 原生應用和混合應用&#xf…

C#高級:常用的擴展方法大全

1.String public static class StringExtensions {/// <summary>/// 字符串轉List&#xff08;中逗 英逗分隔&#xff09;/// </summary>public static List<string> SplitCommaToList(this string data){if (string.IsNullOrEmpty(data)){return new List&…

【Numpy核心編程攻略:Python數據處理、分析詳解與科學計算】1.1 從零搭建NumPy環境:安裝指南與初體驗

1. 從零搭建NumPy環境&#xff1a;安裝指南與初體驗 NumPy核心能力圖解&#xff08;架構圖&#xff09; NumPy 是 Python 中用于科學計算的核心庫&#xff0c;它提供了高效的多維數組對象以及用于處理這些數組的各種操作。NumPy 的核心能力可以概括為以下幾個方面&#xff1a…

【SpringBoot教程】Spring Boot + MySQL + HikariCP 連接池整合教程

&#x1f64b;大家好&#xff01;我是毛毛張! &#x1f308;個人首頁&#xff1a; 神馬都會億點點的毛毛張 在前面一篇文章中毛毛張介紹了SpringBoot中數據源與數據庫連接池相關概念&#xff0c;今天毛毛張要分享的是關于SpringBoot整合HicariCP連接池相關知識點以及底層源碼…

Java進階(一)

目錄 一.Java注解 什么是注解&#xff1f; 內置注解 元注解 二.對象克隆 什么是對象克隆? 為什么用到對象克隆 三.淺克隆深克隆 一.Java注解 什么是注解&#xff1f; java中注解(Annotation)又稱java標注&#xff0c;是一種特殊的注釋。 可以添加在包&#xff0c;類&…

【PyCharm】將包含多個參數的 shell 腳本配置到執行文件來調試 Python 程序

要配置 PyCharm 以使用包含多個參數的 shell 腳本&#xff08;如 run.sh&#xff09;來調試 Python 程序&#xff0c;您可以按照以下步驟操作&#xff1a; 創建一個新的運行/調試配置&#xff1a; 在 PyCharm 中&#xff0c;點擊“運行”菜單旁邊的齒輪圖標&#xff0c;選擇“…

即夢(Dreamina)技術淺析(二):后端AI服務

1. 文本處理(Text Processing) 1.1 功能概述 文本處理模塊的主要任務是將用戶輸入的文字提示詞轉換為機器可以理解的向量表示。這一過程包括分詞、詞嵌入和語義編碼,旨在捕捉文本的語義信息,為后續的圖像和視頻生成提供準確的指導。 1.2 關鍵技術 1.分詞(Tokenization…

藍橋杯之c++入門(一)【第一個c++程序】

目錄 前言一、第?個C程序1.1 基礎程序1.2 main函數1.3 字符串1.4 頭文件1.5 cin 和 cout 初識1.6 名字空間1.7 注釋 二、四道簡單習題&#xff08;點擊跳轉鏈接&#xff09;練習1&#xff1a;Hello,World!練習2&#xff1a;打印飛機練習3&#xff1a;第?個整數練習4&#xff…