每个进程的内核虚拟内存包含啥?

Posted

技术标签:

【中文标题】每个进程的内核虚拟内存包含啥?【英文标题】:What does the Kernel Virtual Memory of each process contain?每个进程的内核虚拟内存包含什么? 【发布时间】:2011-01-27 13:32:22 【问题描述】:

当 3 个程序(可执行文件)加载到内存中时,布局可能如下所示:

alt text http://img97.imageshack.us/img97/3460/processesm.jpg

我有以下问题:

    虚拟内存的概念是否仅限于用户进程?因为,我想知道操作系统内核、驱动程序在哪里?它的内存布局如何?我想了解更多关于内核端内存的信息。我知道它的操作系统特定于你的选择(windows/linux)。

    虚拟内存的概念是基于每个进程的吗?我的意思是说 4GB 的 process1 + 4GB 的 process2 + 4GB 的 process3 = 12GB 的虚拟内存(对于所有进程)是否正确。这听起来不对。或者从总共 4GB 的空间中,1GB 被内核占用,其余 3GB 被所有进程共享。

    他们说,在 4GB 地址空间中的 32 位机器上。其中一半(或最近的 1GB)被内核占用。我可以在此图中看到“内核虚拟内存”占用 0xc0000000 - 0xffffffff (= 1 GB)。他们在谈论这个吗?或者是别的什么?只是想确认一下。

    每个进程的Kernel Virtual Memory 包含什么确切?它的布局是怎样的?

    当我们做 IPC 时,我们谈论的是共享内存。我没有看到这些进程之间共享任何内存。它住在哪里?

    资源(文件、Windows 中的注册表)对所有进程都是全局的。因此,资源/文件句柄表必须在某个全局空间中。那会在哪个区域?

    我在哪里可以了解更多关于内核方面的信息。

【问题讨论】:

图片链接失效 【参考方案1】:

    当系统使用虚拟内存时,内核也使用虚拟内存。 Windows 将使用上面的 2GB(如果您在 Windows 引导加载程序中指定了 /3GB 开关,则为 1GB)供自己使用。这包括内核代码、数据(或者至少是分页的数据——没错,Windows 可以将部分内核地址空间分页到硬盘)和页表。

    每个进程都有自己的 VM 地址空间。当发生进程切换时,页表通常会与另一个进程的页表交换。这在 x86 处理器上很简单 - 更改 CR3 控制寄存器中的页表基地址就足够了。整个 4GB 地址空间被替换为完全不同的 4GB 地址空间的表。话虽如此,通常会有一些地址空间区域在进程之间共享。这些区域在页表中用特殊标志标记,向处理器指示这些区域不需要在处理器的translation lookaside buffer 中无效。

    正如我前面提到的,内核的代码、数据和页表本身需要位于某个地方。此信息位于内核地址空间中。内核代码、数据和页表的某些部分可能会根据需要自行换出到磁盘。有些部分被认为比其他部分更重要,根本不会换掉。

    见(3)

    视情况而定。用户模式共享内存位于用户模式地址空间中。部分内核模式地址空间也很可能在进程之间共享。例如,内核代码在系统中的所有进程之间共享的情况并不少见。该内存所在的位置并不精确。我在这里使用了任意地址,但是一个进程中位于0x100000 的共享内存可能位于另一个进程中的0x101000。不同地址空间中的两个页面,完全不同的地址,可以指向同一个物理内存。

    我不确定你的意思。打开文件句柄不是对所有进程都是全局的。存储在硬盘上的文件系统对所有进程都是全局的。在Windows下,文件句柄由内核管理,对象存放在内核地址空间,由内核对象管理器管理。

    对于基于 Windows NT 的系统,我推荐 Mark Russinovich 和 David Solomon 的 Windows Internals, 5ed

对评论的回应:

现在这 3GB 是黑白共享的 流程?或者每个进程有4GB 空间?

这取决于操作系统。一些内核(例如L4 microkernel)对多个进程使用相同的页表,并使用segmentation 分隔地址空间。在 Windows 上,每个进程都有自己的页表。请记住,即使每个进程都可能获得自己的虚拟地址空间,但这并不意味着物理内存总是不同的。例如,在进程 A 中加载的 kernel32.dll 的映像与进程 B 中的 kernel32.dll 共享。大部分内核地址空间也在进程之间共享。

为什么每个进程都有内核 虚拟内存?

思考这个问题的最好方法是问自己,“如果内核不使用虚拟内存执行,它会如何工作?”在这种假设情况下,每次您的程序导致上下文切换到内核(假设您进行了系统调用)时,都必须在 CPU 在内核空间中执行时禁用虚拟内存。这样做是有成本的,当您切换回用户空间时,重新打开它也是有成本的。

此外,假设用户程序传递了一个指向某些数据的指针以供其系统调用。这个指针是一个虚拟地址。您已经关闭了虚拟内存,因此需要先将指针转换为物理地址,然后内核才能对其进行任何操作。如果您打开了虚拟内存,由于 CPU 上的内存管理单元,您可以免费获得它。相反,您必须手动翻译软件中的地址。我可以描述各种示例和场景(一些涉及硬件,一些涉及页表维护等),但其要点是拥有同质内存管理方案要容易得多。如果用户空间正在使用虚拟内存,那么如果您在内核空间中维护该方案,则编写内核会更容易。至少这是我的经验。

只有一个操作系统实例 内核对吗?那么为什么每个过程 有单独的内核虚拟空间吗?

正如我上面提到的,相当多的地址空间将在进程之间共享。内核空间中的每个进程数据在进程之间的上下文切换期间被换出,但由于只有一个内核,因此其中很多是共享的。

【讨论】:

感谢您的回复。但是somehonw我仍然不清楚。特别是关于内核和用户进程之间的内存划分。是否像总共 4GB 空间划分 b/w 内核(1GB)和其余 3GB 用于用户进程。现在这 3GB 是所有进程共享的吗?或者每个进程有 4GB 空间?为什么每个进程都有内核虚拟内存?操作系统内核只有一个实例,对吗?那么为什么每个进程都有单独的内核虚拟空间。【参考方案2】:

要回答您的问题,您需要更多地了解内核及其用于管理资源(CPU、内存等)并为应用程序提供优雅抽象的技术。

首先我想明确一点,“虚拟内存”是现代操作系统采用的一种内存管理技术;它提供了各种好处,例如进程隔离,通过保护,允许多个程序一起运行,允许大小大于系统中存在的物理内存的程序。 在这种技术下,还有两个术语“虚拟内存”和“虚拟地址空间”;它们不一样,但仍然密切相关。 (您可能想知道虚拟内存如何既是一种技术又是一种概念,但是是的,这是正确的,您将在下面理解)

在计算机科学中,“memory”这个词有 2 个含义。第一个是用于存储数据的东西(寄存器、缓存、RAM、ROM、HDD 等)。第二个,是 primary memory 的同义词(即 RAM)。 当您逐字替换时,“Virtual Memory”只不过是“Virtual RAM”。这是系统中所有时间可用的空间总量,程序在其中加载以供执行。所以这不过是物理 RAM 内存 + 内核分配的辅助存储上的交换内存。 因此,如果您在安装时由内核预留 2GB 的 RAM 和 4GB 的交换空间,那么您系统的虚拟内存为 6GB。我不会在这里解释更多关于交换内存的内容,因为这会偏离主题。

转到虚拟地址空间。 因此,要理解这一点,您需要稍微调整一下自己的思想。正如名称本身“虚拟”所说,地址空间在现实中并不存在!这只是内核给应用程序程序员创造的一种错觉(为了实现我在第 2 段中提到的很多好处) 所以内核给每个进程一个单独的虚拟地址空间。 (如果系统中没有内核并且您在硬件上运行应用程序,那么它将使用物理地址空间,即 RAM 作为其地址空间) 因此,在具有 32 位地址寄存器的机器上,内核可以为每个进程提供 2^32 = 4GB 的虚拟地址空间。 (所以这个虚拟地址空间范围随着硬件架构而变化。最新的处理器有 48 位地址寄存器,因此它们可以提供 2^48 = 256TB 的虚拟地址空间) 而且重要的是这个虚拟地址空间就在空中!!你现在会想,如果它就在空中,进程的代码、数据怎么可能被执行。是的,这需要映射到物理内存。它如何映射到物理内存是由内核使用称为分页的概念来管理的。 所以现在你可以看到内核是如何使用虚拟地址空间实现进程隔离的。所以每个进程可以生成的地址在 0 到 4GB 之间(为了简单起见,假设系统有 32 位地址寄存器),所以这是在它的整体范围内。它对系统中运行的任何其他进程一无所知。所以就像每个进程都被打包在一个单独的空间中。

所以内核代码也像另一个进程/实体。因此,如果内核要驻留在完全不同的地址空间中。那么应用程序就没有办法与内核交互了。如果应用程序不能与内核通信并且内核不能与应用程序通信,那么内核对驱动系统没有用处。 那么现在的问题是——如何让应用程序进程与内核交互? 一个选项是 - 如果内核代码存在于应用程序进程的虚拟地址空间中,那么它们可以相互交互。这就是内核代码存在于每个进程的虚拟地址空间中的原因,因为每个进程都需要与内核通信。不要担心内核代码不会为每个进程物理复制。正如我之前提到的,VAS 只是一种错觉,因此物理内存中只会存在一个内核代码副本,并且所有虚拟地址空间都将引用它(通过分页)。 在 linux 的情况下,内核将被放置在 C000 0000 到 FFFF FFFF 之间的高位地址空间(即在 VAS 中为内核保留 1GB 的原因),其余 3GB(从 0000 0000 到 BFFF FFFF)允许应用程序采用。内核所在的虚拟地址空间称为内核空间,应用程序所在的虚拟地址空间称为用户空间。 如果你仔细观察,你会想出一个问题,如果应用程序代码和内核代码都驻留在同一个虚拟地址空间中,并且由于内核驻留在预先定义好的地址位置,那么这不可能吗?让应用程序代码破坏内核代码!哎呀,起初它看起来是可能的,但它不能。 原因是 - 这在硬件的帮助下受到保护。处理器上会有一个标志,指示执行模式是 SUPERVISOR MODE 还是 USER MODE。内核空间代码应在 SUPERVISOR MODE 下执行(适当设置该标志),而用户空间代码应在 USER MODE 下执行。因此,如果您处于用户模式并尝试访问/修改内核空间中的代码,则会引发异常! (处理器根据指令尝试访问的地址来了解它。如果它高于 C000 0000 那么它可以很容易地检测到它正在尝试访问内核空间代码并且当前执行模式没有适当的权限,因为该标志是使用 USER MODE 权限设置的)。请注意:在 SUPERVISOR 模式下,处理器提供对附加指令集的访问。

我希望如果您理解这个概念,您可以回答自己的问题。在解释概念本身的同时,我已经直接回答了您的许多问题。

【讨论】:

"如何使应用程序进程与内核交互?"我认为可以通过一些系统调用来实现。仍然不明白为什么内核必须成为应用程序虚拟空间的一部分(即使它实际上并没有在物理地址空间中复制)。 此外,似乎并非所有操作系统都在进行内核到用户空间的映射:“Mac OS X 不会将内核映射到每个用户地址空间,因此每个用户/内核转换(在任一方向)都需要地址空间开关。” flylib.com/books/en/3.126.1.91/1 @mauron85:“如何让应用程序进程与内核交互?” - 是的,你是对的,唯一的方法是通过系统调用。 “仍然不明白为什么内核必须成为应用程序虚拟空间的一部分” - 处理器是一个愚蠢的单元(恕我直言),无论您输入什么指令,它都会执行,它没有智能自己的。告诉处理器执行内核代码的唯一方法是提供内核代码所在的地址(通过将下一个 inst 地址放在 'Program Counter' 寄存器中)。 而进入程序计数器的地址是一个虚拟地址,因此要将控制权传递给内核代码,您的内核代码应该位于该虚拟地址空间本身的某个位置。简单的! [但是,要执行内核代码,您的处理器应该以不同的特权运行,即内核模式特权。我想,这就是系统调用会做的事情;将权限从用户模式更改为内核模式] 我所有的理解都是针对linux内核的,我不确定Mac OS X是如何做到的。可能,无论 linux 做什么(这是我解释的),都是实现这一目标的一种方式。

以上是关于每个进程的内核虚拟内存包含啥?的主要内容,如果未能解决你的问题,请参考以下文章

Linux进程地址空间与虚拟内存

Linux虚拟内存相关知识

linux内核内存虚拟地址映射物理地址

游戏开发面试准备8(虚拟内存,c++ override,用户态内核态,显存内存缓存)

linux进程内存相关

Linux 内核Linux 内核特性 ( 组织形式 | 进程调度 | 内核线程 | 多平台虚拟内存管理 | 虚拟文件系统 | 内核模块机制 | 定制系统调用 | 网络模块架构 )