Program vs Process: A program is a passive entity, such as a file containing a list of instructions stored on disk, whereas a process is an active entity, with a program counter specifying the next instruction to execute and a set of associated resources.
可执行程序和进程的区别
进程实体 Process Entity包括:
当出现了引起进程挂起的事件时,用户请求将自己挂起,或者父进程请求挂起自己的子进程,这时使用挂起原语Suspend()
当发生激活事件后,系统利用激活原语Active()
将指定进程激活。激活原语将进程从外存调入内存,然后检查进程状态
挂起状态又称为静止状态,一个就绪进程被挂起,变为静止就绪;阻塞状态进程被挂起,称为静止阻塞。处于静止状态的进程保存在磁盘(外存)上,它只有被对换到内存才能被调度执行。
Causes of Suspension
Blocked VS Suspend
阻塞:正在执行的进程由于发生 I/O 请求、申请缓冲区失败等事件暂时无法继续执行。此时引起进程调度,OS 把处理机分配给另一个就绪进程,而让受阻进程处于暂停状态,一般将这种状态称为阻塞状态。
挂起:由于系统和用户的需要引入了挂起的操作,进程被挂起意味着该进程处于静止状态。如果进程正在执行,它将暂停执行,若原本处于就绪状态,则该进程此时暂不接受调度。
对比维度 | 阻塞(Blocked) | 挂起(Suspended) |
---|---|---|
CPU 占用 | 进程暂停执行,不占用 CPU | 进程暂停执行,不占用 CPU |
发生时机 | 等待资源时自动发生(如等待 I/O、信号量) | 由用户或操作系统出于管理目的主动触发 |
恢复时机 | 等待的资源准备好后,自动进入就绪状态 | 被挂起方(如用户或系统)决定何时恢复 |
存在位置 | 始终在内存中 | 挂起后进程的内存被释放,保存于外存 |
常见场景 | I/O 等待、互斥锁等待 | 用户暂停调试、系统内存压力高,暂时换出不活跃进程 |
调度可能性 | 一旦资源满足,即可转为就绪状态参与调度 | 必须先恢复为就绪状态后,才可参与调度 |
也被称为轻量级进程,更加轻量。多个线程可以在同一个进程中同时执行,并且共享进程的资源比如内存空间、文件句柄、网络连接等。
TCB Components
Tid: A unique thread identifier in the process.
同进程一样,每个线程也有一个线程 ID;进程 ID 在整个系统中是唯一的,线程 ID 只在它所属的进程环境中唯一
Register set: These are small storage areas that quickly hold and release data. They store intermediate values during execution.Including Program counter which keeps track of the execution of the thread, indicating the address of the next instruction to be executed.
Stack: It is a data structure that stores temporary data like function parameters, return addresses, and local variables.
同进程一样,线程之间也存在共享资源和相互合作的制约关系,致使线程在运行时也具有间断性。
线程运行时有以下 3 种状态:
线程具有许多传统进程所具有的特征,所以又称为轻型进程(Light-Weight Process) ,相应地把传统进程称为重型进程(Heavy-Weight Process),传统进程相当于只有一个线程的任务。
在引入了线程的操作系统中,通常一个进程都拥有若干个线程,至少也有一个线程。
下面从调度性、并发性、系统开销和拥有资源等方面对线程和进程进行比较。
Linux Process/Thread Control Operations Comparison
应用功能 | 线程 | 进程 |
---|---|---|
创建 | pthread_create | fork,vfork |
退出 | pthread_exit | exit |
等待 | pthread_join | wait 、waitpid |
取消/终止 | pthread_cancel | abort |
读取 ID | pthread_self() | getpid() |
同步互斥/通信机制 | 互斥锁、条件变量、读写锁 | 无名管道、有名管道、信号、消息队列、信号量、共享内存 |
摘录自javaguide
从下图可以看出:一个进程中可以有多个线程,多个线程共享进程的堆和方法区 (JDK1.8 之后的元空间)资源,但是每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。
线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。
线程执行开销小,但不利于资源的管理和保护;而进程正相反。
进程切换是一个开销很大的操作,线程切换的成本较低。
线程更轻量,一个进程可以创建多个线程。
多个线程可以并发处理不同的任务,更有效地利用了多处理器和多核计算机。而进程只能在一个时间干一件事,如果在执行过程中遇到阻塞问题比如 IO 阻塞就会挂起直到结果返回。
同一进程内的线程共享内存和文件,因此它们之间相互通信无须调用内核。
总体上
从计算机底层来说: 线程可以比作是轻量级的进程,是程序执行的最小单位,线程间的切换和调度的成本远远小于进程。另外,多核 CPU 时代意味着多个线程可以同时运行,这减少了线程上下文切换的开销。
从当代互联网发展趋势来说: 现在的系统动不动就要求百万级甚至千万级的并发量,而多线程并发编程正是开发高并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能力以及性能。
计算机底层
单核时代:在单核时代多线程主要是为了提高单进程利用 CPU 和 IO 系统的效率。 假设只运行了一个 Java 进程的情况,当我们请求 IO 的时候,如果 Java 进程中只有一个线程,此线程被 IO 阻塞则整个进程被阻塞。CPU 和 IO 设备只有一个在运行,那么可以简单地说系统整体效率只有 50%。当使用多线程的时候,一个线程被 IO 阻塞,其他线程还可以继续使用 CPU。从而提高了 Java 进程利用系统资源的整体效率。
多核时代: 多核时代多线程主要是为了提高进程利用多核 CPU 的能力。举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,不论系统有几个 CPU 核心,都只会有一个 CPU 核心被利用到。而创建多个线程,这些线程可以被映射到底层多个 CPU 上执行,在任务中的多个线程没有资源竞争的情况下,任务执行的效率会有显著性的提高,约等于(单核时执行时间/CPU 核心数)。
CITS2002 Systems Programming, Lecture 7,
Process & Thread