Linux進程概念(五)進程地址空間

地址空間排布

這段空間中自上而下,地址是增長的,棧是向地址減小方向增長,里面存放函數中的臨時變量,而堆是向地址增長方向增長,malloc開辟的地址空間存放在堆區,堆棧之間的共享區域,主要用來加載動態庫。

驗證地址空間排布
#include<stdio.h>
#include<stdlib.h>
int g_val_1;//未初始化
int g_val_2 = 100;//初始化
////
int main(int argc, char *argv[], char *env[])
{printf("code addr: %p\n", main);//代碼起始地址const char *str = "hello bit";printf("read only string addr: %p\n", str);//str是指針變量(棧區),str指向字符常量"h"(字符常量區)printf("init global value addr: %p\n", &g_val_2);//printf("uninit global value addr: %p\n", &g_val_1);char *mem = (char*)malloc(100);char *mem1 = (char*)malloc(100);char *mem2 = (char*)malloc(100);printf("heap addr: %p\n", mem);printf("heap addr: %p\n", mem1);printf("heap addr: %p\n", mem2);printf("stack addr: %p\n", &str);printf("stack addr: %p\n", &mem);static int a = 0;int b;int c;printf("a = stack addr: %p\n", &a);printf("stack addr: %p\n", &b);printf("stack addr: %p\n", &c);int i = 0;for(; argv[i]; i++)printf("argv[%d]: %p\n", i, argv[i]);for(i=0; env[i]; i++)printf("env[%d]: %p\n", i, env[i]);
}

運行結果:

我們可以看到代碼區的地址是最小的,

static修飾的全局變量,編譯的時候已經被編譯到全局數據區了。

#include<stdio.h>
#include<unistd.h>
int g_val = 0;
int main()
{printf("begin.....%d\n",g_val);pid_t id = fork();if(id==0){//childint count = 0;while(1){printf("child: pid: %d,ppid: %d, g_val:%d, &g_val: %p\n",getpid(),getppid(),g_val,&g_val);sleep(1);count++;if(count == 5){g_val = 100;}}}else if(id>0){//fatherwhile(1){printf("father: pod: %d,ppid: %d, g_val:%d, &g_val: %p\n",getpid(),getppid(),g_val,&g_val);sleep(1);}}else{//todo}return 0;
}

在五秒后,子進程的g_val變成了100,但父進程的g_val沒有改變,但神奇的是,他們倆的地址竟然還完全相同。

怎么可能同一個變量,同一個地址,同時讀取,讀到了不同的內容。

結論:如果變量的地址,是物理地址,不可能存在上面的現象。絕對不是物理地址,是線性地址或者是虛擬地址。平時寫的c++/c用的指針,指針里面的地址全部都不是物理地址

進程地址空間的概念

每一個進程在創建時不僅要創建內核pcb還要創建進程地址空間

父進程pcb中會有指針指向這塊地址空間,

頁表是一種key-value式的表格,左側對應虛擬地址,右側對應實際物理地址

當父進程創建子進程時,先創建子進程pcb結構,子進程有自己獨立的頁表結構,此時子進程數據和代碼都和父進程相同。子進程一開始頁表為空,一個全局變量在父子進程具有相同的虛擬地址,當子進程往頁表中寫入時,發現父子進程指向相同的物理地址,系統會為子進程重新開辟一段空間來存儲這個全局變量,此時父子進程相同的虛擬地址會映射到不同的物理地址。

先經過寫時拷貝--是由操作系統自動完成的。本質重新開辟空間,但是在這個過程中,左側的虛擬地址是0感知的,不會影響他。所在上上面例子中,將g_val改成100后進行打印,得到的地址相同,都是虛擬地址(子進程的虛擬地址繼承自父進程),但內容不同是因為父進程通過虛擬地址查頁表映射到一塊物理地址,而子進程通過頁表映射映射到物理內存的另一個地址。

地址空間究竟是什么?

什么叫做地址空間?

前提是一個進程一定是正在運行,在32位計算機中,有32位的地址和數據總線,cpu和內存之間的線叫做系統總線,內存和外設之間的線叫做io總線。拷貝的本質是磁盤向內存充放電的過程。,每一根地址總線只有0,1,32跟會有2的32次方中組合,2^32*1bit=4GB.

所以地址空間就是你的地址總線排列組合形成地址范圍[0,2^32)

所謂的進程地址空間,本質是一個描述進程可視范圍的大小

如何理解地址空間上的區域劃分?-

地址空間一定要存在各種區域劃分,要對線性地址進行start和end即可。地址空間本質是內核的一個數據結構對象,類似pcb一樣,地址空間也是要被操作系統管理的,先描述后組織。

struct mm_struct
{unsigned long code_start;//代碼區unsigned long code_end;unsigned long init_start;//初始化區unsigned long init_end;unsigned long uninit_start;//未初始化區unsigned long uninit_end;unsigned long heap_start;//堆區unsigned long heap_end;unsigned long stack_start;//棧區unsigned long stack_end;//...等等
}

所以在創建一個進程時,先要創建對應pcb,再創建對應結構體mm_struct,并劃分區域,32位默認大小為4GB。在范圍內,連續的空間中,每一個最小單位都可以有地址,每個地址都可以被小胖(進程)直接使用。

為什么要有進程地址空間

我們再來思考什么叫做進程?以及為什么要有進程地址空間?

如果進程直接訪問物理內存,那么看到的地址就是物理地址,而語言中有指針,如果指針越界了,一個進程的指針指向了另一個進程的代碼和數據,那么進程的獨立性,便無法保證,因為物理內存暴露,其中就有可能有惡意程序直接通過物理地址,進行內存數據的篡改,如果里面的數據有賬號密碼就可以改密碼,即使操作系統不讓改,也可以讀取。

增加進程虛擬地址空間可以讓我們訪問內存的時候,增加一個轉換的過程,在這個轉換的過程中,可以對我們的尋址請求進行審查,所以一旦異常訪問,直接攔截,該請求不會到達物理內存,保護物理內存。

我們在寫代碼的時候肯定了解過指針越界,我們知道地址空間有各個區域,那么指針越界一定會出現錯誤嗎?

不一定,越界可能他還是在自己的合法區域。比如他本來指向的是棧區,越界后它依然指向棧區,編譯器的檢查機制認為這是合法的,當你指針本來指向數據區,結果指針后來指向了字符常量區,編譯器就會根據mm_struct里面的start,end區間來判斷你有沒有越界,此時發現你越界了就會報錯了,這是其中的一種檢查,第二種檢查為:頁表因為將每個虛擬地址的區域映射到了物理內存,其實頁表也有一種權限管理,當你對數據區進行映射時,數據區是可以讀寫的,相應的在頁表中的映射關系中的權限就是可讀可寫,但是當你對代碼區和字符常量區進行映射時,因為這兩個區域是只讀的,相應的在頁表中的映射關系中的權限就是只讀,如果你對這段區域進行了寫,通過頁表當中的權限管理,操作系統就直接就將這個進程干掉。

頁表

cr3寄存器

在cpu中會有一個cr3寄存器,這個寄存器會保留當前頁表的起始地址,本質上屬于進程的硬件上下文,所以當進程切換時,會帶走寄存器的數據。(這個cr3是物理地址)

頁表會給我們提供很好的權限管理

我們來思考一個問題,我們如何知道某個區域是只讀還是可寫入的呢?

頁表中會有一個標志位來說明該區域是只讀還是可寫入。若一個位置權限是可讀,當我們嘗試對這個位置進行寫入時,此時頁表會直接進行攔截,相當于進行了一次非法操作,操作系統會直接終止這個進程。

此時我們來看一段代碼

char *str ="hello bit";
*str='H';
return 0;

按照我們之前的理解,“hello bit”存在于字符常量區,是不可以被修改的。

但我們今天深入思考一下,為什么代碼是只讀?字符常量區是只讀的?他們是如何加載到只讀區域的?物理內存沒有只讀的概念,可以隨意進行讀寫,沒有權限管理。

這是因為在頁表中的地址映射關系中,頁表中的標志位是只讀的,所以操作系統才會攔截你,跨權限時進程才會被操作系統終止。

我們再來思考一個問題

我們知道,當內存空間不足的時候,處于阻塞態的進程是可以被掛起的,代碼和數據被換出內存,那我們如何知道這個進程已經被掛起了呢?你怎么知道你的進程的代碼和數據在不在內存中?

如果進程在內存,并且狀態時R,說明處于運行態,如果代碼和數據在內存中,并且狀態是S,說明進程正在被阻塞,可是Linux內核狀態沒有掛起轉換態,我們如何知道進程此時是被掛起呢?

操作系統對大文件可以實現分批加載(打游戲時游戲大小十幾個G,而你的內存只有八個G),現在操作系統用多少給你加載多少。頁表中還有一個標志位來表示對應的代碼和數據是否已經被加載到內存中。若這個標志位為1.表示已經被加載,我們直接讀取對應的物理地址,找到對應的物理內存進行訪問。當標志位為0表示當前代碼和數據并未加載到內存中,操作系統會觸發缺頁中斷。我們會找到這個可執行程序的代碼和數據,在內存中申請一塊內存,然后把可執行程序剩余的代碼和數據加載到內存中,然后把這段內存的地址填寫到對應的頁表當中。我們再訪問可以訪問對應的代碼和數據了。

這個時候我們就知道為什么某個區域是只讀還是可讀可寫了,因為通過頁表對他進行管理。

我們再思考一下

當進程在被創建的時候,是先創建內核數據結構還是先加載對應的可執行程序呢?

答案是先創建內核數據結構,即先要為該進程創建對應的pcb,進程地址空間,頁表對應關系,然后才慢慢加載可執行程序,可能你的程序已經跑起來了,但還沒加載完

因為有地址空間和頁表的存在,我們可以將進程管理模塊和內存管理模塊進行解耦合!

我們再來談進程。進程=內核數據結構(tast_struct&&mm_struct地址空間空間&&頁表)+程序的代碼和數據。所以進程在切換的時候不僅要切換pcb還要切換進程地址空間和頁表。

我們經常說進程具有獨立性,是怎么做到的?每一個進程都有獨立的內核數據結構,在物理內存中加載的代碼和數據都是獨立的。在物理內存中加載的數據可以隨便加載,是無序的,但經過頁表的映射,可以在虛擬地址中有序的排布,這就有了各種區域(正文代碼區,初始化區,未初始化區等等)

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

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

相關文章

Go語言實戰案例-判斷二叉樹是否對稱

給定一棵二叉樹&#xff0c;判斷這棵樹是否是對稱的。對稱的含義是&#xff1a;這棵樹的左子樹和右子樹在結構上是鏡像對稱的&#xff0c;且對應節點的值相等。示例 1&#xff1a;1/ \2 2/ \ / \ 3 4 4 3輸出&#xff1a;true示例 2&#xff1a;1/ \2 2\ \3 3輸出&a…

【機器學習深度學習】為什么需要分布式訓練?

目錄 前言 一、模型規模爆炸&#xff1a;單卡GPU已難以承載 1.1 問題描述 1.2 面臨挑戰 1.3 解決方案&#xff1a;模型并行 (Model Parallelism) 1.4 類比理解&#xff1a;模型并行 1.5 模型并行的關鍵點 1.6 模型并行&#xff08;Model Parallelism&#xff09;的流程…

二十八、【Linux系統域名解析】DNS安裝、子域授權、緩存DNS、分離解析、多域名解析

DNS服務深度解析&#xff1a;緩存、分離與多域名管理一、DNS服務架構全景DNS核心組件關系DNS服務器類型對比二、基礎DNS服務配置1. Bind9核心配置文件2. 區域文件結構解析區域文件記錄類型表三、子域授權與分層解析子域授權原理子域配置流程1. 父域配置2. 子域配置遞歸與迭代查…

【LeetCode】前綴表相關算法

目錄1、介紹2、核心概念【1】前綴和后綴【2】最長公共前后綴&#xff08;LPS&#xff09;3、相關算法題【1】找出字符串中第一個匹配項的下標【2】重復的子字符串1、介紹 前綴表是一種在字符串匹配算法&#xff08;特別是KMP算法&#xff09;中使用的數據結構&#xff0c;用于…

(六) Spring AI 1.0版本 + 千問大模型+RAG

上篇文章我們大概講了一下向量模型的知識&#xff0c;本篇文章&#xff0c;我們將會通過RAG實戰的形式&#xff0c;來感受一下RAG。 項目準備 pom.xml 這里我們需要引入向量庫和pdf相關的包<dependency><groupId>org.springframework.ai</groupId><artifa…

Spring Boot與Mybatis-Plus集成SQLServer的完整指南

本文還有配套的精品資源&#xff0c;點擊獲取 簡介&#xff1a;本項目旨在演示如何將SQLServer與Spring Boot以及Mybatis-Plus框架進行整合&#xff0c;打造一個高效穩定的后端服務。詳細介紹涉及了數據庫連接、實體類定義、Mapper接口創建、Service層業務邏輯編寫、Control…

【工作筆記】判斷一條方法需不需要事務/AOP

① 看注解方法/類上有 Transactional → 需要事務&#xff0c;必須走代理方法/類上有自定義 AOP 注解&#xff08;如 Log、Retry、Cacheable 等&#xff09;→ 需要代理什么都沒有 → 幾乎肯定不需要示例需求Transactional public void generateDailyTask(...)? 需要事務publi…

Unity 的UI動畫調節

在游戲開發中&#xff0c;精美的UI動畫能極大提升用戶體驗。Unity提供了強大的動畫系統&#xff0c;讓開發者可以輕松創建流暢的界面動效。本文將介紹UI動畫的核心概念、制作流程和實用技巧。一、核心動畫組件Animation窗口 - 可視化創建關鍵幀動畫Animator組件 - 控制動畫狀態…

26考研11408數據結構

數據結構 1.緒論1.1.1數據結構的基本概念 數據數據元素&#xff1a;數據的基本單位&#xff0c;一個數據元素由多個數據項組成&#xff0c;數據項是組成數據元素不可分割的最小單位數據對象&#xff1a;具有相同性質的數據元素的集合&#xff0c;是數據的一個子集數據類型&…

Solar月賽(應急響應)——攻擊者使用什么漏洞獲取了服務器的配置文件?

某某文化有限公司的運維小王剛剛搭建服務器發現cpu莫名的異常的升高請你幫助小王排查一下服務器。 文章目錄事件介紹事件1&#xff1a;幫助小王找到是什么漏洞?事件2&#xff1a;系統每天晚上系統都會卡卡的幫小明找到問題出在了那&#xff1f;事件3&#xff1a;惡意域名是什么…

高頻面試題

1.HashMap的底層原理JDK1.7版本之前&#xff0c;HashMap的底層數據結構是數組鏈表&#xff0c;HashMap通過哈希算法會將元素的key映射待數組的的槽位(Bucket)。如果多個鍵映射到同一個槽位&#xff0c;就會以鏈表的形式存儲在同一個槽位上。但是鏈表的查詢的復雜度O(n),所有沖突…

魚皮項目簡易版 RPC 框架開發(四)

本文為筆者閱讀魚皮的項目 《簡易版 RPC 框架開發》的筆記&#xff0c;如果有時間可以直接去看原文&#xff0c; 1. 簡易版 RPC 框架開發 前面的內容可以筆者的前面幾篇筆記 魚皮項目簡易版 RPC 框架開發&#xff08;一&#xff09; 魚皮項目簡易版 RPC 框架開發&#xff08;二…

力扣-79.單詞搜索

題目鏈接 79.單詞搜索 class Solution {int m, n;public boolean exist(char[][] board, String word) {m board.length;n board[0].length;boolean[][] visited new boolean[m][n];// 遍歷網格中的每個單元格作為搜索起點for (int i 0; i < m; i) {for (int j 0; j …

LabVIEW的To More Specific Class功能說明

?To More Specific Class 是 LabVIEW 中用于控件引用類型轉換的關鍵函數。可將通用 GObject 引用&#xff0c;精準轉為 Listbox、TreeControl 等特定控件類引用&#xff0c;讓開發者能調用專屬屬性&#xff08;如獲取列表行數&#xff09;&#xff0c;實現對不同控件類的差異化…

Ubuntu20.04安裝和配置Samba實現Win11下共享文件夾

Samba是在Linux和UNIX系統上實現 SMB / CIFS 協議的開源軟件&#xff0c;主要用于局域網內的文件共享和打印服務。Samba通過SMB/CIFS協議實現跨平臺資源共享&#xff0c;支持匿名用戶和本地用戶訪問共享目錄&#xff0c;客戶端主要為Windows系統。其核心進程包括&#xff1a; ?…

設計模式(八)結構型:橋接模式詳解

設計模式&#xff08;八&#xff09;結構型&#xff1a;橋接模式詳解橋接模式&#xff08;Bridge Pattern&#xff09;是 GoF 23 種設計模式中的結構型模式之一&#xff0c;其核心價值在于將抽象部分與實現部分分離&#xff0c;使它們可以獨立變化。它通過“組合”而非“繼承”…

【邊緣填充】——圖像預處理(OpenCV)

目錄 1 邊界復制&#xff08;BORDER_REPLICATE&#xff09; 2 邊界反射&#xff08;BOEDER_REFLECT&#xff09; 3 邊界反射101&#xff08;BORDER_REFLECT101&#xff09; 4 邊界常數&#xff08;BORDER_CONSTANT&#xff09; 5 邊界包裹&#xff08;BORDER_WRAP&#xf…

git同步到github出錯-幾個問題-一天晚上(2025.7.29)

訪問不了github 代理和加速器都正常&#xff0c;但是就是訪問不了這個網站嘗試過幾種方法都不行&#xff0c;后面突然可以了。 之后發現一種情況會不行&#xff1a;同時開啟 同步不了 http連接 https://blog.csdn.net/m0_73972962/article/details/146198392 一堆問題 ssh連接才…

Redis未授權訪問的利用的幾種方法原理以及條件

一、redis通過定時任務反彈shell1.利用條件&#xff1a;需要能夠登錄redis數據庫&#xff0c;并且redis以root用戶運行。同時/var/spool/cron目錄要具有寫和執行權限。二、Redis主從getshell1.原理&#xff1a;在Redis 4.x之后&#xff0c;Redis新增了模塊功能&#xff0c;通過…

DNF 與 YUM 的區別詳解:從 CentOS 7 到 CentOS 9 的演進

&#x1f365; DNF 與 YUM 的區別詳解&#xff1a;從 CentOS 7 到 CentOS 9 的演進標簽&#xff1a;CentOS、YUM、DNF、Linux 包管理、系統升級、兼容性 適用版本&#xff1a;CentOS 7、CentOS 8、CentOS 9&#x1f9e9; 一、背景介紹 CentOS 中使用的包管理工具是 RedHat 系列…