进程VS线程:两个进程能共享同一块共享内存吗?两个线程呢?

19

在思考了共享内存的整个概念之后,一个问题浮现出来:

两个进程能够共享同一块共享内存吗?两个线程可以共享同一块共享内存吗?

经过更加清晰的思考,我几乎可以确定两个进程可以共享同一块共享内存段,其中第一个是父进程,第二个是使用fork()创建的子进程,但是两个线程呢?

谢谢。

4个回答

32
两个进程可以共享同一个共享内存段吗?
是和否。通常,在现代操作系统中,当第一个进程forked另一个进程时,它们共享相同的内存空间,并在所有页面上设置copy-on-write。对任何读写内存页面所做的更新都会导致为该页面制作副本,因此将有两个副本,内存页面将不再在父进程和子进程之间共享。这意味着只有只读页面或尚未写入的页面将被共享。
如果一个进程没有从另一个进程fork出来,则通常不共享任何内存。一个例外是,如果您运行了相同程序的两个实例,则它们可能共享代码甚至静态数据段,但不会共享其他页面。另一个例外是某些操作系统允许应用程序共享由多个应用程序加载的动态库的代码页面。

还有特定的内存映射调用可以共享同一个内存段。该调用指定映射是只读还是读写。如何做这取决于操作系统。

两个线程可以共享相同的共享内存吗?

当然可以。通常,多线程进程中的所有内存都由所有线程“共享”,除了一些相对较小的堆栈空间,这些堆栈空间是每个线程独有的。这通常是线程的定义,即它们都在同一个内存空间内运行。

线程还具有额外的复杂性,因为它们在与处理器/核心相关的高速存储器中具有缓存内存段。这个缓存内存是共享的,并且对内存页面的更新会根据同步操作刷新到中央存储器中。


关于“此缓存内存不共享,对内存页面的更新取决于同步操作是否刷新到中央存储”,这是好事还是坏事? - Pacerier
“线程还具有高速缓存内存段的附加复杂性,这些内存段与处理器/核心相关联”:这是操作系统内核的事情吗?还是只是软件语言库的特性? - Pacerier
这是非常好的事情@Pacerier。本地CPU内存缓存是使得多线程程序性能提升的主要原因之一。这是由硬件而不是操作系统或软件支持的。软件需要在内存屏障方面考虑到内存缓存,以控制刷新和更新,但由于CPU设计的自动特性,它们会自动发生。 - Gray
你是真的认为线程和进程是硬件而不是操作系统的东西吗? - Pacerier
线程是Java的一个概念,虽然在很大程度上它是由操作系统的线程支持。进程是一个操作系统的概念。CPU内存缓存是硬件的一部分。@Pacerier - Gray

5
总的来说,进程的主要目的是防止共享内存!虽然在大多数常见的操作系统上可以通过共享内存段进行进程间通信,但默认情况下没有这些机制。如果未正确设置和管理共享区域,则有可能导致segFault/AV,如果不幸的话,还会出现UB。
属于同一进程的线程不具有硬件内存管理保护,可以分享他们喜欢的任何东西,明显的缺点是他们几乎可以破坏任何他们想破坏的东西。我从未真正发现这是一个巨大的问题,尤其是对于现代面向对象语言,它们倾向于将指针“结构化”为对象实例(Java、C#、Delphi)。

2
一般来说,每个进程都占据一个与其他所有进程隔离的内存空间,以避免不必要的交互(包括可能带来安全问题的交互)。然而,通常情况下,进程之间有一种共享内存的方式。有时这样做是为了减少 RAM 的占用(例如,在 VAX/VMS 中,“已安装文件”就是这样的一个例子)。这也可以是协作进程之间进行高效通信的一种方式。如何实现/构建/管理这种共享(例如,父/子关系)取决于特定操作系统提供的功能和应用程序代码中实现的设计选择。
在进程内部,每个线程可以访问与该进程的所有其他线程完全相同的内存空间。唯一属于线程自身的是“执行上下文”,其中一部分是其堆栈(虽然没有任何东西阻止一个线程访问或操纵同一进程的另一个线程“拥有”的堆栈)。

2
是的,两个进程都可以附加到共享内存段。如果不是这样,共享内存段就没有多少用处了,因为这是共享内存段背后的基本思想 - 这就是为什么它是IPC(进程间通信)的几种形式之一的原因。

同一进程中的两个线程也可以同时附加到共享内存段,但考虑到它们已经共享了所在进程的整个地址空间,可能没有太大的意义(尽管有人可能会将其视为一个挑战,提出一个或多个更或多少有效的用例来做到这一点)。


我怀疑更接近于“不太有效”而不是更有效:) 请不要建议开发人员使用线程做更奇怪的事情,已经够糟糕了<g> - Martin James

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接