IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux——程序地址空间|验证地址空间分布|地址空间|问题探讨fork()|一个变量怎么保存不同的值扩展 |为什么要有地址空间的三大理由|理由1 理由2 理由3 -> 正文阅读

[系统运维]Linux——程序地址空间|验证地址空间分布|地址空间|问题探讨fork()|一个变量怎么保存不同的值扩展 |为什么要有地址空间的三大理由|理由1 理由2 理由3

目录

验证地址空间分布?

?地址空间

?问题探讨

fork ()一个变量怎么保存不同的值

扩展?

?为什么要有地址空间的三大理由

理由1?

理由2?

理由3?

再谈挂起?


?

验证地址空间分布?

栈向下增长,堆向上增长

?这里的初始化和未初始化是指全局数据

这里的地址空间不是内存,以前指针的地址也不是内存

makefile里可使用$@ $^

?$@:目标文件,这里就是hello

$^:这个代表冒号右侧所有文件

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int g_unval;
int g_val = 100;


int main(int argc, char* argv[], char* env[])
{
  
    printf("code addr: %p\n", main);
    printf("init global addr: %p\n", &g_val);
    printf("uninit global addr: %p\n", &g_unval);

  
    char* heap_mem = (char*)malloc(10);

    printf("heap addr: %p\n", heap_mem); 

    printf("stack addr: %p\n", &heap_mem); 
  
    for (int i = 0; i < argc; i++)
    {
        printf("argv[%d]: %p\n", i, argv[i]);
    }
    for (int i = 0; env[i]; i++)
    {
        printf("env[%d]: %p\n", i, env[i]);
    }

    return 0;
}

在C语言中用malloc去申请空间,如果申请10个字节空间,可能会开辟20个,其中10个是开辟的空间,剩下10个自己存储的是该空间的信息如开辟时间等属性

ctrl+v 配合hjkl控制方向再输入I(大写)可进行注释删除与建立

static修饰的变量在正文代码区

C语言中如果直接写'a',"hello world"这些语句可以直接运行,因为这些是字面常量,int a=10;这不是字面常量,a是变量,10是字面常量,字面常量在正文代码区,正文代码区中有一个字符常量区,字面常量就在这个位置(字符常量区)

在32位机器下,一个进程的地址空间,取值范围是0x0000 0000~0xFFFF FFFF

[0,3GB]:用户空间,这个是按照上面所讲的空间排布的

[3GB,4GB]:内核空间?

上面的验证代码,在windows下会跑出不一样的结果,上面的结论,默认只在Linux有效

?地址空间

上面打印的全部是进程在打印,打印出来的地址是程序运行后打印的

内核中的地址空间,本质也一定是一种数据结构,要和一个特定的进程关联起来

当操作系统去直接访问物理内存,会存在一些问题,如进程1有野指针,操作系统访通过这个野指针问到了进程3,进程1可通过野指针直接修改进程3的数据,因此这种方式非常危险,后来被人们进行了改进

内存本身是随时可以被读写的

改进方法:每一个进程有自己的PCB,并且操作系统给每一个进程创建一个虚拟地址空间,这个地址空间叫虚拟/进程地址空间,还添加了一种映射机制,如果要访问物理内存,就要先进行映射

映射机制:虚拟地址->物理地址

每个进程都要有虚拟地址空间?

虚拟地址空间区域划分,通过区域开头和区域尾部来划分区域大小,修改头部或尾部即可修改区域大小,本质是在一个范围里定义出eng和begin

struct space
{
int start;
int end;
}

地址空间是一种内核数据结构,它里面至少要有各个区域的划分

?源代码;

struct mm_struct是内核中的一个结构

虚拟地址空间必须和进程一 一 关联起来

某进程的task_struct里面包含一个指针,这个指针指向该进程所对应的地址空间

?地址空间的划分

?

?映射关系是通过页表实现的,地址空间和页表是每个进程都私有一份

?问题探讨

#include <stdio.h>
#include <unistd.h>

int g_val = 100;

int main()
{
    pid_t id = fork();
    if (id == 0)
    {
        int cnt = 0;
        //child
        while (1)
        {

            printf("I am child, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n",   getpid(), getppid(), g_val, &g_val);
            sleep(1);
            cnt++;
            if (cnt == 5)
            {
                g_val = 200;
                printf("child chage g_val 100 -> 200 success\n");
            }
        }
    }
    else
    {
        //father
        while (1)
        {
            printf("I am father, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n",  getpid(), getppid(), g_val, &g_val);
            sleep(1);
        }

    }
return 0;
}

我们发现改变后g_val的地址仍然一模一样,g_val的值却不同?,这里怎么可能同一个地址,同时读取的时候,出现了不同的值?

这里的地址,不是我们以前所理解的物理内存的地址,而是虚拟地址(线性地址),几乎所有的语言,如果有地址的概念,这个地址一定不是物理地址

出现这种情况是因为,子进程继承了父进程的相关属性,但会对有些内容做修改如一些私有化的内容

刚开始是这样,子进程拷贝一份父进程的内容,把私有数据进行修改,因为是拷贝的所以页表一样,所以指向的物理内存也一样

但是当子进程修改数据的时候,为了保证进程的独立性(互不影响),操作系统识别到子进程修改变量时,会给子进程重新开辟一块空间,并把上面的值拷贝过来,然后修改一下子进程的映射关系,完成更改后这俩个的虚拟地址不受影响,但物理内存不一样,所以打出来的一样时因为打出来的是虚拟地址

写时拷贝:就是等到修改数据时才真正分配内存空间,这是对程序性能的优化,可以延迟甚至是避免内存拷贝,当然目的就是避免不必要的内存拷贝。上面就用到了写实拷贝。

fork ()一个变量怎么保存不同的值

?

程序return会被指向俩次

return的本质就是对id进行写入,发生了写时拷贝,所以父子进程各自起始在物理内存中有属于自己的变量空间,只不过在用户层用同一个变量(虚拟地址)来标识了

扩展?

当我们的程序,在编译的时候,形成可执行程序的时候, 没有被加载到内存的时候,我们的程序内部是有地址的,可执行程序编译的时候,内部已经有地址了(虚拟)

操作系统不仅要遵守地址空间,编译器也要遵守地址空间,编译器编译代码的时候,就已经给我们形成了各个区域,如:代码区,数据区……并且,采用和Linux内核中一样的编址方式,给每一个变量,每一行代码都进行了编址,程序在编译的时候,每一个字段早已经具有了虚拟地址

程序内部的地址,依旧用的是编译器编译好的虚拟地址,当程序加载到内存的时候,每行代码,每个变量都有一个物理地址(外部地址)

这里CPU拿到的是虚拟地址

?根据写好的代码长度确定虚拟地址的起始和结束

物理内存本身就有地址

CPU读到的是虚拟地址

?为什么要有地址空间的三大理由

理由1?

?页表不是简单的映射关系,页表起始也是一种数据结构,其中也包含访问物理内存的权限

凡是非法的访问或者映射,OS都会识别到,并且终止这个进程。如:

char *str="1232";
*str='H';

字符串常量(在代码区中的字符常量区)不允许被修改,因为页表中有这段字符串的读和写权限,所以不能被修改,跟物理内存无关,物理内存可以被随便读写,但是有了页表之后,就不能了

所有的进程崩溃,本质就是进程退出,OS杀掉了这个子进程

地址空间有效的保护了物理内存,因为地址空间和页表是操作系统创建和维护的,凡是想使用地址空间和页表进行映射,也一定要在OS的监管之下来访问,这样保护了物理内存中的所有合法数据,包括各个进程,一级内核相关的数据

理由2?

?因为有地址空间的存在和页表映射的存在,我们的物理内存中,可以对未来数据进行任意位置的加载,就是把代码和数据从磁盘中加载到物理内存中的任意位置

?物理内存的分配和进程的管理没有任何关系

内存管理模块和进程管理模块是解耦合(减少模块和模块的关联性)关系

?我们在C/C++语言上,new或malloc空间的时候,本质是在虚拟地址空间上申请的

当我们申请了空间,如果我们不立马使用,则浪费了空间。

我们申请的地址空间本质是在虚拟内存空间上申请的,而物理内存甚至可以一个字节都不给我们(操作系统自动完成),当我们要真正访问对应的物理空间的时候,操作系统才执行内存的相关管理算法,帮我们在物理内存上申请内存,构建页表映射关系,让我们进行内存的访问。用户和进程对于这种情况,完全0感知。这种策略也叫延迟分配策略。

理由3?

?因为在物理内存中理论上可以任意位置加载,所以物理内存中的几乎所有的数据和代码都是乱序的

因为页表的存在,可以将地址空间上的虚拟地址和物理地址进行映射,在进程视角,所有的内存分布,都是有序的。地址空间+页表的存在可将内存分布有序化,这样方便管理。

?地址空间是OS给进程画的大饼

进程要访问的物理内存中的数据和代码,可能目前并没有在物理内存中,同样的,也可以将不同的进程映射到不同的物理内存,这样更容易实现进程的进程独立性。所以进程的独立性,可以通过地址空间+页表的方式实现

因为有地址空间的存在,每一个进程都认为自己有4GB的空间(32),并且各个区域是有序的,进而可以通过页表映射到不同的区域,来实现进程的独立性,每个进程也不知道其它进程的存在。

再谈挂起?

?红颜色圈起来的都是进程

?加载的本质就是创建进程,但是不需要立刻把所有的程序的代码和数据都加载到内存中,并创建内核数据结构建立映射关系。因为操作系统会帮我们创建进程,甚至极端情况下只有内核结构被创建了出来,连映射关系都没有,这种状态叫新建状态。这样可以实现对程序的分批加载。

既然可以分批加载(唤入),同样可以分批换出,如开机界面加载完就不再加载。甚至这个进程短时间不再被执行了,比如阻塞了。进程的数据和代码被换出了就叫做挂起。

页表映射的时候不止可以映射到物理内存中,还可以直接映射到磁盘中

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 21:17:28  更:2022-10-08 21:20:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 6:08:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码