假设我有一个像 Firefox 这样的浏览器进程,它的 pid = 123。Firefox 有 5 个打开的选项卡,每个选项卡都在一个单独的线程中运行,所以它总共有 5 个线程。
struct task_struct
或在 thread_info 中。 struct task_struct
是任务列表的任务描述符。struct task_struct
在哪里?包含对这五个线程的引用或链接。 struct thread_struct
像 Firefox 这样的进程包含对所有 5 个线程的引用或
每个线程都被视为 Linux 内核中的一个进程。
最佳答案
与 Windows 不同,Linux 在内核中没有“线程”的实现。内核为我们提供了有时称为“轻量级进程”的东西,它是“进程”和“线程”概念的概括,可用于实现其中任何一个。
当您阅读内核代码并看到诸如 thread_struct
之类的内容时,可能会感到困惑。一方面,pid
(进程 ID)另一方面。实际上,两者都是一回事。不要被术语混淆。
每个轻量级进程都有完全不同的thread_info
和 task_struct
(嵌入 thread_struct
)。您似乎认为 task_struct
一个轻量级进程的指针应该指向 task_struct
同一(用户空间)“进程”中的其他(用户空间)“线程”。不是这种情况。在内核内部,每个“线程”都是一个单独的进程,调度程序分别处理每个线程。
Linux 有一个名为 clone
的系统调用。它用于创建新的轻量级流程。当您调用 clone
,您必须提供各种标志来指示新进程和现有进程之间将共享什么。他们可以共享他们的地址空间,也可以各自拥有不同的地址空间。他们可以共享打开的文件,也可以各自拥有自己的打开文件列表。他们可以共享自己的信号处理程序,也可以各自拥有自己的信号处理程序。它们可以在同一个“线程组”中,也可以在不同的线程组中。等等...
尽管“线程”和“进程”在 Linux 中是同一个东西,但是您可以使用 clone
来实现我们通常认为的“进程”。创建 的进程不要分享它们的地址空间、打开的文件、信号处理程序等。
您还可以使用 clone
来实现我们通常认为的“线程”。创建 的进程分享它们的地址空间、打开的文件、信号处理程序等。
如果您查看 task_struct
的定义,你会发现它有指向其他结构的指针,例如 mm_struct
(地址空间),files_struct
(打开文件),sighand_struct
(信号处理程序)等等。当你clone
一个新的“进程”,所有这些结构都将被复制。当你clone
一个新的“线程”,这些结构将在新旧 task_struct
之间共享s——它们都指向同一个mm_struct
, 相同 files_struct
, 等等。无论哪种方式,您只是为 clone
提供不同的标志。告诉它要复制什么,要分享什么。
我刚刚在上面提到了“线程组”,所以您可能对此感到疑惑。简而言之,“进程”中的每个“线程”都有自己的 PID,但它们都共享相同的 TGID(线程组 ID)。 TGID 都等于第一个程序线程的 PID。用户空间“PID”,如 ps
中所示,或在 /proc
,实际上是内核中的“TGID”。当然,clone
有一个标志来确定一个新的轻量级进程是否会有一个新的 TGID(因此将它放在一个新的“线程组”中)。
UNIX 进程也有“父”和“子”。 Linux中有指针task_struct
它实现了父子关系。而且,正如您可能已经猜到的那样,clone
有一个标志来确定一个新的轻量级进程的父进程将是什么。它可以是调用 clone
的进程。 , 或调用 clone
的进程的父进程.你能弄清楚创建“进程”时使用哪个,创建“线程”时使用哪个?
查看 clone
的联机帮助页;这将是非常有教育意义的。也可以试试 strace
在使用 pthread 的程序上查看 clone
正在使用。
(很多都是凭内存写的;其他人可以根据需要随时进行更正)
关于linux - 内核如何将线程与进程分离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30839834/