我在一次采访中被问到这样一个问题:
在windows中,假设有一个exe依赖于一些dll,当你启动exe时,会加载依赖的dll,这些dll是在内核态还是用户态加载的?
我不太确定这个问题,没有提到答案——你能帮忙解释一下吗?
谢谢。
我在一次采访中被问到这样一个问题:
在windows中,假设有一个exe依赖于一些dll,当你启动exe时,会加载依赖的dll,这些dll是在内核态还是用户态加载的?
我不太确定这个问题,没有提到答案——你能帮忙解释一下吗?
谢谢。
我不是 Windows 内部如何工作的专家,但我知道正确的答案是用户模式,因为只有与您的操作系统相关的进程才被允许进入内核空间http://en.wikibooks.org/ wiki/Windows_Programming/User_Mode_vs_Kernel_Mode
基本上,如果它不是操作系统进程,它将在用户空间中分配。
这个问题非常不精确/模棱两可。“在 Windows 中”暗示了一些东西,但不清楚是什么。面试官可能指的是 Win32 子系统 - 即您通常作为最终用户看到的 Windows 部分。问题的最后一部分更加模棱两可。
现在,虽然进程和部分对象(在 MSDN 中称为 MMF,加载的 PE 映像,例如.exe
and.dll
和.sys
)确实是内核对象,并且需要底层执行程序(和内存管理器等)的一些帮助,但 DLL 中的相应代码(包括)当从用户模式进程调用时,DllMain
其行为将与任何其他用户模式进程完全相同。也就是说,从 DLL 运行代码的每个线程都将转换到内核模式以最终使用 OS 服务(打开文件、加载 PE 文件、创建事件等),或者在足够的情况下在用户模式下做一些事情。
也许面试官甚至对有时被称为“内核空间”和“用户空间”的内存范围感兴趣,传统上是 32 位的 2 GB 边界。是的,DLL 通常在 2 GB 边界以下结束,即在“用户空间”中,而其他共享内存(内存映射文件,MMF)通常在该边界之上结束。
面试官甚至有可能成为对 DLL 的常见误解的受害者。DLL 本身只是一块休眠的内存,它从来没有单独运行任何东西(是的,对于DllMain
. 当然,加载器会处理各种事情,例如重定位,但最终如果不显式或隐式调用(在加载 DLL 的进程的某个线程的上下文中),什么都不会运行。因此,出于所有实际目的,该问题需要您回问。
我想知道面试官是否对他/她所问的概念有一个清晰的认识。
让我添加更多信息。从对另一个答案的评论看来,人们对 DLL 也存在关于驱动程序的相同误解。与 EXE(或最终的“进程”)相比,驱动程序更接近 DLL 的概念。问题是驱动程序在大多数情况下都不自己做任何事情(尽管它可以创建系统线程来改变它)。驱动程序不是进程,它们不会创建进程。
对于任何为 Windows 进行任何重要应用程序开发的人来说,答案显然是用户模式。让我解释两件事。
动态链接库
动态链接库与常规的旧链接库或 .lib 非常相似。当您的应用程序使用 .lib 时,它会在编译后粘贴到函数定义中。您通常使用 .lib 来存储 API 并修改函数而不必重建整个项目,只需将具有相同名称的新 .lib 粘贴到旧的并且只要接口(函数名称和参数)没有更改它仍然有效。伟大的模块化。
.dll 做同样的事情,但它不需要重新链接或任何编译。您可以将 .dll 视为本质上的 .lib,它与使用它的应用程序一样被编译为 .exe。只需放置共享名称和函数签名的新 .dll 即可。您只需更换 .dll 即可更新您的应用程序。这就是为什么大多数 Windows 软件由 .dll 和一些 exe 组成的原因。
.dll 的使用有两种方式
隐式链接
要以这种方式链接,如果您有一个 .dll userapplication.dll
,您将拥有一个userapplication.lib
定义 dll 中所有入口点的文件。您只需链接到静态链接库,然后将 .dll 包含在工作目录中。
显式链接
或者,您可以通过首先调用LoadLibrary(userapplication.dll)
返回 .dll 的句柄以编程方式加载 .dll。然后GetProcAddress(handle, "FunctionInUserApplicationDll")
它返回一个你可以使用的函数指针。这样,您的应用程序可以在尝试使用之前检查内容。c#
有点不同但更容易。
用户/内核模式
Windows 有两种主要的执行模式。用户模式和内核模式(内核进一步分为系统和会话)。对于用户模式,物理内存地址是不透明的。用户模式使用映射到真实内存空间的虚拟内存。巧合的是,用户模式驱动程序也是 .dll 的。用户模式应用程序通常可以获得大约 4Gb 的虚拟寻址空间来使用。两个不同的应用程序无法有意义地使用这些地址,因为它们位于该应用程序或进程的上下文中。用户模式应用程序无法在不回退到内核模式驱动程序的情况下知道它的物理内存地址。基本上你习惯于编程的一切(除非你开发驱动程序)。
内核模式受到用户模式应用程序的保护。大多数硬件驱动程序在内核模式的上下文中工作,通常所有的 Windows api 都分为两类用户和内核。内核模式驱动程序使用内核模式 api,不使用用户模式 api,因此不使用 .dll(您甚至不能打印到控制台,因为这是用户模式 api 集)。相反,他们使用 .sys 文件,这些文件是驱动程序,并且在用户模式下基本上以完全相同的方式工作。.sys 是一种 pe 格式,所以基本上 .exe 就像 .dll 一样,就像没有 main() 入口点的 .exe。
所以从提问者的角度来看,你有两组
[内核/.sys] 和 [用户/.dll 或 .exe]
内核中确实没有 .exe,因为操作系统所做的一切都不是用户。当系统或另一个内核组件启动某些东西时,它们通过调用 DriverEntry() 方法来完成它,所以我猜这就像 main()。
所以这个问题在这个意义上是很简单的。