Linux--地址空间

本文通过实例探讨了Linux中地址空间的概念,解释了虚拟地址的作用以及页表在内存映射中的关键角色。重点介绍了写时拷贝技术如何保证进程独立性,并揭示了虚拟地址与物理地址的区别。
摘要由CSDN通过智能技术生成

目录

看一个现象

基本概念

 细节问题--理解它

1.如何理解地址空间?

2.为什么要有地址空间?

3. 进一步了解页表和写时拷贝

 4.如何理解虚拟地址?


看一个现象

 先通过一段代码,看一看现象

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);
        }
    }
}

运行结果:我们都知道父子进程是具有独立性的。进程=内核数据结构+代码和数据。

这里就能解释:程序开始运行,5s后子进程将g_val修改成300,但并没有影响到父进程。

        我们知道程序进入编译阶段,将c语言文件转成二进制文件时,函数名和变量名都已经不存在了,而是被替换成地址了。

但让人不理解的地方:父子进程的g_val地址竟然是一样的,内容却不一样!!!

我们虽然不知道这个地址是怎么回事,但我们可以确定的是,这个地址绝对不是物理地址,否则不可能会有两种值。我们在系统层面上将这个地址称为:虚拟地址


基本概念

        每个进程在Linux中都拥有自己独立的地址空间。这个地址空间是一个虚拟的、连续的内存区域,包含了程序可以访问的所有内存地址。地址空间的大小通常远大于实际的物理内存大小,这是通过虚拟内存技术实现的。(地址空间本质就是内核中的一个结构体对象)

        页表是Linux实现虚拟内存到物理内存映射的关键数据结构。在Linux中,内存是以页为单位进行管理的,每个页的大小通常是固定的(例如4KB)。页表是一个表结构,其中每个条目都对应于地址空间中的一个页,并包含了该页在物理内存中的地址或其在交换空间中的位置信息。

以上面的情况为例:

        我们在父进程中创建了g_val,接着我们创建了子进程,子进程会把父进程的很多内核数据结构全拷贝一份(这里就包括了地址空间和页表)。父进程创建了变量g_val,100会存在于物理内存里(真实地址),但在这之前地址空间会给g_val一个虚拟地址,虚拟地址和真实地址都会被放在页表中,父进程通过页表使用虚拟地址就可以读取到g_val的值了

        子进程中包含了父进程的大部分信息,子进程也可以通过这个虚拟地址读取到g_val的值,这时子进程想通过虚拟地址修改g_val的值(300),由于进程之间是由独立性的,为了维护这种独立性,操作系统会自主完成写时拷贝,重新在物理内存中开辟一块空间存放300,并且将子进程页表中虚拟地址对应的真实地址修改成新开辟空间的地址。

        此时父子进程中g_val的虚拟地址是一样的但被页表映射到了物理空间不同的区域。

关于写时拷贝

        为了保证进程的独立性,可不可以把数据在创建子进程的时候,全部给子进程拷贝一份?可以,但不好,因为浪费空间了。

        在Linux内核中,写时拷贝技术被广泛应用在进程创建、文件映射等场景中。例如,当父进程创建子进程时,子进程会继承父进程的代码段、数据段、堆和栈等内容。此时,为了避免立即复制这些资源造成的性能损耗及浪费问题,Linux内核采用了写时拷贝技术。也就是说,子进程在开始时并不拥有这些资源的物理副本,而是与父进程共享这些资源。只有当子进程或者父进程试图修改这些资源时,Linux内核才会进行实际的复制操作,以确保修改不会影响到另一个进程。


 细节问题--理解它


1.如何理解地址空间?

a.什么是划分区域?

实际问题中:画38线。

在计算机中:地址空间本质是内核的一个struct结构体,内部有很多的属性都是表示start(起始),end(结束)的范围

b.对地址空间的理解

        进程空间实际上就是操作系统给给每个进程画的饼,操作系统给每个进程的地址空间的大小都是真实物理内存的大小,比如真实的物理内存大小有100G,OS就会告诉每个进程,我会给你100G的大小供你使用, 这里给进程的100G就是地址空间,但这是虚拟内存,实际并没有那么大。


2.为什么要有地址空间?

        在Linux中,地址空间的存在具有多重重要性。首先,它确保了不同进程之间无法直接访问对方的内存空间,从而极大地保护了内存中代码和数据的安全性。想象一下,如果没有地址空间的隔离,多个进程可能会相互干扰,导致数据混乱和系统崩溃。
        其次,地址空间有助于实现进程间的独立性。每个进程都有其自己的地址空间,这使得它们的数据和代码相互隔离,避免了潜在的冲突。这种独立性是操作系统能够同时管理多个进程的关键。
        此外,地址空间还使得进程能够以统一的视角看待自己的代码和数据。通过虚拟地址空间,操作系统为每个进程创建了一个独立的环境,使得进程能够方便地访问自己的资源和执行操作,而无需关心底层的物理内存布局。
        在更底层的技术实现上,地址空间结合页表实现了虚拟地址到物理地址的映射。这种映射机制不仅提供了内存管理的灵活性,还允许操作系统在必要时进行内存保护,防止进程访问未授权的内存区域。

        Linux通过隔离机制、页表和MMU、内存保护机制以及高级安全特性等多种手段来拦截和处理非法请求,确保地址空间的安全性和稳定性。这些机制共同构成了一个强大的安全防线,保护着Linux系统的正常运行和数据的完整性。
 


3. 进一步了解页表和写时拷贝

关于页表

        每个进程都有自己的页表,这使得每个进程可以认为自己拥有整个虚拟地址空间,而不会干扰到其他进程。当进程访问某个虚拟地址时,CPU会查找页表,找到对应的物理地址,然后完成访问。
        页表的一个重要特性是它可以实现虚拟内存的一些高级特性,比如内存保护(通过设置页表的访问权限位来防止非法访问)、内存共享(多个进程可以共享同一物理页,通过设置页表的共享位来实现)以及内存映射(将文件或其他对象映射到虚拟地址空间)。

看下面这段C语言代码,是否可行:

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

答案是不可行的。*str储存在常量区不能被修改,因为常量区有代码没有写权限(通过设置页表的访问权限位来防止非法访问)                                                                                           


写时拷贝       

      在写时拷贝技术中,当一个进程试图复制另一个进程的内存空间时(例如,通过fork()创建一个新进程),操作系统并不会立即复制所有的内存页。相反,它只会复制页表,并标记这些页为“写时拷贝”。这意味着新进程和老进程现在共享相同的物理内存页。
        只有当新进程试图写入某个共享页时,操作系统才会触发写时拷贝操作。这时,操作系统会为该页分配一个新的物理页,并将新进程页表中的对应项更新为指向这个新页。然后,操作系统会将老进程和新进程对共享页的修改隔离开来,确保每个进程都有自己的数据副本。
        这种写时拷贝的策略可以极大地提高fork()的效率,因为大多数情况下,新进程并不会立即修改它的内存空间。因此,只有在必要时才进行实际的内存复制,这可以节省大量的时间和资源。              


 4.如何理解虚拟地址?

在最开始的时候,虚拟地址和页表里面的地址从哪里来?

        程序里面本身就有地址!!!这里的地址是虚拟地址(逻辑地址)。 因此,虚拟地址和页表里面的地址直接从程序里面读就行了。程序一旦加载到物理内存的时候,它就会有新的物理地址,接着根据虚拟到物理建立映射关系。这种编译模式叫做平坦模式。

        这些地址在程序运行时被使用,与程序的实际物理内存位置无直接关联。虚拟地址空间是从0开始的,每个进程都有自己独立的虚拟地址空间。
        虚拟地址的主要特点是与实际的物理内存容量无关,它使得程序在编写时无需关心实际的物理内存布局和大小,从而提高了程序的可移植性和灵活性。此外,虚拟地址空间通常被分为用户空间和内核空间两部分,这种分离机制有助于保护系统内核不受用户程序的影响,提高了系统的安全性。
        在Linux系统中,虚拟地址到物理地址的映射是通过页表实现的。页表是操作系统为了支持虚拟内存而创建的数据结构,它记录了虚拟地址到物理地址的映射关系。当进程访问某个虚拟地址时,CPU会查找页表,找到对应的物理地址,然后完成访问。这种映射关系可以动态改变,操作系统可以根据需要添加、删除或修改页表中的条目,以支持内存的动态分配、释放和共享等操作。
        总的来说,虚拟地址在Linux中是一种重要的内存管理机制,它使得程序能够以一种抽象、统一的方式访问内存,提高了程序的可靠性和可维护性。同时,通过页表等机制,操作系统能够实现对内存的高效管理和优化。

 

 

  • 26
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值