我与Linux的爱恋:进程地址空间


外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

🔥个人主页guoguoqiang. 🔥专栏Linux的学习

Alt

1.来段代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>
int g_val = 100;
 
int main()
{
    printf("father is running, pid: %d, ppid: %d\n", getpid(), getppid());
 
 
    pid_t id = fork();
    if(id == 0)
    {
        //child
        int cnt = 0;
        while(1)
        {
            printf("I am child process, 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 = 300;
                printf("I am child process, change %d -> %d\n", 100, 300);
            }
        }
    }
    else
    {
        //father
        while(1)
        {
            printf("I am father process, pid: %d, ppid: %d. g_val: %d, &g_val: %p\n", getpid(), getppid(), g_val, &g_val);
            sleep(1);
        }
    }
}

在这里插入图片描述
从一开始父子进程是相同的,由此可以推出我们打印的地址不是物理地址,而是虚拟地址,所以我们在c/c++中看到的都为虚拟地址,由操作系统负责转化为物理地址。
关于地址,从低地址到高地址存储的依次是:代码段、初始化全局数据区、未初始化全局数据区、堆区、栈区、命令行参数与环境变量。其中,堆区的空间是从小到大增长的,而栈区的空间是从大到小增长的

其中在32位机器中,使用32位(比特位)来表示一个地址,这意味着它可以表示 2^32
个不同的地址,也就是4GB;在64位机器中,使用64位(比特位)来表示一个地址,它可以表示 2^64 个不同的地址,也就是16EB

需要注意的是,这里的EB指的是艾字节(Exabyte),1EB等于 1,0241,024 PB(拍字节),而1PB等于 1,0241,024 TB(太字节),1TB等于 1,0241,024 GB(千兆字节),1GB等于 1,0241,024 MB(兆字节),1MB等于 1,0241,024 KB(千字节),1KB等于 1,0241,024 字节。因此,16EB是一个非常巨大的数字,远远超过了当前大多数应用场景的需求。
在这里插入图片描述

32位下是4GB 64位下是16EB

2.引入最基本的理解

当子进程修改g_val的值时,为了确保进程的独立性(也就是说子进程的数值修改不应该影响父进程),此时就会发生写时拷贝,会给子进程g_val开辟独立的物理地址空间,而不是与父进程共享同一块空间,通过我们观察发现,代码依旧共享一段空间,只是数据区不同了。
在这里插入图片描述

3.尝试理解

什么叫做地址空间?
在32位机器下,数据与地址总共32根线,每根数据与地址线可以产生充电和放电两种状态即0和1.因此地址总线组合排列形成的地址范围为[0,2^32],这就是地址空间。
那么如何理解地址空间的区域划分呢?
有时候你和同桌(张三和李四)吵架了,然后桌子总共200cm,然后规定每人100cm,即把课桌划分为[0,100] ,[101,200]这两个区域划分,如果我们要记录区域结果,我们需要先描述再组织

struct desktop{
	int zhangsan _start;
	int zhangsan _end;
	int lisi _start;
	int lisi _end;
}

操作系统为每个进程创建了进程地址空间和mm_struct,用于记录每个进程的各个区域的起始位置和结束位置。在已经被分配给某进程的空间范围内,该进程可以随意使用和访问
在这里插入图片描述
那么为什么要有进程地址空间呢?
例子:大富翁的私生子
大富翁有三个私生子,三个私生子之间互不直到对方的存在,这个大富豪对每个私生子说我有100亿,等到哪天你就继承我的100亿。如果其中某个儿子有需求要10w大富翁就给10w,但是如果要100亿 ,就可能无法成功,但他并不会觉得这100亿不是他的,而是觉得自己申请的太多了。
在这里插入图片描述
这里的大富翁就相当于操作系统,而这三个私生子就相当于进程。操作系统有4GB的内存空间,每个进程都认为自己有4GB,但是在通常情况下,进程并不会申请过大的空间。
在这里插入图片描述
因此 1.将无序变为有序
在操作系统中,虚拟内存是一种内存管理功能,它让每个进程都认为自己有连续的、独立的地址空间。而物理内存可能并不是连续的,并且被多个进程共享。这样每个进程都认为自己有所有的内存,但是实际上它们操作的是不同的物理内存区域。

进程隔离:每个进程都不能访问其他进程的内存,这增强了系统的稳定性和安全性。

内存保护:操作系统可以设置权限,防止进程访问它不应该访问的内存区域。

内存扩展:通过虚拟内存,系统可以使用硬盘空间作为临时的内存使用,即交换空间(swap space),从而扩展可用内存的大小

例子:压岁钱
小明新年获得了200块压岁钱,母亲担心小明会乱花钱,于是和小明说“你的压岁钱由我来保管,你需要买什么就和我说,我在从这里给你钱”。于是有一天小明要买一款游戏机150元,找妈妈去要,结果妈妈说“游戏机,会害了你的学习,不给买”。再一次小明要去买学习资料,向妈妈申请50块钱,妈妈同意了。因此,新增一个人,作为中间层,可以对非法请求进行拦截

在操作系统中,页表除了包含虚拟地址到物理地址的映射关系,还记录了该区域的读写权限。当用户对其已申请空间做了超出读写权限外的操作,则会被操作系统识别到,并终止该进程。
2.操作系统会拦截非法请求–>对物理内存进行保护
使用虚拟地址+页表的方式可以保证进程的独立性
3.因为有地址空间和页表的存在,将进程管理模块和内存管理模块进行了解耦合
我们也可以知道C/C++申请的地址是虚拟地址。
我们要重新定义一下我们对进程的概念 进程=内核数据结构(PCB+页表+进程地址空间)+代码和数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值