最近,我在面试中被问到一个问题,即流程和线程之间的区别。真的,我不知道答案。我想了一会儿,给了一个非常奇怪的答案。
线程共享相同的内存,而进程则不共享。在回答这个问题之后,面试官给了我一个邪恶的微笑并向我解释了以下问题:
Q值。 您是否知道程序划分的细分?
我的回答:是的(认为这很简单)堆栈,数据,代码,堆
Q值。 那么,请告诉我:线程共享哪些细分市场?
我无法回答这个问题并最终说出所有这些内容。
请问,任何人都可以为流程和线程之间的差异提供正确和令人印象深刻的答案吗?
答案 0 :(得分:150)
你非常正确,但线程共享除了堆栈之外的所有段。线程有独立的调用堆栈,但是其他线程堆栈中的内存仍然可以访问,理论上你可以在其他线程的本地堆栈框架中保存一个指向内存的指针(尽管你可能应该找到一个更好的放置内存的地方!)。 / p>
答案 1 :(得分:49)
来自Wikipedia(我认为这对面试官来说是一个非常好的答案:P)
线程与传统不同 多任务操作系统 过程中:
- 进程通常是独立的,而线程作为a的子集存在 过程
- 进程携带相当多的状态信息,而多线程 在进程共享状态中也是如此 作为记忆和其他资源
- 进程具有单独的地址空间,而线程共享它们 地址空间
- 进程只能通过系统提供的进程间进行交互 沟通机制。
- 同一进程中线程之间的上下文切换通常更快 比上下文切换 流程。
答案 2 :(得分:38)
真正需要指出的是,这个问题实际上有两个方面 - 理论方面和实施方面。
首先,让我们看一下理论方面。您需要了解流程在概念上理解流程和线程之间的区别以及它们之间共享的内容。
我们在Tanenbaum的Modern Operating Systems 3e中的 2.2.2经典线程模型部分中有以下内容:
流程模型基于两个独立的概念:资源 分组和执行。有时将它们分开是有用的; 这是线程进来的地方......
他继续说道:
查看流程的一种方法是,这是一种方法 将相关资源集中在一起进程具有地址空间 包含程序文本和数据,以及其他资源。这些 资源可能包括打开的文件,子进程,待处理的警报, 信号处理程序,会计信息等。通过他们 以流程的形式,它们可以更容易地管理。 流程的另一个概念通常是执行的线程 缩短为只是线程。该线程有一个程序计数器保持 跟踪下一步执行的指令。它有寄存器,其中 保持当前的工作变量。它有一个堆栈,其中包含 执行历史记录,每个过程调用一帧但不调用 然而从...返回。虽然线程必须在某个进程中执行,但是 线程及其过程是不同的概念,可以处理 分开。流程用于将资源组合在一起;线程 是计划在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 |
以上是线程工作所需的内容。正如其他人所指出的,像段这样的东西是依赖于操作系统的实现细节。
答案 3 :(得分:27)
告诉采访者,这完全取决于操作系统的实施。
以Windows x86为例。只有 2 段[1],代码和数据。它们都映射到整个2GB(线性,用户)地址空间。 Base = 0,Limit = 2GB。他们会做一个,但x86不允许段读/写和执行。所以他们做了两个,并设置CS指向代码描述符,其余(DS,ES,SS等)指向另一个[2]。但两者都指向相同的东西!
面试你的人做了一个隐藏的假设,他/她没有陈述,这是一个愚蠢的伎俩。
关于
Q值。那么告诉我哪个段线程 共享?
这些细分与问题无关,至少在Windows上是这样。线程共享整个地址空间。只有1个堆栈段,SS,它指向与DS,ES和CS完全相同的东西[2]。即整个血腥的用户空间。 0-2GB。当然,这并不意味着线程只有1个堆栈。当然,每个都有自己的堆栈,但x86段不用于此目的。
也许* nix做了不同的事情。谁知道。问题基于的前提被打破了。
ntsd notepad
:cs=001b ss=0023 ds=0023 es=0023
答案 4 :(得分:19)
通常,线程称为轻量级过程。如果我们将内存分为三个部分,那么它将是:代码,数据和堆栈。 每个进程都有自己的代码,数据和堆栈部分,由于这个上下文,切换时间有点高。为了减少上下文切换时间,人们提出了线程的概念,它与其他线程/进程共享数据和代码段,并且它有自己的STACK段。
答案 5 :(得分:16)
进程具有代码,数据,堆和堆栈段。现在,线程OR线程的指令指针(IP)指向进程的代码段。所有线程共享数据和堆段。那么堆栈区呢?什么是堆栈区域?它是一个由进程创建的区域,仅供其线程使用...因为堆栈可以比堆等更快的方式使用。进程的堆栈区域在线程之间划分,即如果有3个线程,则该过程的堆栈区域分为3个部分,每个部分分配给3个线程。换句话说,当我们说每个线程都有自己的堆栈时,该堆栈实际上是分配给每个线程的进程堆栈区域的一部分。当一个线程完成其执行时,该进程将回收该线程的堆栈。实际上,不仅进程的堆栈在线程之间划分,而且线程使用的所有寄存器集合如SP,PC和状态寄存器都是进程的寄存器。 因此,在共享时,代码,数据和堆区域是共享的,而堆栈区域只是在线程之间划分。
答案 6 :(得分:12)
线程共享代码和数据段以及堆,但它们不共享堆栈。
答案 7 :(得分:5)
线程共享数据和代码,而进程则不共享。两者都不共享堆栈。
进程还可以共享内存,更准确地说是代码,例如在Fork()
之后,但这是一个实现细节和(操作系统)优化。多个进程共享的代码(希望)在第一次写入代码时会被复制 - 这称为copy-on-write。我不确定线程代码的确切语义,但我假设共享代码。
Process Thread Stack private private Data private shared Code private1 shared2
1 代码逻辑私有,但出于性能原因可能会共享。 2 我不是百分百肯定。
答案 8 :(得分:4)
线程共享所有内容 [1]。整个过程有一个地址空间。
每个线程都有自己的堆栈和寄存器,但所有线程的堆栈在共享地址空间中都是可见的。
如果一个线程在其堆栈上分配了一些对象,并将该地址发送到另一个线程,则它们都具有对该对象的相同访问权。
实际上,我刚刚注意到一个更广泛的问题:我认为你混淆了 segment 一词的两种用法。
可执行文件(例如,ELF)的文件格式中有不同的部分,可以称为段,包含已编译的代码(文本),初始化的数据,链接器符号,调试信息等。没有堆或者在这里堆叠段,因为它们只是运行时构造。
这些二进制文件段可以单独映射到进程地址空间,具有不同的权限(例如,代码/文本的只读可执行文件,以及初始化数据的写入时不可执行文件)。
此地址空间的区域按惯例用于不同目的,例如堆分配和线程堆栈(由语言运行时库强制执行)。它只是内存,除非你在虚拟8086模式下运行,否则可能不会被分段。每个线程的堆栈都是在线程创建时分配的一块内存,当前堆栈顶部地址存储在堆栈指针寄存器中,每个线程都保留自己的堆栈指针以及其他寄存器。
[1]好的,我知道:信号掩码,TSS / TSD等。地址空间,包括其所有映射的程序段,仍然是共享的。
答案 9 :(得分:2)
在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中的哪一个。堆管理是另一回事 - 它只是对操作系统的一系列调用。但是,如果您根本没有操作系统,例如在嵌入式系统中,您可以在代码中使用新的/删除吗?
我对年轻人的建议 - 阅读一些好的汇编程序设计书。在这方面,大学课程似乎很差。
答案 10 :(得分:1)
线程共享堆(有关线程特定堆的研究)但当前实现共享堆。 (当然还有代码)
答案 11 :(得分:0)
在进程中,所有线程共享系统资源,如堆内存等,而Thread有自己的堆栈
所以你的ans应该是所有线程为进程共享的堆内存。
答案 12 :(得分:0)
除了全局内存,线程还共享许多其他属性 (即,这些属性对于流程是全局的,而不是特定的 到一个线程)。这些属性包括:
- 进程ID和父进程ID;
- 进程组ID和会话ID;
- 控制终端;
- 进程凭据(用户和组ID);
- 打开文件描述符;
- 记录使用
fcntl();
创建的锁- 信号处置;
- 与文件系统有关的信息:umask,当前工作目录和根目录;
- 间隔计时器(
setitimer()
)和POSIX计时器(timer_create()
);- 系统V信号量撤消(
semadj
)值(第47.8节);- 资源限制;
- 消耗的CPU时间(由
times()
返回);- 已消耗的资源(由
getrusage()
返回);和- 很好的值(由
setpriority()
和nice()
设置)。在每个线程不同的属性中, 以下:
- 线程ID(第29.5节);
- 信号屏蔽;
- 特定于线程的数据(第31.3节);
- 备用信号栈(
sigaltstack()
);- errno变量;
- 浮点环境(请参见
fenv(3)
);- 实时调度策略和优先级(第35.2和35.3节);
- CPU亲和力(特定于Linux,在35.4节中进行了介绍);
- 功能(特定于Linux,在第39章中进行了介绍);和
- 堆栈(局部变量和函数调用链接信息)。
摘录自:The Linux Programming Interface: A Linux and UNIX System Programming Handbook , Michael Kerrisk,第619页