我是流程的初学者,但仍在努力理解为它们获取子流程和为其分配内存的目的,所以我的问题是:
Q1。为什么Linux没有自动收割子进程的机制,我的意思是所有子进程都像垃圾回收一样在完成后就被终止并删除,因此用户不需要使用waitpid
来手动收割子进程?
Q2。我的教科书说,当用户使用fork()创建新的子进程时,该子进程将获得父级文本,数据和bss段,堆和用户堆栈的相同(但独立)副本。那么,这是否意味着也将相同大小的父母的内存地址分配给了孩子,而孩子的内存内容与孩子的父母完全相同?如果是这样的话,可以说我们创建了大量的子进程,不是堆栈很容易溢出吗?
,您会看到以红色突出显示的用户堆栈是父堆栈。因此,如果父程序使用fork()并在执行fork()函数时如何分配子进程的堆栈?是当前堆栈旁边的子堆栈
答案 0 :(得分:2)
Q1。为什么Linux没有自动获取子进程的机制,我的意思是所有子进程都像垃圾回收一样被终止并在完成后被删除,因此用户无需使用waitpid即可手动获得子进程?
是的。只需将SIGCHLD设置为忽略状态,您的孩子就会为您获得收益。
Q2。我的教科书说,当用户使用fork()创建新的子进程时,该子进程将获得父级文本,数据和bss段,堆和用户堆栈的相同(但独立)副本。那么是否意味着也将相同大小的父母的内存地址分配给了孩子,而孩子的内存内容与父母的孩子完全一样?
这是一个实现细节。大多数现代系统只是在两个进程之间共享内存。
如果是这样,可以说我们创建了大量的子进程,不是堆栈很容易溢出吗?
不。当您在特定进程的内存视图中用完地址空间时,堆栈溢出。由于每个fork
都会创建一个新进程,因此在任何特定进程中您都不会用完地址空间。
Q3。可以说下面的图片是父进程,请在此处输入图片描述 ...,您会看到以红色突出显示的用户堆栈是父堆栈。因此,如果父程序使用fork()并在执行fork()函数时如何分配子进程的堆栈?子堆栈在当前堆栈旁边?
子进程的堆栈在另一个进程中。因此,如果这是父进程的段的地址空间,则子进程的堆栈根本不在其中。子进程从父级地址空间的副本开始-通常与共享的实际内存页共享,至少直到任一进程尝试修改它们(取消共享它们)为止。
答案 1 :(得分:2)
尽管David Schwartz already answered提出了一些问题,但我想谈一谈基本情况。因此,不要将其视为答案,而应将其视为扩展评论。
问题是,显示的图像不能很好地表示普通用户空间应用程序在当前计算机和操作系统上看到的地址空间。
每个进程都有自己的地址空间。这是使用virtual memory实现的;从虚拟地址到实际硬件访问的映射,对于用户空间进程而言,其所有意图和目的都是不可见的。 (虚拟内存不是按地址分配的,而是使用称为pages的较小块。在所有当前体系结构上,每个页面的大小均为2字节的幂。2 12 = 4096是常见的,但也使用其他尺寸,例如2 16 = 65536和2 21 = 2097152。)
即使您使用共享内存,它也可以驻留在不同进程的不同虚拟地址上。 (这也是您不能真正在共享内存中使用指针的原因。)
在对进程进行fork()处理时,将克隆虚拟内存。 (复制本身本身并不是真正的,因为那样会浪费资源。通常OS内核使用一种称为copy-on-write的技术,因此实际使用的物理RAM用于相同的内存通过两个/所有进程,直到其中一个修改其“副本”;这时,OS内核在允许进行修改之前,先将受影响的页面复制并复制其内容。)
这意味着在fork()之后,父进程和子进程的堆栈将位于完全相同的虚拟地址上。
唯一的限制是有多少实际RAM。实际上,操作系统内核还可以将当前未使用的文件移动到swap or paging file;但是如果很快就需要这些页面,则会减慢机器的运行速度。至少在Linux上,二进制文件和库也直接映射到它们各自的文件-这就是为什么您无法在使用可执行文件时修改它们的原因-因此,除非修改代码的RAM副本,否则它们往往不使用交换/分页文件。
在大多数情况下,某些虚拟内存范围是为OS内核保留的。 not 并不意味着内核内存对于用户空间是可见的,或以任何方式均可访问;这只是一种确保与用户空间进程之间来回传输数据时,OS内核可以使用用户空间虚拟内存地址,而不将其与自己的内部地址混合的方法。本质上,OS内核不会为任何用户空间进程创建任何虚拟内存映射来使用其自身的地址,从而简化其自身的工作。
关于Linux的一个有趣的细节是,新线程的默认堆栈大小通常很大,在32位x86上为8 MiB(8,388,608字节)。除非您设置较小的堆栈,否则进程可以创建的线程数受可用虚拟内存的限制。每个用户空间进程可以使用32位x86上的低3 GiB或低于3,221,225,472的虚拟内存地址;最多可以容纳384个8 MiB堆栈。如果考虑了标准库等,通常在这些系统上,一个进程可以创建300个线程,然后再耗尽虚拟内存。如果使用较小的堆栈(例如65536字节),则即使在32位x86上,进程也可以创建数千个线程。请记住,这里的问题是可用虚拟内存地址空间不足了,而不是内存本身。