2016-09-26 120 views
0

我从Eric Lippert's answer了解到“两个进程可以共享非私有内存页面,如果有二十个进程全部加载同一个DLL,那么进程全部共享该代码的内存页面,它们不共享虚拟内存地址空间,它们共享记忆。”DLL如何处理来自多个进程的并发?

现在,如果硬盘上的相同DLL文件在加载到应用程序后将共享相同的物理内存(不管是RAM还是页面文件),而是映射到不同的虚拟内存地址空间,处理并发性相当困难?

据我所知,C++中的并发概念更多的是关于处理线程 - 一个进程可以启动多个线程,每个线程可以运行在一个单独的核心上,所以当不同的线程同时调用DLL时,可能会有数据竞速,我们需要互斥,锁定,信号,条件变量等等。

但是,DLL如何处理多进程?数据赛车的相同概念将会发生,不是吗?有什么工具可以处理?仍然是相同的工具集?

+0

并发只有当你共享可写数据的问题。这不是在这里发生的。 –

+0

我相信这些页面是在写入时共享的。因此,如果在DLL内部存在可变状态(而不仅仅是代码),每个进程都将获得它自己的页面版本。 (对于Windows来说,如果知道这是否可行,或者DLL内存是只读的,那么这种方法不够完善)。 – Thilo

+0

@KerrekSB所以一个DLL不能拥有自己的可写内存?一个DLL正在处理的所有内存是否由进程拥有? – athos

回答

2

DLL中包含多个“段”,每个段都有一个描述符告诉Windows它的Characteristics。这是一个32位的DWORD。代码段明显具有设置的代码位,并且通常也是可共享的位。只读数据也可以共享,而可写数据通常不具有可共享标志。

现在你可以额外段组特性的一个不寻常的组合:可写共享。这不是默认的,确实可能会导致竞争条件。所以你的问题的最终答案是:问题主要由段的缺省特性来避免,其次,具有非标准特性段的任何DLL都必须处理自己产生的问题。

+0

额外的段,可写和可共享?有趣!是否也在https://msdn.microsoft.com/en-us/library/ms809762.aspx中进行了描述? – athos

+0

@athos:不。Matt Pietrek的文章的观众并不是你的平均开发者;他们可以弄清楚它的含义。对于正常的开发者,例如有https://msdn.microsoft.com/en-us/library/h90dkhs0(v=vs.90).aspx。 (我再说一遍:你用这个想法得到的可预见的问题是自己造成的;不要告诉我们我们没有提醒你) – MSalters

+0

注意到谢谢:) – athos

5

现在,如果硬盘上的相同DLL文件在加载到应用程序后将共享相同的物理内存(不管是RAM还是页面文件),而是映射到不同的虚拟内存地址空间,则不会这使得并发性处理非常困难?

正如其他答案所指出的那样,如果共享内存在初始化后永远不会写入,那么并发问题就无关紧要,这通常是DLL的情况。如果您试图通过写入内存来更改DLL中的代码或资源,那么可能性很大,您在某个地方有一个错误的指针,最好的办法是使用访问冲突来崩溃。

不过,我想简要地也跟进您的关注:

...映射到不同的虚拟内存地址空间...

在实践中,我们非常努力地避免这种情况因为当它发生时,第一次加载代码页时会出现严重的用户注意的性能问题。 (当然,工作集可能会大量增加,这会导致其他性能问题。)

DLL中的代码通常包含硬编码的虚拟内存地址,假设代码将被加载到已知代码中,编译时虚拟内存“基址”地址。如果这种假设在运行时被违反 - 例如,那里已经存在另一个DLL,那么所有这些硬编码地址都需要在运行时进行修补,这很昂贵。

如果你想要一些历史细节,请参见主题雷蒙德的文章:https://blogs.msdn.microsoft.com/oldnewthing/20041217-00/?p=36953/