2 分钟阅读

Linux 的进程与线程

在 Linux 中,fork() 用来创建新进程。这些新进程称为子进程,每个子进程一开始都会共享父进程的所有段,比如 text、stack、heap 等,直到它试图修改 stack 或 heap 为止。一旦发生修改,内核就会为子进程复制一份独立的 stack 和 heap 段,让这些改动只对子进程可见。text 段是只读的,因此父进程和子进程共享同一个 text 段。

Linux 线程与轻量级进程

Linux 中的线程无非就是进程的一条执行流。包含多条执行流的进程称为多线程进程。

非多线程的进程只有一条执行流,也就是主执行流,因此称为单线程进程。在 Linux 内核眼里并不存在线程这个概念,每个线程都是一个独立的进程,只不过和其他普通进程有些不同。

线程常常和轻量级进程(Light Weight Processes,简称 LWP)这个术语混在一起。这要追溯到 Linux 只在用户级支持线程的那段时期。那时即便是多线程应用,在内核看来也只是单个进程。这给管理用户级线程的库带来了很大挑战,因为它必须保证:当某个线程发出阻塞调用时,其他线程不会因此受阻。

后来实现方式变了,每个线程都关联到一个进程,好让内核能够管理。但正如前面所说,Linux 内核并不把它们当作线程,而是在内部都视为进程。这些进程就叫轻量级进程。

轻量级进程(LWP)和普通进程的主要区别在于,LWP 共享同一个地址空间以及打开的文件等资源。正因为共享了部分资源,相比其他普通进程它们更”轻”,轻量级进程的名字也由此而来。

所以,实际上我们可以说,线程和轻量级进程是同一回事。只不过线程是在用户级使用的术语,而轻量级进程是在内核级使用的术语。

从实现的角度看,线程是通过 Linux 中符合 POSIX 标准的 pthread 库所暴露的函数来创建的。而在底层,无论创建普通进程还是轻量级进程,都要经由 clone() 函数完成。也就是说,创建普通进程时用的 fork() 会带着适当的参数去调用 clone();创建线程或 LWP 时,pthread 库中的某个函数则会带着相关的标志去调用 clone()。所以两者的主要差别,来自传给 clone() 函数(准确地说是一个系统调用)的不同标志。

想了解更多关于 fork()clone() 的内容,请查阅它们各自的 man page。