那么我想深入了解内核是如何将进程分离成线程以在
struct task_struct
或thread_info
中执行。像
struct task_struct
是任务列表中的任务描述符。struct task_struct
在哪里包含对这五个线程的引用或链接。像 Firefox 这样的进程的
struct thread_struct
是否包含对所有 5 个线程的引用还是
每个线程都像 Linux 内核中的一个进程一样处理。
那么我想深入了解内核是如何将进程分离成线程以在 struct task_struct
或 thread_info
中执行。
像 struct task_struct
是任务列表中的任务描述符。
struct task_struct
在哪里包含对这五个线程的引用或链接。
像 Firefox 这样的进程的 struct thread_struct
是否包含对所有 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
之间共享——它们都指向同一mm_struct
、同一files_struct
等等。无论哪种方式,你只需向clone
提供不同的标志,告诉它该复制什么,该共享什么。ps
或/proc
中显示的那些,实际上是内核中的“TGIDs”。自然地,clone
有一个标志来确定一个新的轻量级进程是否有一个新的TGID(从而将其放入一个新的“线程组”)。task_struct
中实现了父子关系的指针。正如你可能已经猜到的,clone
有一个标志来确定一个新的轻量级进程的父进程是什么。它可以是调用clone
的进程,也可以是调用clone
的进程的父进程。你能想出在创建“进程”时使用哪个,以及在创建“线程”的时候使用哪个吗?查看clone
的手册,这会非常有益。还可以尝试对使用pthread的程序运行strace
,以便查看clone
的使用情况。
(很多内容都是凭记忆写的;其他人可以随意进行编辑以进行必要的更正)
task_struct
包含关于进程的与体系结构无关的信息。thread_info
包含与体系结构相关的信息和内核堆栈。task_struct
拥有一个指向thread_info
的指针,同时从thread_info
也可以访问到task_struct
。在 Linux 中,进程和线程都基本上是task_structs
的变种。 - bolov