线程之间共享什么资源?
最近,我在采访中被问到一个过程和一个线程之间的区别是什么。 真的,我不知道答案。 我想了一会儿,给了一个非常奇怪的答案。
线程共享相同的内存,进程不。 回答这个问题后,面试官给了我一个邪恶的微笑,向我解释了下面的问题:
问:你知道程序分割的部分吗?
我的回答: 是的(认为这是一个简单的)堆栈,数据,代码,堆
问: 那么告诉我:线程共享哪些细分?
我无法回答这个问题,最后说了所有这些。
请问任何人都可以提供一个stream程和一个线程之间的区别的正确和令人印象深刻的答案?
你几乎是正确的,但线程共享除了堆栈的所有部分。 线程有独立的调用堆栈,但是其他线程堆栈中的内存仍然可以访问,理论上你可以在其他线程的本地堆栈帧中保存一个指向内存的指针(尽pipe你可能应该find一个放置内存的更好的地方!
从维基百科 (我认为这对面试官来说是一个非常好的答案:P)
线程不同于传统的多任务操作系统进程:
- 进程通常是独立的,而线程作为进程的子集存在
- 进程携带相当多的状态信息,而进程内的多个线程共享状态以及内存和其他资源
- 进程具有独立的地址空间,而线程共享其地址空间
- 进程只能通过系统提供的进程间通信机制进行交互。
- 同一进程中的线程之间的上下文切换通常比进程之间的上下文切换更快。
真正需要指出的是这个问题实际上有两个方面 – 理论方面和实现方面。
首先,我们来看看理论方面。 你需要理解一个过程是从概念上理解一个过程和一个线程之间的区别以及它们之间共享的东西。
我们从2.2.2节Tanenbaum的现代操作系统3e中 的经典线程模型 :
stream程模型基于两个独立的概念:资源分组和执行。 有时分开它们是有用的。 这是线程进来….
他继续说道:
查看stream程的一种方式是将相关资源分组在一起。 一个进程有一个包含程序文本和数据的地址空间,以及其他资源。 这些资源可能包括打开的文件,subprocess,待处理的警报,信号处理程序,会计信息等等。 通过将它们以stream程的forms放在一起,可以更容易地进行pipe理。 一个进程拥有的另一个概念是一个执行的线程,通常缩短为只是线程。 线程有一个程序计数器,用于跟踪下一个要执行的指令。 它有寄存器,它保存当前的工作variables。 它有一个包含执行历史的堆栈,每个被调用的过程有一个框架,但还没有返回。 虽然线程必须在某个进程中执行,但线程及其进程是不同的概念,可以分开处理。 过程用于将资源组合在一起; 线程是计划在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(线性,用户)地址空间。 Base = 0,Limit = 2GB。 他们会做一个,但x86不允许一个段是读/写和执行。 所以他们做了两个,设置CS指向代码描述符,其余的(DS,ES,SS等)指向另一个[2]。 但都指向相同的东西!
面试你的人做了一个隐藏的假设,他/她没有说,这是一个愚蠢的伎俩拉。
所以关于
问:那么告诉我哪个线程段共享?
这些细分与问题无关,至less在Windows上是这样的。 线程共享整个地址空间。 SS只有1个堆栈段,它指向DS,ES和CS所做的完全相同的东西[2]。 即整个血腥的用户空间 。 0-2GB。 当然,这并不意味着线程只有一个堆栈。 当然,每个都有自己的堆栈,但是x86分段并不用于这个目的。
也许*尼克斯做了不同的事情。 谁知道。 这个问题所依据的前提被打破了。
- 至less为用户空间。
- 从
ntsd notepad
:cs=001b ss=0023 ds=0023 es=0023
一般来说,线程称为轻量级进程。 如果我们将内存分成三部分,那么它将是:代码,数据和堆栈。 每个进程都有自己的代码,数据和堆栈部分,由于这个上下文切换时间有点高。 为了减less上下文切换时间,人们提出了线程的概念,它与其他线程/进程共享数据和代码段,并且有自己的STACK段。
一个进程有代码,数据,堆和堆栈段。 现在,线程OR线程的指令指针(IP)指向进程的代码段。 数据和堆段由所有线程共享。 那么堆栈区呢? 实际上堆栈区是什么? 它的进程创build的区域只是为了让它的线程使用…因为堆栈可以以比堆等更快的方式使用。进程的堆栈区域在线程之间分割,即如果有3个线程,则进程堆栈区分为3部分,每部分分配给3个线程。 换句话说,当我们说每个线程都有自己的堆栈时,该堆栈实际上是分配给每个线程的进程堆栈区域的一部分。 当线程完成执行时,线程的堆栈被进程回收。 实际上,不仅一个进程的堆栈在线程之间分配,而且一个线程使用的所有寄存器如SP,PC和状态寄存器都是进程的寄存器。 所以在共享方面,代码,数据和堆区是共享的,而堆栈区只是在线程之间分配的。
线程共享代码和数据段以及堆,但是它们不共享堆栈。
线程共享数据和代码,而进程不共享。 两个堆栈不共享。
进程也可以共享内存,更精确的代码,例如在Fork()
,但这是一个实现细节和(操作系统)优化。 由多个进程共享的代码(希望)会在第一次写入代码时被复制 – 这被称为写时复制(copy-on-write) 。 我不确定线程代码的确切语义,但我假设共享代码。
进程线程 堆叠私人私人 数据私有共享 代码私人1共享2
1代码在逻辑上是私有的,但由于性能原因可能会共享。 2我不是100%确定。
线程共享一切 [1]。 整个过程有一个地址空间。
每个线程都有自己的堆栈和寄存器,但是所有线程的堆栈都在共享地址空间中可见。
如果一个线程在其堆栈上分配了一些对象,并将该地址发送给另一个线程,那么他们将同等地访问该对象。
其实,我只是注意到一个更广泛的问题:我认为你混淆了字段的两个用途。
可执行文件(例如,ELF)的文件格式在其中具有不同的部分,其可以被称为段,包含编译代码(文本),初始化数据,链接符号,debugging信息等。没有堆或堆栈段在这里,因为这些只是运行时的结构。
这些二进制文件段可以分别映射到进程地址空间,具有不同的权限(例如,对于代码/文本的只读可执行文件,以及对于初始化数据的不可执行文件的复制)。
这个地址空间的区域被用于不同的目的,比如堆分配和线程堆栈,按照惯例(由你的语言运行时库强制执行)。 尽pipe这只是记忆,除非你在8086模式下运行,否则可能不会被分割。 每个线程的堆栈都是在线程创build时分配的一块内存,当前的堆栈顶端地址存储在一个堆栈指针寄存器中,每个线程都保留自己的堆栈指针以及其他寄存器。
[1]好的,我知道:信号掩码,TSS / TSD等。地址空间,包括其所有映射的程序段,仍然是共享的。
在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指向同一地区,但这只是为了简化事情。
一般来说,所有段寄存器在运行时可以有不同的值。 所以,面试问题是正确的:线程之间共享CODE,DATA和STACK中的哪一个。 堆pipe理是另一回事 – 它只是一个调用操作系统的序列。 但是如果你根本就没有操作系统,比如在embedded式系统中 – 你还能在你的代码中新增/删除吗?
我对年轻人的build议 – 阅读一些好的汇编编程书。 大学课程似乎在这方面很差。
线程共享堆(有一个关于线程特定堆的研究),但是当前的实现共享堆。 (当然还有代码)
看一下
StackExchange的答案类似的问题
引用CareerCup的顶级答案 :
共享:
- 代码部分
- 数据部分(静态+堆)
- 堆内存和内存地址
- 全局variables存在于堆中
- 内存pipe理信息(基址/重定位和限制寄存器,页表
- 进程状态(新build,就绪,运行,等待,暂停等)
- I / O状态信息(打开的文件描述符等)
- 信号
- 环境variables
- 虚拟地址空间
- 权限
- 其他资源(例如文件)
不共享:
- 堆栈(局部variables,临时variables,返回地址)
- 寄存器
- 线程特定的数据
- 线程ID
- 保存的寄存器,堆栈指针,指令指针
- 信号掩码
- 优先级(调度信息)
在面试中我遇到了一个MCQ:
线程的一个进程共享
(A)全局variables但不是堆。
(B)堆但不是全局variables。
(C)既不是全局variables也不是堆。
(D)堆和全局variables。 ✓
正确:数据部分存在堆和全局variables
当我研究,我发现它是从GateOverflow和也采取
find相当好的答案:
- 全局variables存储在堆和堆是共享的,所以他们都共享。
- 一个进程的线程共享一切(代码,数据,文件,堆),堆栈和寄存器除外。