Linux 内核:线程与进程 - task_struct 与 thread_info
Posted
技术标签:
【中文标题】Linux 内核:线程与进程 - task_struct 与 thread_info【英文标题】:Linux Kernel: Threading vs Process - task_struct vs thread_info 【发布时间】:2014-02-17 02:11:20 【问题描述】:我读到 Linux 不支持线程或轻量级进程的概念,并且它认为内核线程就像任何其他进程一样。然而,这个原则在代码中并没有很准确地反映。我们看到 task_struct 保存了进程的状态信息(如果错误,请纠正我)以及附加到进程内核堆栈底部的 thread_info。
现在的问题是,当 linux 应该像任何其他进程一样解释线程时,为什么代码支持通过 thread_info 单独线程的概念?
请让我知道我在这里缺少什么 - 我是 linux 内核开发的新手。
【问题讨论】:
【参考方案1】:Linux 中的线程被视为恰好共享某些资源的进程。每个线程都有自己的thread_info
(就像你说的那样在堆栈的底部)和自己的task_struct
。我可以想到将它们作为单独结构维护的两个原因。
thread_info
依赖于架构。 task_struct
是通用的。
thread_info
会减少该进程的内核堆栈大小,因此应该保持较小。 thread_info
被放置在堆栈的底部,作为一种微优化,它可以通过向下舍入堆栈大小来从当前堆栈指针计算其地址,从而节省 CPU 寄存器。
【讨论】:
【参考方案2】:较旧的方法:在较旧的内核中,在 2.6 之前,进程描述符是静态分配的,因此可以从该结构的特定偏移处读取值。
新方法: 但在 2.6 及更高版本中,您可以使用平板分配器动态分配进程描述符。因此,旧方法不再有效。因此引入了Thread_info
。
在《Linux Kernel Development, Chapter 3》一书中明确提到:
task_struct 结构通过slab分配器分配给 提供对象重用和缓存着色(参见第 11 章,“内存 管理”)。在 2.6 内核系列之前,struct task_struct 是 存储在每个进程的内核堆栈的末尾。这允许 具有很少寄存器的体系结构,例如 x86,来计算 通过堆栈指针定位进程描述符而不使用 一个额外的寄存器来存储位置。使用进程描述符 现在通过slab分配器动态创建,一个新的结构, struct thread_info 被创建,它再次位于底部 堆栈(对于向下增长的堆栈)和堆栈顶部(对于 长大的堆栈)[4]。请参见图 3.2。新的结构也使 计算其值的偏移量以用于装配相当容易 代码。
【讨论】:
【参考方案3】:正如彼得所说,thread_info 是特定于架构的,其中包含必要的信息,例如寄存器、pc、fp 等。
在上下文切换期间保存/恢复流程执行需要此信息。
http://lxr.free-electrons.com/source/arch/arm/include/asm/thread_info.h#L33
task_struct --> thread_info --> struct cpu_context_save cpu_context
【讨论】:
【参考方案4】:task_struct
是一个大型数据结构。因此,存储大型结构的任务非常困难。所以内核引入了thread_info
的概念,它比task_struct
要苗条很多,只是指向task_struct
结构。
Source
【讨论】:
【参考方案5】:Linux 支持线程。
线程与进程类似,但每个进程都有自己的地址空间(堆栈、堆、程序代码),线程共享地址空间,因此每个进程都可以访问堆数据。由于地址空间是共享的,因此在上下文切换期间我们不需要切换我们使用哪个页表来将虚拟内存转换为物理内存。由于在多线程应用程序中两个线程访问相同的地址空间,因此您将有多个堆栈区域,每个线程一个。
是的,进程的状态作为状态存储在 task_struct 中。您可以通过转到 sched.h 文件来探索 task_struct 中存储的内容。
线程和进程非常相似,但有时您希望共享数据,即当您对大型数据集进行操作时(添加两个大型数组等)。
【讨论】:
以上是关于Linux 内核:线程与进程 - task_struct 与 thread_info的主要内容,如果未能解决你的问题,请参考以下文章
Linux内核线程kernel thread详解--Linux进程的管理与调度
Linux内核线程kernel thread详解--Linux进程的管理与调度
Linux下的进程类别(内核线程轻量级进程和用户进程)以及其创建方式--Linux进程的管理与调度