假设我有像Firefox这样的浏览器进程,它有pid = 123. Firefox有5个打开的标签,每个标签都在一个单独的线程中运行,所以总共有5个线程。
所以我想深入了解内核如何将进程分离到要在struct task_struct
或thread_info中执行的线程。
类似struct task_struct
是任务列表的任务描述符。
struct task_struct
在哪里包含这五个主题的引用或链接。
像Firefox这样的进程的struct thread_struct
是否包含对所有5个线程的引用
或
每个线程都被视为Linux内核中的进程。
答案 0 :(得分:12)
与Windows不同,Linux没有"线程"的实现。在内核中。内核为我们提供了有时被称为"轻量级进程",它们是"进程"的概念的概括。和"线程",可以用来实现。
当您阅读内核代码并且一方面看到thread_struct
和另一方面pid
(进程ID)之类的内容时,可能会感到困惑。实际上,两者都是同一个。不要被术语搞糊涂。
每个轻量级进程都有完全不同的thread_info
和task_struct
(嵌入式thread_struct
)。您似乎认为一个轻量级进程的task_struct
应该指向其他(用户空间)"线程"的task_struct
s。在相同的(用户空间)"过程"。不是这种情况。在内核中,每个"线程"是一个单独的过程,调度程序分别处理每个过程。
Linux有一个名为clone
的系统调用,用于创建新的轻量级进程。当您调用clone
时,您必须提供各种标志,指示新进程与现有进程之间将共享的内容。他们可以共享他们的地址空间,或者他们每个人都可以拥有不同的地址空间。他们可以共享他们的打开文件,或者他们每个人都有自己的打开文件列表。他们可以共享信号处理程序,也可以各自拥有自己的信号处理程序。它们可以位于相同的"线程组",或者它们可以位于不同的线程组中。等等...
虽然"线程"和"进程"在Linux中是一样的,你可以实现我们通常认为的"进程"使用clone
创建不共享其地址空间,打开文件,信号处理程序等的流程。
你也可以实现我们通常认为的"线程"使用clone
创建 DO共享其地址空间,打开文件,信号处理程序等的流程。
如果你看一下task_struct
的定义,你会发现它有指向其他结构的指针,例如mm_struct
(地址空间),files_struct
(打开文件),{{ 1}}(信号处理程序),等等。当您sighand_struct
新的"进程"时,所有这些结构都将被复制。当您clone
新的"线程"时,这些结构将在新旧clone
之间共享 - 它们都指向相同的task_struct
,相同的mm_struct
,依此类推。无论哪种方式,您只是向files_struct
提供不同的标志,以告诉它要复制什么,以及分享什么。
我刚刚提到了#34;线程组"以上,所以你可能想知道这一点。简而言之,每个"线程"在一个"过程"有自己的PID,但它们都共享相同的TGID(线程组ID)。 TGID都等于第一个程序线程的PID。用户空间" PID",如clone
或ps
中显示的那些,实际上是" TGID"在内核中。当然,/proc
有一个标志来确定一个新的轻量级进程是否会有一个新的TGID(因此将它放入一个新的"线程组")或不。
UNIX进程也有#34;父母"和#34;孩子"。 Linux clone
中有指针实现了父子关系。而且,正如您可能已经猜到的那样,task_struct
有一个标志来确定新轻量级进程的父级将是什么。它可以是调用clone
的进程,也可以是调用clone
的进程的父。你能想出在创建"进程"时使用了哪个,以及在创建"线程"时使用了哪个?
查看clone
的联系人页面;这将是非常有教育意义的。另请尝试使用pthreads查看正在使用的clone
的程序strace
。
(其中很多都是从记忆中写出来的;其他人可以根据需要随意修改)