最近,我在一次采访中被问到一个问题:进程和线程有什么区别?真的,我不知道答案。我想了一会儿,给出了一个非常奇怪的答案。

线程共享相同的内存,而进程不共享。回答完这个问题后,面试官对我邪恶地笑了笑,然后接连问了我几个问题:

问:你知道节目分成哪些部分吗?

我的答案是:是的(认为这很简单)堆栈,数据,代码,堆

问:那么,告诉我:线程共享哪些片段?

我无法回答这个问题,最后只能把它们都说了出来。

请问,谁能就进程和线程之间的区别给出正确的、令人印象深刻的答案?


当前回答

线程共享所有内容[1]。整个进程只有一个地址空间。

每个线程都有自己的堆栈和寄存器,但所有线程的堆栈都在共享地址空间中可见。

如果一个线程在它的堆栈上分配了某个对象,并将该地址发送给另一个线程,它们对该对象的访问权是相等的。


实际上,我刚刚注意到一个更广泛的问题:我认为你混淆了segment这个词的两种用法。

可执行文件(如ELF)的文件格式有不同的部分,可以称为段,包含编译的代码(文本)、初始化的数据、链接器符号、调试信息等。这里没有堆段或堆栈段,因为它们是仅运行时结构。

这些二进制文件段可以分别映射到进程地址空间,具有不同的权限(例如,对于代码/文本,只读可执行;对于初始化的数据,写时复制不可执行)。

根据约定(由语言运行库强制执行),这个地址空间的区域用于不同的目的,如堆分配和线程堆栈。不过,这些都只是内存,并且可能没有分段,除非您在虚拟8086模式下运行。每个线程的堆栈是在线程创建时分配的内存块,当前堆栈顶部地址存储在堆栈指针寄存器中,每个线程保留自己的堆栈指针和其他寄存器。


好的,我知道:信号掩码,TSS/TSD等。地址空间,包括它所有映射的程序段,仍然是共享的。

其他回答

线程共享代码、数据段和堆,但不共享堆栈。

线程共享所有内容[1]。整个进程只有一个地址空间。

每个线程都有自己的堆栈和寄存器,但所有线程的堆栈都在共享地址空间中可见。

如果一个线程在它的堆栈上分配了某个对象,并将该地址发送给另一个线程,它们对该对象的访问权是相等的。


实际上,我刚刚注意到一个更广泛的问题:我认为你混淆了segment这个词的两种用法。

可执行文件(如ELF)的文件格式有不同的部分,可以称为段,包含编译的代码(文本)、初始化的数据、链接器符号、调试信息等。这里没有堆段或堆栈段,因为它们是仅运行时结构。

这些二进制文件段可以分别映射到进程地址空间,具有不同的权限(例如,对于代码/文本,只读可执行;对于初始化的数据,写时复制不可执行)。

根据约定(由语言运行库强制执行),这个地址空间的区域用于不同的目的,如堆分配和线程堆栈。不过,这些都只是内存,并且可能没有分段,除非您在虚拟8086模式下运行。每个线程的堆栈是在线程创建时分配的内存块,当前堆栈顶部地址存储在堆栈指针寄存器中,每个线程保留自己的堆栈指针和其他寄存器。


好的,我知道:信号掩码,TSS/TSD等。地址空间,包括它所有映射的程序段,仍然是共享的。

在进程中,所有线程共享系统资源,如堆内存等,而线程有自己的堆栈

所以你的ans应该是一个进程中所有线程共享的堆内存。

真正需要指出的是,这个问题有两个方面——理论方面和实现方面。

首先,让我们看看理论方面。您需要从概念上理解进程是什么,才能理解进程和线程之间的区别以及它们之间共享的内容。

我们从Tanenbaum的2.2.2节现代操作系统3e中的经典线程模型中获得以下内容:

流程模型基于两个独立的概念:资源 分组和执行。有时把它们分开是有用的; 这就是线程....的由来

他继续说:

One way of looking at a process is that it is a way to group related resources together. A process has an address space containing program text and data, as well as other resources. These resource may include open files, child processes, pending alarms, signal handlers, accounting information, and more. By putting them together in the form of a process, they can be managed more easily. The other concept a process has is a thread of execution, usually shortened to just thread. The thread has a program counter that keeps track of which instruc­tion to execute next. It has registers, which hold its current working variables. It has a stack, which contains the execution history, with one frame for each proce­dure called but not yet returned from. Although a thread must execute in some process, the thread and its process are different concepts and can be treated sepa­rately. Processes are used to group resources together; threads are the entities scheduled for execution on the CPU.

他进一步提供了以下表格:

Per process items             | Per thread items
------------------------------|-----------------
Address space                 | Program counter
Global variables              | Registers
Open files                    | Stack
Child processes               | State
Pending alarms                |
Signals and signal handlers   |
Accounting information        |

以上就是线程工作所需要的。正如其他人指出的那样,像段这样的东西是依赖于操作系统的实现细节的。

告诉面试官这完全取决于操作系统的实现。

以Windows x86为例。只有两个段[1],代码和数据。它们都被映射到整个2GB(线性,用户)地址空间。基础= 0,限制= 2 gb。他们本来可以做一个,但x86不允许一个段同时读/写和执行。所以他们做了两个,并设置CS指向代码描述符,其余(DS, ES, SS等)指向另一个[2]。但两者都指向同样的东西!

面试你的人做了一个隐藏的假设,他/她没有说出来,这是一个愚蠢的伎俩。

所以关于

问:告诉我是哪个线段 分享吗?

细分市场与问题无关,至少在Windows上是这样。线程共享整个地址空间。只有一个堆栈段SS,它指向DS ES CS做的完全一样的东西[2]。比如整个用户空间。0-2GB。当然,这并不意味着线程只有一个堆栈。当然,每个都有自己的堆栈,但x86段并不用于此目的。

也许*nix做一些不同的事情。谁知道呢。这个问题的前提被打破了。


至少对于用户空间是这样的。 从ntsd记事本:cs=001b ss=0023 ds=0023 es=0023