Linux 哪些段受写入时复制的影响?

Linux 哪些段受写入时复制的影响?,linux,memory-management,operating-system,copy-on-write,Linux,Memory Management,Operating System,Copy On Write,我对写下拷贝的理解是“每个人都有一个相同数据的单一共享拷贝,直到它被写出来,然后再制作一个拷贝” 相同数据的共享副本是由堆和bss段组成还是仅由堆组成 哪些内存段将被共享,这取决于操作系统吗 为了更好地理解,你应该从你的词汇表中删除术语部分。大多数系统在页面上工作;不是片段。在64位的英特尔领域,这一技术终于消失了 你应该问,“什么页面在写的时候拷贝会受到影响。” 这将是一个进程写入时多个进程共享的可写页面 这可能发生在使用叉子之后。实现分叉的一种方法是创建父进程地址空间的完整副本。然而,这可能

我对写下拷贝的理解是“每个人都有一个相同数据的单一共享拷贝,直到它被写出来,然后再制作一个拷贝”

  • 相同数据的共享副本是由堆和bss段组成还是仅由堆组成
  • 哪些内存段将被共享,这取决于操作系统吗

  • 为了更好地理解,你应该从你的词汇表中删除术语部分。大多数系统在页面上工作;不是片段。在64位的英特尔领域,这一技术终于消失了

    你应该问,“什么页面在写的时候拷贝会受到影响。”

    这将是一个进程写入时多个进程共享的可写页面

    这可能发生在使用叉子之后。实现分叉的一种方法是创建父进程地址空间的完整副本。然而,这可能需要很大的努力,特别是因为大多数情况下,一个人会在fork之后在子对象中执行exec

    另一种方法是让父代和子代共享相同的内存。这对于只读内存很好,但如果多个进程可以写入同一内存,则会出现明显的问题

    这可以通过让进程对读/写内存充电直到进程写入内存来克服。在这种情况下,该页面将不被写入过程共享,操作系统将分配一个新的页面框架,将其映射到地址空间,将原始数据复制到该页面,然后允许写入过程继续。

    操作系统可以设置它希望的任何“写入时复制”策略,但通常它们都做相同的事情(即最有意义的内容)

    粗略地说,对于一个类似POSIX的系统(linux、BSD、OSX),有四个方面(您所称的部分)值得关注:
    data
    (其中
    intx=1;
    go)、
    bss
    (其中
    inty
    go)、
    sbrk
    (这是heap/malloc)和
    stack

    当一个
    fork
    完成时,操作系统会为共享父级所有页面的子级设置一个新的页面映射。然后,在父级和子级的页面映射中,所有页面都标记为只读

    每个页面映射还具有一个引用计数,该计数指示有多少进程共享该页面。在fork之前,refcount将为1,之后,refcount将为2

    现在,当任何一个进程尝试写入R/O页时,都会出现页面错误。操作系统将看到这是“写入时复制”的错误,将为该进程创建一个私有页面,从共享页面复制数据,将该页面标记为该进程可写,然后继续

    它还将降低refcount。如果refcount现在[再次]为1,则操作系统会将另一进程中的页面标记为可写和非共享[这消除了另一进程中的第二个页面错误--这是一种加速,因为此时操作系统知道另一进程应该可以自由写入而不受干扰]。此加速可能取决于操作系统

    实际上,
    bss
    部分得到了更特殊的处理。在它的初始页面映射中,所有页面都映射到一个包含所有零的页面(也称为“零页面”)。映射标记为R/O。因此,
    bss
    区域的大小可能为GB,并且它将只占用一个物理页面。此单一、特殊、零页面在所有进程的所有
    bss
    部分之间共享,而不管它们之间是否存在任何关系

    因此,一个进程可以从该区域的任何页面读取数据,并得到它所期望的结果:零。只有当进程尝试写入这样的页面时,相同的写时拷贝机制才会启动,进程获得一个私有页面,映射被调整,进程被恢复。现在,它可以根据需要自由写入页面

    再一次,操作系统可以选择它的策略。例如,在fork之后,共享大多数堆栈页面可能更有效,但是从“当前”页面的私有副本开始,这取决于堆栈指针寄存器的值

    当一个
    exec
    系统调用[在子系统上]完成时,内核必须撤消在
    fork
    [减少引用计数]、释放子系统的映射等过程中完成的大部分映射,并恢复父系统的原始页面保护(即,除非它执行另一个
    fork
    ),否则它将不再共享其页面


    虽然不是您最初问题的一部分,但可能会有一些相关活动感兴趣,例如,
    exec
    syscall后的按需加载[页面]和按需链接[符号]

    当一个进程执行一个
    exec
    时,内核执行上面的清理,并读取可执行文件的一小部分以确定其对象格式。主要格式是ELF,但可以使用内核理解的任何格式(例如OSX可以使用ELF[IIRC],但它也有其他格式)

    对于ELF,可执行文件有一个特殊的部分,它提供了一个完整的FS路径,即所谓的“ELF解释器”,它是一个共享库,通常是
    /lib64/ld.linux.so

    内核使用内部形式
    mmap
    ,将此映射到应用程序空间,并为可执行文件本身设置映射。大多数内容都标记为R/O页面和“不存在”

    在进一步讨论之前,我们需要讨论页面的“备份存储”。也就是说,如果出现页面错误,我们需要从页面所在的磁盘加载页面。对于heap/malloc,这通常是交换磁盘[aka paging disk]

    在linux下,通常是在安装系统时添加的“linux交换”类型的分区。当写入一个页面时,该页面必须刷新到磁盘以释放一些物理内存,然后写入其中。请注意,第一节中的页面共享算法仍然适用

    无论如何,当一个可执行文件第一次映射到内存中时,它的备份存储是文件系统中的可执行文件