Linux進程地址空間、寫時拷貝

1.進程地址空間

感知進程地址空間

C/C++有內存的概念,內存空間包括棧、堆、代碼段等等,下面是32位下的內存分布圖,自底向上(由0x000000000xFFFFFFFF);
在這里插入圖片描述

下面通過程序來驗證各個數據在該空間的地址,由此感知整個地址空間的分布情況。

#include <stdio.h>
#include <stdlib.h>int g_val= 10;	// 初始化數據
int un_g_val;	// 未初始化數據int main(int argc, char *argv[], char *env[])
{printf("code addr			    :%p\n", main);		// main函   數地址,即為程序開始的地址printf("init global     addr		:%p\n", &g_val);	// 初始化數據地址printf("uninit global   addr		:%p\n", &un_g_val);// 未初始化數據地址char *m1 = (char*)malloc    (sizeof(char)*10);char *m2 = (char*)malloc    (sizeof(char)*10);printf("heap1 addr			    :%p\n", m1);		// 先申請的堆空間變量地址printf("heap2 addr			    :%p\n", m2);		// 后申請的堆空間變量地址printf("stack1 addr			    :%p\n", &m1);		// 先申請的棧空間變量地址printf("stack2 addr			    :%p\n", &m2);		// 后申請的棧空間變量地址int i = 0;for (i = 0; argv[i]; i++){printf("argv%d addr			:%p\n", i, &argv[i]);		}for (i = 0; env[i]; i++){printf("env%d addr			:%p\n", i, &env[i]);		}return 0;
}

程序運行后結果如下,完全符合上圖的地址分布:
由低到高依次為正文代碼段->初始化數據->未初始化數據->堆空間->棧空間->命令行參數環境變量
同時,棧和堆中間有一大塊空白區域且向中間漸進。

root@hcss-ecs-e6eb:~/learning/Linux-learning/Process# ./myprocess 
code addr			:0x558e4fa8a189
init global addr		:0x558e4fa8d010
uninit global addr		:0x558e4fa8d018
heap1 addr			:0x558e514606b0
heap2 addr			:0x558e514606d0
stack1 addr			:0x7fff4a3a7618
stack2 addr			:0x7fff4a3a7620
argv0 addr			:0x7fff4a3a7748
env0 addr			:0x7fff4a3a7758
env1 addr			:0x7fff4a3a7760
env2 addr			:0x7fff4a3a7768
env3 addr			:0x7fff4a3a7770
env4 addr			:0x7fff4a3a7778
env5 addr			:0x7fff4a3a7780
...

上述地址是在64位機器打印出來的,但實際上該機器內存只有2GB,而實際感知到的內存空間為 2 64 B = 2 34 G B 2^{64}B=2^{34}GB 264B=234GB,這中間可是相差了很多,可以初步推斷:C/C++所謂的內存地址不是物理地址,而是一個虛擬地址/線性地址

通過下面的程序可以更好地進行理解:設置一個全局變量g_val,父子進程都擁有這個全局變量,觀察子進程修改該全局變量值前后該變量在父子進程的狀態變化。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int g_val = 100;int main()
{pid_t id = fork();if (id == 0){while (1){printf("I'm child process:%d, ppid:%d,  g_val:%d, &g_val:%p\n", getpid(), getppid(), g_val, &g_val);g_val = 200;printf("after modify, I'm child process:%d, ppid:%d, g_val:%d, &g_val:%p\n", getpid (), getppid(),   g_val, &g_val);sleep(2);}}else {while (1){printf("I'm father process:%d, ppid:%d, g_val:%d, & g_val:%p\n", getpid(), getppid(), g_val, &g_val);sleep(2);}   }return 0;
}

可以看到,修改前g_val的值和地址在父子進程中都是一致的,但是子進程將g_val的值進行修改之后,雖然值不同,但是地址相同。若該地址是真實的物理地址,顯然不可能出現這種情況,C/C++中使用的地址是虛擬地址。

I'm father process:1068014, ppid:1061991, g_val:100, &g_val:0x55a8287d7010
I'm child process:1068015, ppid:1068014, g_val:100, &g_val:0x55a8287d7010
after modify, I'm child process:1068015, ppid:1068014, g_val:200, &g_val:0x55a8287d7010
I'm father process:1068014, ppid:1061991, g_val:100, &g_val:0x55a8287d7010
I'm child process:1068015, ppid:1068014, g_val:200, &g_val:0x55a8287d7010

虛擬地址在Linux下也稱為線性地址
內存是硬件,操作系統肯定不會讓用戶直接進行操作,當進程分配到CPU資源后,CPU通過一個虛擬地址來訪問貯存,這個虛擬地址被送到內存之前先轉換成適當的物理地址,這一任務叫地址翻譯。CPU芯片上叫做內存管理單元(Memory Management Unit, MMU)的專用硬件,利用存放在主存中的查詢表來動態翻譯虛擬地址,該表的內容由操作系統管理。

在這里插入圖片描述

同一變量地址相同,其實是虛擬地址相同,內容不同是因為映射到了不同的物理地址。 這也能解釋為什么fork函數會有兩個返回值,兩個返回id雖然同一虛擬地址,但是其指向的物理空間卻不相同。

認識進程地址空間

地址空間(address space)是一個非負整數地址的有序集合。

操作系統通過進程控制塊task_struct來管理進程,每個進程都有一塊獨立的進程地址空間,這個空間不是物理上內存真正存在的空間task_struct有一個類型為mm_struct的成員,操作系統通過進程控制塊中的mm_struct來管理這塊空間。在mm_struct結構體中,通過記錄各段的首尾地址即區間方式來維護各段。

struct task_struct{//.../* 進程地址空間 1) mm: 指向進程所擁有的內存描述符 2) active_mm: active_mm指向進程運行時所使用的內存描述符對于普通進程而言,這兩個指針變量的值相同。但是,內核線程不擁有任何內存描述符,所以它們的mm成員總是為NULL。當內核線程得以運行時,它的active_mm成員被初始化為前一個運行進程的active_mm值*/struct mm_struct *mm, *active_mm;//...
};struct mm_struct{//...//維護代碼段和數據段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;//...// 頁表指針pgd_t * pgd;  
}

概念上而言,虛擬內存被組織為一個由存放在磁盤上的N個連續的字節大小的單元組成的數組。每字節都有一個唯一的虛擬地址,作為到數組的索引。磁盤上數組的內容被緩存到主存中。

除此之外,mm_struct中還有一個重要成員頁表指針,該指針指向了一個存放在物理內存中叫做頁表(page table)的數據結構,每個進程指向一個頁表,頁表是一種映射關系,虛擬地址通過MMU得到虛擬頁號和頁內偏移量,虛擬頁號通過頁表映射到該物理頁號,MMU拼接上頁內偏移量就能得到最終物理地址

在這里插入圖片描述

操作系統通過地址空間+頁表,將物理內存不連續的區域統一映射到同一塊區域,讓進程以統一的視角來看待內存。同時進程管理和內存管理解耦合,方便OS設計。地址空間+頁表是保護內存安全的重要手段。

2.寫時拷貝

寫時拷貝(copy-on-write, COW)就是等到修改數據時才真正分配內存空間,這是對程序性能的優化,可以延遲甚至是避免內存拷貝,當然目的就是避免不必要的內存拷貝。

進程這一抽象能夠為每個進程提供自己的私有的虛擬地址空間即進程地址空間,可以免受其他進程的錯誤讀寫。
不過,許多進程有同樣的只讀代碼區域。例如,每個運行的Linux Shell 程序 bash的進程都有相同的代碼區域。
那么如果,每個進程都在物理內存中保持這些常用代碼的副本,就是極端的浪費了。

一個對象可以被映射到虛擬內存的一個區域,要么作為共享對象,要么作為私有對象
一個進程對共享對象的任何寫操作,對其他將該共享對象映射到虛擬內存中的進程都是可見的。不可見的即是私有對象


假設進程1將一個共享對象映射到它的虛擬內存的一個區域中,若假設進程2將同一共享對象也映射到它的地址空間(并不一定要和進程1在相同的虛擬地址處),會出現下圖的情況,以節省物理內存的消耗。即使對象被映射到多個共享區域,物理內存中也只需要存放共享對象的一個副本。

在這里插入圖片描述

私有對象用一種叫做寫時拷貝的巧妙技術被映射到虛擬內存中。私有對象開始生命周期的方式與共享對象的一樣,在物理內存中只保存有私有對象的一個副本。
就拿上面程序中父子進程來說,子進程修改g_val之前,兩個進程都將其私有對象(代碼、數據、g_val等等)映射到他們虛擬內存的不同區域,但是共享這個對象的同一物理副本,修改g_val前,此時相應私有區域都被標記為只讀。
在這里插入圖片描述

然而子進程修改g_val時,子進程試圖寫私有區域的某個頁面,這個寫操作會觸發一個保護故障
當故障處理程序注意到保護異常是由于進程試圖寫私有的寫時拷貝區域中的一個頁面引起的,它就會在物理內存中創建這個頁面的一個新副本,更新頁表條目指向新的副本,然后恢復這個頁面的可寫權限。

在這里插入圖片描述

寫時拷貝充分地使用了最稀有的物理內存。

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

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

相關文章

python成功解決AttributeError: can‘t set attribute ‘lines‘

文章目錄 報錯信息與原因分析解決方法示例代碼代碼解釋總結 報錯信息與原因分析 在使用 matplotlib繪圖時&#xff0c;若嘗試使用 ax.lines []來清除圖表中的線條&#xff0c;會遇到AttributeError: can’t set attribute錯誤。這是因為 ax.lines是一個只讀屬性&#xff0c;不…

從零搭建微服務項目Pro(第6-2章——微服務鑒權模塊SpringSecurity+JWT)

前言&#xff1a; 在上一章已經實現了SpringBoot單服務的鑒權&#xff0c;在導入SpringSecurity的相關依賴,以及使用JWT生成的accessToken和refreshToken能夠實現不同Controller乃至同一Controller中不同接口的權限單獨校驗。上一章鏈接如下&#xff1a; 從零搭建微服務項目Pr…

win安裝軟件

win安裝軟件 jdk安裝 jdk安裝 首先去官網下載適合系統版本的JDK&#xff0c;下載地址&#xff1a; http://www.oracle.com/technetwork/java/javase/downloads/index.html進入下載頁面&#xff0c;如下圖&#xff1a; 首先選擇&#xff1a;Accept License Agreement單選按鈕&…

Prompt-Tuning 提示詞微調

1. Hard Prompt 定義&#xff1a; Hard prompt 是一種更為具體和明確的提示&#xff0c;要求模型按照給定的信息生成精確的結果&#xff0c;通常用于需要模型提供準確答案的任務. 原理&#xff1a; Prompt Tuning原理如下圖所示&#xff1a;凍結主模型全部參數&#xff0c;在…

【Vue生命周期的演變:從Vue 2到Vue 3的深度剖析】

Vue生命周期的演變&#xff1a;從Vue 2到Vue 3的深度剖析 1. 生命周期鉤子的概念與意義 Vue框架通過生命周期鉤子函數使開發者可以在組件不同階段執行自定義邏輯。這些鉤子函數是Vue組件生命周期中的關鍵切入點&#xff0c;對于控制組件行為至關重要。 2. Vue 2中的生命周期…

java ai 圖像處理

Java AI 圖像處理 圖像處理是人工智能&#xff08;AI&#xff09;領域中非常重要的一個應用方向。通過使用Java編程語言和相應的庫&#xff0c;我們可以實現各種圖像處理任務&#xff0c;如圖像識別、圖像分類、圖像分割等。本文將介紹一些常見的圖像處理算法&#xff0c;并通過…

從 0~1 保姆級 詳細版 PostgreSQL 數據庫安裝教程

PostgreSQL數據庫安裝 PostgreSQL官網 【PostgreSQL官網】 | 【PostgreSQL安裝官網_Windows】 安裝步驟 step1&#xff1a; 選擇與電腦相對應的PostgreSQL版本進行下載。 step2&#xff1a; 雙擊打開剛才下載好的文件。 step3&#xff1a; 在彈出的setup窗口中點擊 …

Keil MDK中禁用半主機(No Semihosting)

在 ARM 編譯器&#xff08;如 Keil MDK&#xff09; 中禁用半主機&#xff08;Semihosting&#xff09;并實現標準庫的基本功能&#xff0c;需要以下步驟&#xff1a; 1. 禁用半主機 #pragma import(__use_no_semihosting) // 禁用半主機模式作用&#xff1a;防止標準庫函數&…

github | 倉庫權限管理 | 開權限

省流版總結&#xff1a; github 給別人開權限&#xff1a;倉庫 -> Setting -> Cllaborate -> Add people GitHub中 將公開倉庫改為私有&#xff1a;倉庫 -> Setting -> Danger Zone&#xff08;危險區&#xff09; ->Change repository visibility( 更改倉…

快速部署大模型 Openwebui + Ollama + deepSeek-R1模型

背景 本文主要快速部署一個帶有web可交互界面的大模型的應用&#xff0c;主要用于開發測試節點&#xff0c;其中涉及到的三個組件為 open-webui Ollama deepSeek開放平臺 首先 Ollama 是一個開源的本地化大模型部署工具,提供與OpenAI兼容的Api接口&#xff0c;可以快速的運…

極狐GitLab 項目導入導出設置介紹?

極狐GitLab 是 GitLab 在中國的發行版&#xff0c;關于中文參考文檔和資料有&#xff1a; 極狐GitLab 中文文檔極狐GitLab 中文論壇極狐GitLab 官網 導入導出設置 (BASIC SELF) 導入和導出相關功能的設置。 配置允許的導入源 在從其他系統導入項目之前&#xff0c;必須為該…

信奧還能考嗎?未來三年科技特長生政策變化

近年來&#xff0c;科技特長生已成為名校錄取的“黃金敲門磚”。 從CSP-J/S到NOI&#xff0c;編程競賽成績直接關聯升學優勢。 未來三年&#xff0c;政策將如何調整&#xff1f;家長該如何提前布局&#xff1f; 一、科技特長生政策趨勢&#xff1a;2025-2027關鍵變化 1. 競…

AI測試用例生成平臺

AI測試用例生成平臺 項目背景技術棧業務描述項目展示項目重難點 項目背景 針對傳統接口測試用例設計高度依賴人工經驗、重復工作量大、覆蓋場景有限等行業痛點&#xff0c;基于大語言模型技術實現接口測試用例智能生成系統。 技術棧 LangChain框架GLM-4模型Prompt Engineeri…

操作系統-PV

&#x1f9e0; 背景&#xff1a;為什么會有 PV&#xff1f; 類比&#xff1a;內存&#xff08;生產者&#xff09; 和 CPU&#xff08;消費者&#xff09; 內存 / IO / 磁盤 / 網絡下載 → 不斷“生產數據” 例如&#xff1a;讀取文件、下載視頻、從數據庫加載信息 CPU → 負…

工廠方法模式詳解及在自動駕駛場景代碼示例(c++代碼實現)

模式定義 工廠方法模式&#xff08;Factory Method Pattern&#xff09;是一種創建型設計模式&#xff0c;通過定義抽象工廠接口將對象創建過程延遲到子類實現&#xff0c;實現對象創建與使用的解耦。該模式特別適合需要動態擴展產品類型的場景。 自動駕駛感知場景分析 自動駕…

基于 S2SH 架構的企業車輛管理系統:設計、實現與應用

在企業運營中&#xff0c;車輛管理是一項重要工作。隨著企業規模的擴大&#xff0c;車輛數量增多&#xff0c;傳統管理方式效率低下&#xff0c;難以滿足企業需求。本文介紹的基于 S2SH 的企業車輛管理系統&#xff0c;借助現代化計算機技術&#xff0c;實現車輛、駕駛員和出車…

IntelliJ IDEA download JDK

IntelliJ IDEA download JDK 自動下載各個版本JDK&#xff0c;步驟 File - Project Structure &#xff08;快捷鍵 Ctrl Shift Alt S&#xff09; 如果下載失敗&#xff0c;換個下載站點吧。一般選擇Oracle版本&#xff0c;因為java被Oracle收購了 好了。 花里胡哨&#…

MCP協議在納米材料領域的深度應用:從跨尺度協同到智能研發范式重構

MCP協議在納米材料領域的深度應用&#xff1a;從跨尺度協同到智能研發范式重構 文章目錄 MCP協議在納米材料領域的深度應用&#xff1a;從跨尺度協同到智能研發范式重構一、MCP協議的技術演進與納米材料研究的適配性分析1.1 MCP協議的核心架構升級1.2 納米材料研發的核心挑戰與…

OpenAI發布GPT-4.1:開發者專屬模型的深度解析 [特殊字符]

最近OpenAI發布了GPT-4.1模型&#xff0c;卻讓不少人感到困惑。今天我們就來深入剖析這個新模型的關鍵信息&#xff01; 重要前提&#xff1a;API專屬模型 &#x1f4bb; 首先需要明確的是&#xff0c;GPT-4.1僅通過API提供&#xff0c;不會出現在聊天界面中。這是因為該模型主…

DemoGen:用于數據高效視覺運動策略學習的合成演示生成

25年2月來自清華、上海姚期智研究院和上海AI實驗室的論文“DemoGen: Synthetic Demonstration Generation for Data-Efficient Visuomotor Policy Learning”。 視覺運動策略在機器人操控中展現出巨大潛力&#xff0c;但通常需要大量人工采集的數據才能有效執行。驅動高數據需…