最近,我在一次采访中被问到一个问题,即流程和线程之间的区别 . 真的,我不知道答案 . 我想了一会儿,给了一个非常奇怪的答案 .
线程共享相同的内存,而进程则没有 . 在回答这个问题之后,面试官给了我一个邪恶的微笑并向我解释了以下问题:
Q. Do you know the segments in which a program gets divided?
我的回答:是的(认为这很容易)Stack,Data,Code,Heap
Q. So, tell me: which segments do threads share?
我无法回答这个问题并最终说出了所有这些内容 .
请问,任何人都可以为流程和线程之间的差异提供正确和令人印象深刻的答案吗?
12 回答
在进程中,所有线程共享系统资源,如堆内存等,而Thread有自己的堆栈
所以你的ans应该是所有线程共享进程的堆内存 .
真正需要指出的是,这个问题实际上有两个方面 - 理论方面和实施方面 .
首先,让我们看一下理论方面 . 您需要了解流程在概念上是什么,以理解流程和线程之间的差异以及它们之间共享的内容 .
我们从Tanenbaum的Modern Operating Systems 3e中的2.2.2经典线程模型中得到以下内容:
他继续:
再向下,他提供了下表:
以上是线程工作所需的内容 . 正如其他人所指出的那样,像段这样的东西是依赖于OS的实现细节 .
线程共享数据和代码,而进程则不共享 . 两个都不共享堆栈 .
进程也可以共享内存,更准确地说是代码,例如在_209000之后,但这是一个实现细节和(操作系统)优化 . 多个进程共享的代码(希望)在第一次写入代码时会被复制 - 这被称为copy-on-write . 我不确定线程代码的确切语义,但我假设共享代码 .
1代码在逻辑上是私有的,但出于性能原因可能会共享 . 2我不是百分百肯定 .
通常,线程称为轻量级过程 . 如果我们将内存分为三个部分,那么它将是:代码,数据和堆栈 . 每个进程都有自己的代码,数据和堆栈部分,由于这个上下文,切换时间有点高 . 为了减少上下文切换时间,人们提出了线程的概念,它与其他线程/进程共享数据和代码段,并且它有自己的STACK段 .
你是非常正确的,但线程共享除堆栈之外的所有段 . 线程有独立的调用堆栈,但是其他线程堆栈中的内存仍然可以访问,理论上你可以在一些其他线程的本地堆栈框架中保存一个指向内存的指针(尽管你可能应该找到一个更好的放置内存的地方!) .
进程具有代码,数据,堆和堆栈段 . 现在,线程OR线程的指令指针(IP)指向进程的代码段 . 所有线程共享数据和堆段 . 那么堆栈区呢?什么是堆栈区域?它是一个由进程创建的区域,仅供其线程使用...因为堆栈可以比堆等更快的方式使用 . 进程的堆栈区域在线程之间划分,即如果有3个线程,则该过程的堆栈区域分为3个部分,每个部分分配给3个线程 . 换句话说,当我们说每个线程都有自己的堆栈时,该堆栈实际上是分配给每个线程的进程堆栈区域的一部分 . 当一个线程完成其执行时,该进程将回收该线程的堆栈 . 实际上,不仅一个进程的堆栈在线程之间划分,而且所有的一组寄存器都是一个线程像SP,PC和状态寄存器一样使用该过程的寄存器 . 因此,在共享时,代码,数据和堆区域是共享的,而堆栈区域只是在线程之间划分 .
线程共享代码和数据段以及堆,但它们不共享堆栈 .
从Wikipedia(我认为这对面试官来说会是一个非常好的答案:P)
告诉采访者,这完全取决于操作系统的实施 .
以Windows x86为例 . 只有 2 段[1],代码和数据 . 并且他们已经制作了一个,但是x86不允许段读/写和执行 . 所以他们做了两个,并设置CS指向代码描述符,其余(DS,ES,SS等)指向另一个[2] . 但两者都指向相同的东西!
面试你的人做了一个隐藏的假设,他/她没有陈述,这是一个愚蠢的伎俩 .
关于
这些细分与问题无关,至少在Windows上是这样 . 线程共享整个地址空间 . 只有1个堆栈段,SS,它指向与DS,ES和CS完全相同的东西[2] . 即整个血腥的用户空间 . 0-2GB . 当然,这并不意味着线程只有1个堆栈 . 当然每个都有自己的堆栈,但x86段不用于此目的 .
也许* nix做了不同的事情 . 谁知道 . 这个问题基于的前提被打破了 .
至少对于用户空间 .
来自
ntsd notepad
:cs=001b ss=0023 ds=0023 es=0023
在x86框架中,可以划分尽可能多的段(最多2 ^ 16-1) . ASM指令SEGMENT / ENDS允许这样做,操作符SEG和OFFSET允许初始化段寄存器 . CS:IP通常由加载程序初始化,但对于DS,ES,SS,应用程序负责初始化 . 许多环境允许所谓的“简化段定义”,如.code,.data,.bss,.stack等,并且还取决于“内存模型”(小型,大型,紧凑型等),加载器初始化段寄存器因此 . 通常.data,.bss,.stack和其他常见的片段(我20年后没有这样做,所以我不记得所有)被归为一组 - 这就是为什么通常DS,ES和SS指向teh相同的区域,但这只是为了简化事情 .
通常,所有段寄存器在运行时可以具有不同的值 . 因此,面试问题是正确的:线程之间共享CODE,DATA和STACK中的哪一个 . 堆管理是另一回事 - 它只是对操作系统的一系列调用 . 但是,如果你根本没有操作系统,比如在嵌入式系统中,你可以在代码中使用新的/删除吗?
我对年轻人的建议 - 阅读一些好的汇编编程书 . 在这方面,大学课程似乎很差 .
线程共享一切[1] . 整个过程有一个地址空间 .
每个线程都有自己的堆栈和寄存器,但所有线程的堆栈在共享地址空间中都是可见的 .
如果一个线程在其堆栈上分配一些对象,并将该地址发送到另一个线程,则它们都将具有对该对象的相同访问权限 .
实际上,我刚刚注意到一个更广泛的问题:我认为你混淆了两个词段的用法 .
可执行文件(例如,ELF)的文件格式具有不同的部分,其可以被称为段,包含编译的代码(文本),初始化的数据,链接器符号,调试信息等 . 没有堆或堆栈段这里,因为那些是仅运行时构造 .
这些二进制文件段可以单独地映射到进程地址空间,具有不同的许可(例如,对于代码/文本是只读可执行的,对于初始化数据是写入时不可执行的) .
根据约定,此地址空间的区域用于不同的目的,如堆分配和线程堆栈(由您的语言运行时库强制执行) . 它只是内存,除非你在虚拟8086模式下运行,否则可能不会被分段 . 每个线程的堆栈是在线程创建时分配的一块内存,当前堆栈顶部地址存储在堆栈指针寄存器中,并且每个线程保持其自己的堆栈指针以及其他寄存器 .
[1]好的,我知道:信号掩码,TSS / TSD等地址空间,包括其所有映射程序段,仍然是共享的 .
线程共享堆(有关于线程特定堆的研究)但当前实现共享堆 . (当然还有代码)