我对 写 时 复制的 理解是:“每个人都有相同数据的单个共享副本,直到被写入,然后再创建一个副本”。
操作系统可以设置所需的任何“写时复制”策略,但是通常,它们都执行相同的操作(即最有意义的操作)。
松散地,对于类似POSIX的系统(Linux,BSD,OSX),有四个感兴趣的领域(您所说的段):(data
哪里int x = 1;
),bss
(哪里int y
),sbrk
(这是堆/ malloc)和stack
当fork
完成后,OS设置了新的一页地图对孩子说股所有的父母的页面。然后,在父级 和 子级的页面映射中,所有页面都标记为只读。
每个页面映射还具有一个参考计数,该参考计数指示共享该页面的进程数。在派生之前,引用计数为1,之后为2。
现在,当 任何一个 进程尝试写入R /
O页面时,都会出现页面错误。操作系统将看到这是用于“写入时复制”,将为该进程创建一个私有页面,从共享中复制数据,将该页面标记为该进程可写并继续。
它还会降低引用计数。如果现在refcount [再次]为1,则OS会将 另一个 进程中的页面标记为可写且不可共享[这消除了另一个进程中的第二个页面错误-
加速是因为这时OS知道另一个过程应该可以自由地再次写[]。这种加速可能取决于操作系统。
实际上,该bss
部分将获得 更多 特殊待遇。在它的初始页面映射中,所有页面都映射到包含所有零(也称为“零页面”)的 单个 页面。该映射标记为R
/ O。因此,该bss
区域的大小可能为千兆字节,并且仅会占用一个物理页面。这种单一的,特殊的,零页面之间共享 所有 bss
的部分 全部
过程,他们不管有 任何 在所有的相互关系。
因此,一个进程可以从该区域的任何页面读取并获得期望的值:零。只有当进程尝试写入此类页面时,才会启动相同的写入机制副本,该进程获得私有页面,调整映射,然后恢复该进程。现在可以自由地将其写入页面。
操作系统可以再次选择其策略。例如,在派生之后,共享 大多数 堆栈页可能会更有效,但是从“当前”页的私有副本开始,这是由堆栈指针寄存器的值确定的。
当exec
在子fork
节点上执行系统调用时,内核必须撤消在[降低引用计数],释放子节点的映射等过程中完成的许多映射,以及还原父节点的原始页面保护(即它将不再共享)它的页面,除非它另做一个fork
)
尽管这不是您原始问题的一部分,但可能涉及到一些相关的活动,例如 ,按需加载 [页面]和 按需exec
调用[符号]在syscall 之后 链接
。
当进程执行an时exec
,内核将执行上述清理,并读取可执行文件的一小部分以确定其对象格式。主要格式是ELF,但是可以使用内核理解的任何格式(例如OSX可以使用ELF
[IIRC],但也有其他格式)。
对于ELF,可执行文件具有一个特殊的部分,该部分提供指向所谓的“
ELF解释器”的完整FS路径,该路径是共享库,通常为/lib64/ld.linux.so
。
内核使用的内部形式mmap
将其映射到应用程序空间,并为可执行文件本身设置映射。大多数东西都被标记为R / O页面 并且 “不存在”。
在继续之前,我们需要讨论页面的“后备存储”。也就是说,如果发生页面错误,我们需要从磁盘从何处加载页面。对于堆/
malloc,通常是交换磁盘[aka分页磁盘]。
在linux下,通常是安装系统时添加的“ linux
swap”类型的分区。当写入页面时必须将其刷新到磁盘上以释放一些物理内存时,它会被写入那里。请注意,第一部分中的页面共享算法仍然适用。
无论如何,当可执行文件首先 映射 到内存时,其后备存储是文件系统中的可执行文件。
因此,内核将应用程序的程序计数器设置为指向ELF解释器的起始位置,并将控制权转移给它。
ELF解释器负责其业务。每当它试图执行的自身[一个“代码”页],其一部分 映射 但 不
装载,发生页面错误和负载从所述后备存储器页(例如,ELF翻译的文件),并改变所述映射至R / O但 存在 。
ELF解释器,共享库和可执行文件本身就会发生这种情况。
现在,ELF解释器将mmap
用于映射libc
到应用程序空间(同样,受需求负载的影响)。如果ELF解释器必须修改代码页以重新定位符号[或尝试写入任何将文件作为后备存储的文件,例如data
页面],则会发生保护错误,内核会将页面的后备存储更改为磁盘上的
文件 到交换磁盘上的页面,调整保护,然后恢复应用程序。
内核还必须处理ELF解释器(例如)试图写入[说] data
尚未加载的页面的情况(即,它必须先加载它,然后将后备存储更改为交换磁盘)。
ELF解释器然后使用的部分内容libc
来帮助其完成初始链接活动。它重新定位了允许其完成工作所需的最低要求。
但是,ELF解释器 不会 在大多数其他共享库的所有符号附近重新定位。它 会 去翻可执行文件,并再次使用mmap
,创建一个 映射
的共享库可执行需求(即你看到的,当你这样做ldd executable
)。
这些到共享库和可执行文件的映射可以被认为是“段”。
每个共享库中都有一个指向跳转的解释器的符号表。但是,ELF解释器所做的更改很少。
[ 注意: 这是一个宽松的解释]仅当应用程序尝试调用给定函数的跳转条目时[这就是GOT等。等
您可能已经看到的东西]是否发生重定位。跳转条目将控制权转移给解释器,解释器查找符号的 真实
地址并调整GOT,以便它现在直接指向符号的最终地址并重做调用,该调用现在将调用实函数。在随后调用相同的给定函数时,它现在可以直接执行。
这称为“按需链接”。
所有这些mmap
活动的副产品是经典sbrk
syscall,几乎没有用处。它将很快与共享库内存映射之一发生冲突。
因此,现代libc
不使用它。当malloc
需要从OS获得更多内存时,它将从匿名服务器请求更多内存,mmap
并跟踪哪些分配属于哪个mmap
映射。(即,如果释放了足够的内存来构成整个映射,则free
可以执行munmap
)。
因此,总而言之,我们同时进行“按需复制”,“按需加载”和“按需链接”。看来复杂,但让fork
和exec
快速进入,顺利。这增加了一些复杂性,但是仅在需要时(“按需”)才执行额外的开销。
因此,根据需要,开销活动不是在程序开始启动时出现较大的停顿/延迟,而是在程序的整个生命周期内分散开来。
我使用下面的下拉列表按UTC偏移量存储应用程序的时区: 使用PHP时,我并没有最简单的方法将这些转换为时区缩写,我唯一的编程方法就是对大约400个时区缩写列表进行排序。有人知道这个下拉列表中的每个时区是什么吗,以及夏令时是什么吗?(我假设我需要手动定义这两个列表) 编辑:将这个列表解析为每个时区的一个缩写,但它们不是“流行”的。
本文向大家介绍常用的Mysql复制架构有哪些?相关面试题,主要包含被问及常用的Mysql复制架构有哪些?时的应答技巧和注意事项,需要的朋友参考一下 1)一主多从 在主库读取请求压力非常大的场景下, 可以通过配置一主多从复制架构实现读写分离, 把大量对实时性要求不是特别高的读请求通过负载均衡分布到多个从库上, 降低主库的读取压力,在主库出现异常宕机的情况下, 可以把一个从库切换为主库继续提供服务 。
我是从多年使用spring和刚刚开始研究JEE7和CDI的时候来回答这个问题的。 在Spring世界中,您必须将粘贴在bean上才能将其转换为Spring bean,Spring将注入依赖项,但在CDI中,似乎没有的等效项。 null
ChatGPT可能会对世界产生的负面影响
我正在使用GCC为ARM开发一个C。我遇到了一个问题,我没有启用优化,我无法为我的代码创建二进制(ELF),因为它不适合可用空间。然而,如果我只是启用调试优化(-Og),这是我所知的最低优化,代码很容易适合。 在这两种情况下,都启用了-ffunction-节、-fdata-节、-fno-异常和-Wl、--gc-节。 闪存大小:512 kB 没有优化:. text溢出约200 kB 使用-Og优化:
我正在开发一个。NET应用程序(例如A)将与其他应用程序对话。NET应用程序(例如B)。应用程序A将被Java应用程序(比如X)使用。我目前正在使用Apache Thrift。除了对OOPS特性(如重载和继承)的基本支持外,Thrift非常棒。当然,我们可以用我们想要的方式定制Thrift编译器,因为它是一种开源技术。 我的一个朋友建议在应用程序A中使用WCF并使用WSHttp绑定。使用Basic