线程之间共享哪些资源?

Xin*_*nus 239 multithreading operating-system process

最近,我在面试中被问到一个问题,即流程和线程之间的区别.真的,我不知道答案.我想了一会儿,给了一个非常奇怪的答案.

线程共享相同的内存,而进程则没有.在回答这个问题之后,面试官给了我一个邪恶的微笑,并向我解释了以下问题:

:你知道程序划分的部分吗?

我的回答:是的(认为这是一个简单的)堆栈,数据,代码,堆

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

我无法回答这个问题并最终说出了所有这些.

请问,任何人都可以为流程和线程之间的差异提供正确和令人印象深刻的答案吗?

Gre*_*ill 159

你是非常正确的,但线程共享堆栈之外的所有段.线程有独立的调用堆栈,但是其他线程堆栈中的内存仍然可以访问,理论上你可以在一些其他线程的本地堆栈帧中保存一个指向内存的指针(尽管你可能应该找到一个放置内存的更好位置!).

  • 有趣的是,即使线程具有独立的调用堆栈,其他堆栈中的内存仍然可访问. (22认同)
  • @bph:访问另一个线程的堆栈内存是“可能的”,但是出于良好的软件工程实践的利益,我不会说这样做是“可接受的”。 (2认同)

Jor*_*oba 51

来自维基百科(我认为这对面试官来说是一个非常好的答案:P)

线程与传统的多任务操作系统进程的不同之处在于:

  • 进程通常是独立的,而线程作为进程的子集存在
  • 进程携带相当多的状态信息,而进程内的多个线程共享状态以及内存和其他资源
  • 进程具有单独的地址空间,而线程共享其地址空间
  • 进程只能通过系统提供的进程间通信机制进行交互.
  • 在同一进程中的线程之间的上下文切换通常比进程之间的上下文切换更快.

  • 关于上述第2点:对于线程,CPU也会维护上下文。 (2认同)

Rob*_*nes 44

真正需要指出的是,这个问题确实存在两个方面 - 理论方面和实施方面.

首先,让我们看一下理论方面.您需要了解流程在概念上是什么来理解流程和线程之间的区别以及它们之间共享的内容.

我们从第2.2.2节Tanenbaum的现代操作系统3e中的经典线程模型中得到以下结论:

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

他继续:

查看流程的一种方法是将相关资源组合在一起.进程具有包含程序文本和数据以及其他资源的地址空间.这些资源可能包括打开文件,子进程,待处理警报,信号处理程序,记帐信息等.通过以流程的形式将它们组合在一起,可以更容易地管理它们.进程的另一个概念是执行线程,通常缩写为只是线程.该线程有一个程序计数器,用于跟踪下一个要执行的指令.它有寄存器,它保存当前的工作变量.它有一个堆栈,其中包含执行历史记录,每个过程调用一个帧但尚未返回.虽然线程必须在某些进程中执行,线程及其过程是不同的概念,可以单独处理.流程用于将资源组合在一起; 线程是计划在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        |
Run Code Online (Sandbox Code Playgroud)

以上是线程工作所需的内容.正如其他人所指出的那样,诸如段之类的东西是依赖于OS的实现细节

  • 这是一个很好的解释.但它可能应该与某个被认为是"答案"的问题联系起来 (2认同)

Ale*_*ski 27

告诉采访者,这完全取决于操作系统的实施.

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

面试你的人做了一个隐藏的假设,他/她没有陈述,这是一个愚蠢的伎俩.

所以关于

问:那么告诉我哪个段线程共享?

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

也许*nix做了不同的事情.谁知道.这个问题基于的前提被打破了.


  1. 至少对于用户空间.
  2. 来自ntsd notepad:cs=001b ss=0023 ds=0023 es=0023

  • 啊,不!你是段与段混淆!部分是链接器如何将模块划分为部分(数据,rdata,text,bss等),如您所述.但我正在讨论intel/amd x86硬件中指定的段.与编译器/链接器完全无关.希望有道理. (4认同)

小智 19

通常,线程称为轻量级过程.如果我们将内存分为三个部分,那么它将是:代码,数据和堆栈.每个进程都有自己的代码,数据和堆栈部分,由于这个上下文,切换时间有点高.为了减少上下文切换时间,人们提出了线程的概念,它与其他线程/进程共享数据和代码段,并且它有自己的STACK段.


小智 17

进程具有代码,数据,堆和堆栈段.现在,线程OR线程的指令指针(IP)指向进程的代码段.所有线程共享数据和堆段.那么堆栈区呢?什么是堆栈区域?它是一个由进程创建的区域,仅供其线程使用...因为堆栈可以比堆等更快的方式使用.进程的堆栈区域在线程之间划分,即如果有3个线程,则该过程的堆栈区域分为3个部分,每个部分分配给3个线程.换句话说,当我们说每个线程都有自己的堆栈时,该堆栈实际上是分配给每个线程的进程堆栈区域的一部分.当一个线程完成其执行时,该进程将回收该线程的堆栈.实际上,不仅进程的堆栈在线程之间划分,而且线程使用的所有寄存器集合如SP,PC和状态寄存器都是进程的寄存器.因此,在共享时,代码,数据和堆区域是共享的,而堆栈区域只是在线程之间划分.


Kev*_*son 12

线程共享代码和数据段以及堆,但它们不共享堆栈.

  • "能够访问堆栈中的数据"和共享堆栈之间存在差异.这些线程有自己的堆栈,当它们调用方法时会被推送和弹出. (9认同)
  • 他们都是同样有效的观点.是的,每个线程都有自己的堆栈,因为线程和堆栈之间存在一对一的对应关系,每个线程都有一个空间用于自己的正常堆栈使用.但它们也是完全共享的进程资源,如果需要,任何线程都可以像访问其自身一样轻松访问任何其他线程的堆栈. (2认同)
  • @nextTide 没有两部分。堆栈是共享的,期间。每个线程都有自己的堆栈,但它们也是共享的。也许一个很好的比喻是,如果您和您的妻子各有一辆汽车,但您可以随时使用彼此的汽车。 (2认同)

Dan*_*ner 5

线程共享数据和代码,而进程则不共享.两者都不共享堆栈.

进程还可以共享内存,更准确地说是代码,例如在a之后Fork(),但这是一个实现细节和(操作系统)优化.多个进程共享的代码(希望)在第一次写入代码时会重复 - 这称为写时复制.我不确定线程​​代码的确切语义,但我假设共享代码.

           Process   Thread

   Stack   private   private
   Data    private   shared
   Code    private1  shared2

1代码在逻辑上是私有的,但出于性能原因可能会共享. 2我不是百分百肯定.


snr*_*snr 5

\n

除了全局内存之外,线程还共享许多其他属性(即,这些属性对于进程来说是全局的,而不是特定于线程的)。这些属性包括以下内容:

\n\n
    \n
  • 进程ID和父进程ID;
  • \n
  • 进程组ID和会话ID;
  • \n
  • 控制终端;
  • \n
  • 进程凭证(用户和组 ID);
  • \n
  • 打开文件描述符;
  • \n
  • 记录使用创建的锁fcntl();
  • \n
  • 信号处置;
  • \n
  • 文件系统\xe2\x80\x93相关信息:umask、当前工作目录、根目录;
  • \n
  • 间隔定时器 ( setitimer()) 和 POSIX 定时器 ( timer_create());
  • \n
  • System V 信号量 undo ( semadj) 值(第 47.8 节);
  • \n
  • 资源限制;
  • \n
  • 消耗的 CPU 时间(由 返回times());
  • \n
  • 消耗的资源(由 返回getrusage());和
  • \n
  • setpriority()不错的值(由和设置nice())。
  • \n
\n\n

每个线程的独特属性如下:

\n\n
    \n
  • 线程 ID(第 29.5 节);
  • \n
  • 信号掩码;
  • \n
  • 线程特定数据(第 31.3 节);
  • \n
  • 交替信号堆栈 ( sigaltstack());
  • \n
  • 错误号变量;
  • \n
  • 浮点环境(参见fenv(3));
  • \n
  • 实时调度策略和优先级(第 35.2 和 35.3 节);
  • \n
  • CPU 亲和性(特定于 Linux,在第 35.4 节中描述);
  • \n
  • 功能(Linux 特定的,在第 39 章中描述);和
  • \n
  • 堆栈(局部变量和函数调用链接信息)。
  • \n
\n
\n\n

摘自:《Linux 编程接口:Linux 和 UNIX 系统编程手册》,Michael Kerrisk,第 619 页

\n