【问题标题】:How are static objects shared across DLL and Windows Binary memory静态对象如何跨 DLL 和 Windows 二进制内存共享
【发布时间】:2012-05-21 23:15:27
【问题描述】:

我有一点关于 DLL 的设计问题,我目前处于你可能称之为的问题中,正如维基百科所指的那样 DLL Hell 问题如下:

我创建了一个系统,该系统具有多个实现为 DLL 的模块。这些在应用程序中使用,并且可以加载 DLL,但并非全部都需要。如果只完成数学之类的事情,它可以链接到“Utilities.dll”并使用它。 问题是,我有一个记录器/跟踪器。这会将所有内容记录到一个文件和一个调试控制台中,调试控制台只是一个流输出。问题是如何处理多个 DLL 尝试使用同一个日志类。看到日志类在这个“Utilities.dll”中,“DataManagers.dll”和其他 dll 之类的东西也想使用日志类功能。这包括登录到文件。我目前正在使用临界区来确保不会发生写入冲突,但看到临界区是在用户模式下实现的,我必须在某些时候切换到互斥锁或类似的东西才能拥有内核模式对象。但是,如果我只使用关键部分,那么在 DLL 内存中拥有多个日志类实例将意味着我会遇到一些严重的问题。

我似乎无法一起解决的问题是,所有 DLL 都能够使用相同的日志类实例,而不必一个一个地链接到 Utilities.dll。我不想将 8 个 dll 加载到我的演示项目中,并让所有这 8 个引用带有日志类的那个 dll,如果我需要更多类似日志类的东西,这将是一个连锁反应。有没有办法正确地做到这一点?使用类的功能,在其他 DLL 中的 DLL 中使用静态函数,在 .exe Windows 二进制文件中使用相同的“静态”函数,从而不会在写入日志文件甚至调试控制台的输出流时发生冲突。

如果我完全错了并试图做不可能的事情,请告诉我并帮助实现尽可能接近此的目标。我知道在 DLL 中使用 Singleton 模式时会出现一些类似的问题,但这可以通过

到目前为止我已经尝试过:

  • 在初始化 DLL 的类时,给它们一个日志库的实例,但这违背了类具有所有静态成员的目的。

我也发现了这个类似的问题(甚至我的全球工具所在的库的名称如何......)但它没有回答我的问题并且有一些不同的方法以及从 09 年开始。 How to mimic the "multiple instances of global variables within the application" behaviour of a static library but using a DLL?

【问题讨论】:

  • dll dll dll dll。可能是什么问题呢? ;)
  • 是的,一个 DLL 很容易实现,问题是当更多人加入时,sigh

标签: c++ memory memory-management dll


【解决方案1】:

您的问题根本不是“DLL 地狱”。

这是一个基本了解 DLL 如何工作的问题。每个进程最多加载一次 DLL。因此,如果您的 DLL 被多个其他 DLL 使用,它仍然会在每个进程中存在一次。如果您的日志类对象在 DLL 中实现为单例(例如全局对象),那么每个进程都有一个对象。

然后应该保护一个对象以防止在进程中并发使用。关键部分是流程本地的,非常适合这一点。您不需要互斥体,因为两个进程各自都有自己的 Utilities.DLL 及其对象的副本。

如果您的记录器记录到单个固定文件,您可能会遇到问题。在这种情况下,两个进程会尝试登录到同一个文件。这是一个设计问题,无论如何您都不想破解。将您的日志输出分开,因此请确保每个记录器写入一个唯一的日志文件。

【讨论】:

  • 以及为什么 MSND 无法在您所做的几行中解释这一点,我不知道。非常感谢,我真的不知道使用多个 DLL 时内存是如何组织的。但是根据你所说的,这意味着我在这里没有真正的问题。我可以在我创建的每个模块中使用实用程序 DLL 和单例日志记录类。并且不会发生从不同进程记录到同一个文件,它将是多个线程记录到同一个文件,但这是关键部分帮助我的地方。谢谢!
【解决方案2】:

从下图可以看出,,

实际上,在一个进程中多次映射一个 DLL 是很常见的(与一种非常流行的观点相反)!这是一个基本了解 DLL 如何工作的问题:-)

在干净的 Windows XP 系统上,您可以在 iexplore.exe 进程中看到多个 dxtmsft.dll、ieframe.dll、iepeers.dll 等实例。所有这些 Dll 都只是映射到不同的地址。

【讨论】:

    【解决方案3】:

    使用 WinApi 的 CreatePipe 或 CreateNamedPipe 函数,这是一个使用 Windows 共享内存的类似队列的通信部分:

        BOOL WINAPI CreatePipe(
      __out         PHANDLE hReadPipe,
      __out         PHANDLE hWritePipe,
      __in          LPSECURITY_ATTRIBUTES lpPipeAttributes,
      __in          DWORD nSize
    );
    

        HANDLE WINAPI CreateNamedPipe(
      __in          LPCTSTR lpName,
      __in          DWORD dwOpenMode,
      __in          DWORD dwPipeMode,
      __in          DWORD nMaxInstances,
      __in          DWORD nOutBufferSize,
      __in          DWORD nInBufferSize,
      __in          DWORD nDefaultTimeOut,
      __in          LPSECURITY_ATTRIBUTES lpSecurityAttributes
    );
    

    使用 ReadFile 和 WriteFile 从其他线程/进程中通过管道进行通信。

    为了从管道中读取,进程在调用 ReadFile 函数时使用读取句柄。 ReadFile 在以下情况之一为真时返回:在管道的写端完成写操作、已读取请求的字节数或发生错误。

    当进程使用 WriteFile 写入匿名管道时,直到写入所有字节后,写入操作才会完成。如果在写入所有字节之前管道缓冲区已满,则 WriteFile 不会返回,直到另一个进程或线程使用 ReadFile 来提供更多可用缓冲区空间。

    使用一个线程处理输入并将日志写入文件和/或控制台。

    更多关于管道的信息:http://msdn.microsoft.com/en-us/library/windows/desktop/aa365780(v=vs.85).aspx

    【讨论】:

    • 管道如何解决他的问题?管道几乎只是一个文件,它需要另一个进程或至少另一个线程。仅仅引用 Microsoft 文档并不是真正的答案,尤其是当您没有明确说明您在引用时。
    • 管道不是文件!它是(正如我已经说过的)跨进程的可共享内存部分。它将以这样一种方式解决问题,即可以像访问虚拟 ram-disk 一样访问管道,进程/线程可以在其中写入或读取。您不需要明确需要另一个进程来访问管道,但我建议使用一个工作线程来处理管道事务(读/写)。据我所知,他需要某种文件/控制台服务器,而不是使用同一类的数千个实例。
    • 问题更多在于在一个进程中加载​​多个 DLL 会如何影响内存对象,如单例或静态类。我之前已经实现了 WinAPI 管道,但我更喜欢只使用 fstream 并使用临界区(如果跨进程共享,则使用互斥锁)阻塞多个线程。
    • @DragosDutu:管道的行为不像共享内存。它的行为就像一个文件。毕竟,您可以使用 ReadFile 和 WriteFile 访问它。您还需要多少文件?
    • @CareyGregory WinApi 使用 ReadFile/WriteFile 进行几乎任何类型的需要数据传输的通信。甚至 tcp/ip 套接字也可以被视为文件句柄。 Aso USB 通信,甚至老式的 RS-232 数据传输(即 COM1/COM2?)都可以通过 ReadFile/WriteFile 进行处理。看看这个,特别是hFile参数的描述:msdn.microsoft.com/en-us/library/windows/desktop/…
    猜你喜欢
    • 1970-01-01
    • 2010-11-29
    • 2016-06-17
    • 1970-01-01
    • 2010-10-20
    • 1970-01-01
    • 2011-10-01
    • 1970-01-01
    • 2011-06-22
    相关资源
    最近更新 更多