我在很多地方读到 Linux 为 Java VM 中的每个用户线程创建一个内核线程。 (我看到术语“内核线程”有两种不同的使用方式:
- 为执行核心操作系统工作而创建的线程以及
- 操作系统识别并安排执行用户工作的线程。
我说的是后一种类型。)
由于 Linux 进程支持父进程和子进程之间的共享内存空间,内核线程与内核进程是否相同,或者它确实是不同的实体?
答案1
文档可能非常混乱,所以这里是“真实的“Linux 型号:
- 在 Linux 内核中,可以运行(和调度)的东西称为“进程”,
- 每个进程都有一个系统唯一的进程ID(PID)和一个线程组ID(TGID),
- “正常”进程的 PID=TGID 并且没有其他进程共享此 TGID 值,
- “线程”进程是指其 TGID 值与其他进程共享的进程,
- 共享相同 TGID 的多个进程至少也共享相同的内存空间和信号处理程序(有时更多),
- 如果“线程”进程的 PID=TGID,则可以称为“主线程”,
- 从任何进程调用
getpid()
都会返回其 TGID(=“主线程”PID), - 从任何进程调用
gettid()
都会返回其 PID (!), - 任何类型的进程都可以通过系统调用创建
clone(2)
, - 进程之间共享的内容是通过将特定标志传递给来决定的
clone(2)
, - 您可以列出文件夹的数字名称,
ls /proc
就像/proc/NUMBER
TGID 一样, - 文件夹的数字名称与
/proc/TGID/task
PID/proc/TGID/task/NUMBER
一样, - 即使您没有看到每个现有的 PID 都带有
ls /proc
,您仍然可以这样做cd /proc/any_PID
。
结论:从内核的角度来看,只有进程存在,每个进程都有自己唯一的PID,所谓的线程只是一种不同类型的进程(至少与一个或多个其他进程共享相同的内存空间和信号处理程序) ·s)。
笔记:Linux 中“线程”概念的实现导致了词汇混乱,如果getpid()
它没有按照你的想法做,那是因为它的行为遵循 POSIX 兼容性(线程应该共享一个公共 PID)。
答案2
Linux 上的线程和进程完全没有区别。如果你看克隆(2)您将看到一组标志,用于确定线程之间共享的内容和不共享的内容。
经典进程只是不共享任何内容的线程;你可以在Linux下分享你想要的组件。
其他操作系统实现的情况并非如此,它们存在更实质性的差异。
答案3
线程是Linux下的进程。它们是通过clone
系统调用创建的,系统调用返回一个进程 ID,可以通过kill
系统调用向该 ID 发送信号,就像进程一样。线程进程在ps
输出中可见。该clone
调用会传递一些标志,这些标志确定父进程的环境有多少与线程进程共享。
答案4
前面的答案非常好,指出线程是 Linux 内核内部的进程,并且您无论如何都可以克隆()您喜欢的进程状态的任何子集。
但我认为记住这一点是有帮助的:有多少上下文可以共享或必须唯一保存,以及上下文切换可能需要多少个周期,这可能取决于可能有多少不同,而不仅仅是不同程度就操作系统而言,还涉及硬件,例如TLB。因此,克隆什么和共享什么很重要。
在应用层面,一个新线程(按照传统的理解,共享内存映像、当前目录、打开的文件句柄等)总是比一个最多只在最初共享这些的新进程便宜。即使进程是通过写时复制来分叉的,一旦写入,您就必须进行复制。这就是为什么在设计应用程序时,创建 10,000 个线程比创建 10,000 个进程更合理。出于安全原因执行新进程的原因是运行不同的可执行文件或防火墙。