进程与线程的区别
老八股版本
- 一个线程属于一个进程,一个进程包含多个线程
- 进程是资源分配的基本单位
- 有独立的地址空间,拥有CPU资源、内存资源、文件资源等
- 线程是CPU调度的基本单位
- 私有(线程上下文):线程栈(但能通过指针被另一个线程修改),寄存器,程序计数器
- 共享:进程地址空间中除线程上下文之外的所有内容:堆、全局变量、静态变量、代码段、文件等公共资源
- 数据共享:进程有独立的地址空间,数据隔离,共享复杂,需要IPC;线程共享进程数据
- 上下文切换:进程切换需要刷新TLB(对于不同进程,相同虚拟地址会映射不同物理地址产生歧义)并获取新的地址空间、然后切换硬件上下文和内核栈;线程切换时只需要切换硬件上下文和内核栈
- 可靠性:多进程更可靠,一个进程崩溃后,在保护模式下不会对其他进程产生影响;但线程之间没有隔离性,一个线程崩溃时可能已经破坏了其他线程的内存,导致整个进程kill
- 通信方式:线程共享进程数据,线程通信主要用于线程同步,所以没有IPC数据交换的机制
内核中对线程的表示
在 Linux 中,无论进程还是线程,都是抽象成了 task 任务,在源码里都是用 task_struct 结构来实现的。

对于线程来讲,所有的字段都是和进程一样的(本来就是一个结构体)。包括状态、pid、task 树关系、地址空间、文件系统信息、打开的文件信息等等字段,线程也都有。
在 Linux 中,每一个 task_struct 都需要被唯一的标识,它的 pid 就是唯一标识号,即每个进程/线程的pid都不同。对于线程,还需通过 tgid 字段来表示其所归属的进程 ID。
线程创建过程

可见两者创建过程差不多,一样使用的是内核里的 do_fork 函数,最后走到 copy_process 来完整创建。copy_process 先是复制了一个新的 task_struct 出来,然后对 task_struct 中的各种核心对象进行拷贝处理,还申请了 pid 。
区别在于二者在调用 do_fork 时传入的 clone_flags 里的标记不一样:
- 创建进程时的 flag:仅有一个 SIGCHLD
- 创建线程时的 flag:包括 CLONE_VM、CLONE_FS、CLONE_FILES、CLONE_SIGNAL、CLONE_SETTLS、CLONE_PARENT_SETTID、CLONE_CHILD_CLEARTID、CLONE_SYSVSEM。
对于线程来讲,由于传入了flags,其地址空间 mm_struct、目录信息 fs_struct、打开文件列表 files_struct 都是和创建它的任务共享的,无需创建。

对于进程来讲,没传 flags,地址空间 mm_struct、挂载点 fs_struct、打开文件列表 files_struct 都要是独立拥有的,都需要去申请内存并初始化它们。

总结
因为在内核中线程和进程都是用 task_struct 来表示,只不过线程和进程的区别是会和创建它的父进程共享打开文件列表、目录信息、虚拟地址空间等数据结构,会更轻量一些,所以在 Linux 下的线程也叫轻量级进程。
对于内核任务来说,无论有多少个任务,其使用地址空间都是同一个。所以一般都叫内核线程,而不是内核进程。
注意
fork的父子进程间:
共享:内存映射区(mmap)、文件描述符(文件偏移量共享)、信号处理方式
不共享:进程ID、内存空间(写时复制)
CPU核心、线程数概念
CPU物理数
1 | cat /proc/cpuinfo | grep 'physical id' | sort | uniq | wc -l |
物理CPU数量,普通电脑一般只有一个CPU插槽,也就是只有一个物理CPU
CORE核心数(物理核心数)
1 | cat /proc/cpuinfo | grep 'core id' | sort | uniq | wc -l |
一个物理CPU拥有的运算单元个数,每个运算单元有一套寄存器、L1、L2私有缓存。一个核心同一时刻只能运行一个线程,不论这个线程是哪个进程的。每隔一定时间就会切换线程,如果切换到其它进程的线程,会切换CR3(进程切换时,会将该进程页表的指针加载到CR3寄存器)。
THREAD线程数(逻辑核心数)
1 | cat /proc/cpuinfo | grep 'processor id' | sort | uniq | wc -l |
英特尔的超线程技术,可以实现一个核心同时执行多个线程,即逻辑核心数的说法:8核16线程,一个核心能跑二个线程,
CPU在执行一条机器指令时,并不会完全地利用所有的CPU资源,是有大量资源被闲置着的。超线程技术允许两个线程同时不冲突地使用CPU中的资源。比如一条整数运算指令只会用到整数运算单元,此时浮点运算单元就空闲了,若使用了超线程技术,且另一个线程刚好此时要执行一个浮点运算指令,CPU就允许属于两个不同线程的整数运算指令和浮点运算指令同时执行,这是真的并行。
超线程”可能”是并行的,也可能不是并行的,但这也并不意味着两个线程在同一个CPU(也就是同一个核心)中一直都可以并行执行,只是恰好碰到两个线程当前要执行的指令不使用相同的CPU资源时才可以真正地并行执行。
进程的多个线程能多核并行吗
内核态线程
- 优点:一个进程的多个线程可以分配到多个CORE核心同时执行,并且由于内核级线程只有很小的数据结构和堆栈,切换速度快,本身也可以用多线程技术实现提高系统的运行速率。
- 缺点:线程在用户态的运行,而线程的调度和管理在内核实现,在控制权从一个线程传送到另一个线程需要用户态到内核态再到用户态的模式切换,比较占用系统资源。
用户态线程
- 优点:线程的调度不需要内核直接参与,控制简单,可以在不支持线程的操作系统中实现
- 缺点:没有办法使用多核心并行执行