代码之家  ›  专栏  ›  技术社区  ›  Xinus

线程之间共享哪些资源?

  •  218
  • Xinus  · 技术社区  · 15 年前

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

    线程共享相同的内存,进程不共享。回答完后,面试官恶狠狠地笑了我一笑,向我提出了以下问题:

    Q. 你知道程序分成哪些部分吗?

    我的回答是: 是的(认为很简单)堆栈、数据、代码、堆

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

    我不能回答这个问题,结果说了所有的话。

    拜托,有人能就过程和线程之间的区别给出正确而令人印象深刻的答案吗?

    13 回复  |  直到 6 年前
        1
  •  146
  •   Greg Hewgill    15 年前

    你说得很对,但线程共享所有段 除了 堆栈。线程有独立的调用堆栈,但是其他线程堆栈中的内存仍然可以访问,从理论上讲,您可以在其他线程的本地堆栈帧中保存指向内存的指针(尽管您可能应该找到更好的位置来放置内存!).

        2
  •  48
  •   beta Rob    13 年前

    Wikipedia (我认为这会给面试官一个很好的回答:p)

    螺纹与传统螺纹不同 多任务操作系统 其中的过程:

    • 进程通常是独立的,而线程作为 过程
    • 进程携带大量的状态信息,而多线程 在进程共享状态中 作为内存和其他资源
    • 进程有单独的地址空间,而线程共享它们 地址空间
    • 进程仅通过系统提供的进程间交互 沟通机制。
    • 同一进程中线程之间的上下文切换通常更快 比上下文切换 过程。
        3
  •  38
  •   Robert S. Barnes Antoni    11 年前

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

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

    我们有以下部分 2.2.2经典螺纹模型 在里面 Modern Operating Systems 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        |
    

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

        4
  •  26
  •   Alex Budovski    15 年前

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

    以Windows x86为例。只有 段[1],代码和数据。它们都映射到整个2GB(线性,用户)地址空间。基极=0,极限=2GB。他们本来可以做一个,但x86不允许读/写和执行一个段。所以他们做了两个,并将cs设置为指向代码描述符,而其余的(ds、es、ss等)则指向另一个[2]。但两者都指向相同的东西!

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

    所以关于

    问:那么告诉我哪段线 分享?

    这些片段与问题无关,至少在Windows上是这样。线程共享整个地址空间。只有一个堆栈段ss,它指向与ds、es和cs所做的完全相同的东西[2]。即。 整个该死的用户空间 .0-2GB。当然,这并不意味着线程只有一个堆栈。当然,每个都有自己的堆栈,但x86段不用于此目的。

    也许尼克斯做了一些不同的事情。谁知道呢。这个问题所基于的前提被打破了。


    1. 至少对于用户空间。
    2. ntsd notepad 以下内容: cs=001b ss=0023 ds=0023 es=0023
        5
  •  17
  •   eeerahul Arrj    13 年前

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

        6
  •  16
  •   Community CDub    12 年前

    进程具有代码、数据、堆和堆栈段。现在,一个或多个线程的指令指针(IP)指向进程的代码段。数据段和堆段由所有线程共享。那么栈区呢?什么是堆叠区域?它是由进程创建的一个区域,只供线程使用…因为堆栈的使用速度比堆等快得多。进程的堆栈区域被划分为多个线程,即如果有3个线程,则将进程的堆栈区域划分为3个部分,并将每个部分分配给3个线程。换句话说,当我们说每个线程都有自己的堆栈时,这个堆栈实际上是分配给每个线程的进程堆栈区域的一部分。当一个线程完成其执行时,该线程的堆栈将由进程回收。事实上,不仅一个进程的堆栈被划分为多个线程,而且一个线程使用的所有寄存器集(如sp、pc和state寄存器)都是该进程的寄存器。 因此,在共享方面,代码、数据和堆区域是共享的,而堆栈区域只是在线程之间划分的。

        7
  •  12
  •   Kevin Peterson    15 年前

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

        8
  •  5
  •   Daniel Brückner    15 年前

    线程共享数据和代码,而进程不共享。堆栈不能为两者共享。

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

               Process   Thread
    
       Stack   private   private
       Data    private   shared
       Code    private1  shared2
    

    代码是 逻辑上 私有,但出于性能原因可能共享。 我不是百分之百确定。

        9
  •  4
  •   Useless    15 年前

    线程共享 一切 〔1〕。整个过程有一个地址空间。

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

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


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

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

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

    此地址空间的区域按约定(由语言运行库强制)用于不同的目的,如堆分配和线程堆栈。不过,这些都只是内存,除非在虚拟8086模式下运行,否则可能不会被分段。每个线程的堆栈是在线程创建时分配的一块内存,当前堆栈顶部地址存储在堆栈指针寄存器中,每个线程都将自己的堆栈指针与其其他寄存器一起保存。


    [1]好的,我知道:信号屏蔽、TSS/TSD等。但是地址空间,包括其所有映射的程序段,仍然是共享的。

        10
  •  2
  •   George    14 年前

    在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通常指向同一个区域,但这只是为了简化事情。

    一般来说,所有段寄存器在运行时都可以有不同的值。 因此,访谈问题是正确的:线程之间共享的是代码、数据和堆栈中的哪一个。堆管理是另外一回事——它只是对操作系统的一系列调用。但是,如果你根本没有操作系统,比如在嵌入式系统中,你的代码中还能有新的/删除的操作系统呢?

    我给年轻人的建议-读一些好的汇编编程书。在这方面,大学的课程似乎很差。

        11
  •  1
  •   Dani    15 年前

    线程共享堆(有关于线程特定堆的研究),但当前的实现共享堆。(当然还有代码)

        12
  •  0
  •   roshni    7 年前

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

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

        13
  •  0
  •   Soner from The Ottoman Empire    6 年前

    除了全局内存之外,线程还共享许多其他属性 (即,这些属性是流程的全局属性,而不是特定属性 对一个线程)这些属性包括:

    • 进程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页