线程的实现是如何在系统中完成的?我知道子进程是使用 fork() 调用创建的,并且线程是轻量级的。线程的创建与子进程的创建有何不同?
4 回答
线程是使用系统调用创建的,该clone()
系统调用可以创建一个与其父进程共享内存空间和一些内核控制结构的新进程。这些进程称为 LWP(轻量级进程),也称为内核级线程。
fork()
创建一个新进程,该进程最初与其父进程共享内存,但页面是写时复制的,这意味着当原始页面的内容发生更改时会创建单独的内存页面。因此,父进程和子进程都不能再更改彼此的内存,并且它们有效地作为单独的进程运行。此外,新分叉的子进程是一个成熟的进程,具有独立的内核控制结构。
每个进程都有自己的地址空间,也就是进程可以访问的虚拟地址范围。当一个新进程被分叉时,必须制作所有相关资源的副本。分叉完成后,子节点和父节点拥有各自不同的地址空间以及其中涉及的所有资源。当然,这是一个性能密集型操作。
虽然同一进程中的所有线程共享相同的地址空间,但当产生一个新线程时,每个线程只需要自己的堆栈,并且不会像进程那样重复所有资源。因此产生一个线程的性能要低得多密集的。
当然,这两种操作不能也不应该进行比较,因为它们都针对不同的需求提供了本质上不同的特性。
好吧,您已经阅读了重要部分,现在这里是幕后的东西:
在当前的实现中(当前意味着过去几十年),进程内存在技术上不会在分叉后立即复制。只读部分只是在两个进程之间共享(因为它们无论如何都不能更改),当然还有共享库的只读部分。但最重要的是,所有可写的东西最初也只是共享的。然而,它是以写保护的方式共享的,一旦你写入子进程内存(例如通过增加一个变量),内核中就会产生一个页面错误,这只会导致内核实际复制相应的页面(然后发生修改)。
这种称为“写入时复制”的出色优化导致子进程通常不会真正消耗与其父进程一样多的(物理)内存。然而,对于程序开发者(和用户)来说,它是完全透明的。
那么它有很大的不同,首先,子进程在某种程度上是父程序的副本,并且所有变量都重复,并且您通过其 PID 将子进程与父进程区分开来。线程就像新程序,它们与主程序同时运行(看起来是同时运行的,由于 os 对 cpu 时间的切片)。线程可以在程序中使用全局变量,但它们不会像进程一样重复。因此使用线程比使用新进程要便宜得多。