[上文我们操作系统的基础概]念,今天我们来深入剖析下进程、线程的概念。本小节为进程线程基础概念篇(一),主要介绍什么是进程、线程,以及为什么引入进程和线程。
进程、线程概念全解析(一)
一、进程基础概念
- 什么是进程?为什么需要引入进程?
在早期,单一操作员单一控制终端,每次只能运行一个程序,CPU利用率不高。**为了提高 CPU 利用率,实现能将多个程序同时加载、并发执行,从而引入了进程的概念。**这些存在于计算机内存中的一个一个的程序就称之为进程。
也就是说,进程就是一个程序,比如我们打开的QQ,后台其实就是一个进程。这个概念的引入就是为了实现多道程序并发执行,以提高 CPU 的利用率。
同时这里也涉及到了两个新的概念,即并发与并行。
- 并发与并行
并发,即感觉上好像是一起执行,但实际上同一时刻只有一处程序在运行,但一个时间段有多个程序在跑。而并行,就是同一时刻就有多个程序在运行。在早期单核的时代,进程引入实现的效果实际上是并发,它让每个用户感觉都是自己独占 CPU。
- 进程的管理,即如何产生与消亡?
首先进程的管理者,无可厚非,就是操作系统。操作系统在管理进程时,需要记录进程的一些信息,这个用于维护进程记录的结构就是进程控制块(Process Control Block,PCB),它有点类似我们人的档案,包括我们的一些身份证信息等。在PCB中一般会存储包括程序计数器、寄存器、栈指针、进程ID、进程创建时间,当前持有的句柄等信息。
每个进程占用一片内存空间,进程之间是独立的,所以也就会引出下一个问题,即进程之间如果需要交流有什么方式。
- 进程间通信的方式,后面会再详细介绍每种方式
管道、套接字、信号、信号量、共享内存、消息队列
二、线程相关基础概念
- 什么是线程?为什么需要引入线程
这个问题可以转换为“为什么有了进程,还需要线程”,即“进程模型”有什么缺陷。进程模型的缺陷在于一个时间只能做一件事情,如果想同时做两件或多件事情,进程就不够用了,比如打开qq,想同时能和多个人一起聊天。此外,更为重要的是,如果进程在执行的过程中阻塞,例如等待输入,整个进程都会挂起,如果进程内只有少量工作输入等待输入,就造成了浪费。
基于上述问题,引入了线程的概念。可以理解线程就是为了帮助进程能同时做多个事情,并更好的提高利用率而产生的。
通常一个进程至少有一个线程,当一对一时,线程就可以认为是进程,所以通常也说线程其实是轻量级的进程。可以看到当有了线程概念后,其实线程才是CPU调度的最小单位,即基本单位。
- 多线程相比多进程的好处?
这个问题其实等同于在问进程模型的缺陷,进程之间是不共享资源的,每个进程都有PCB来管理自己的“档案”信息,所以进程切换开销会很大,需要保存当前的状态信息,而同一个进程下的多线程是共享进程的资源的,切换开销相比进程要小很多。
对于I/O密集型任务,因为频繁发生I/O等待(即阻塞),会频繁进行切换,所以更适合于多线程,而对于CPU密集型任务,因为切换少,能用到多核,因为更适合于多进程。
- 多线程可能带来的风险?
-
安全性:
通常我们说安全性,即“永远不会发生糟糕的事情”,但显然多线程的场景,因为线程并发执行,顺序不可控,所以很容易发生不可预测的问题,特别是在操作共享变量的场景下。比如常见的 num++ 操作 -
活跃性
活跃性即“某件正确的事情最终一定会执行”,但在多线程情况下,因为存在锁竞争,很容易发生类似于死锁(你等着我,我等着你)、饥饿(我永远没办法被调度)、活锁(前面一直出问题,后续的代码永远无法执行)等问题 -
性能问题
性能问题即“正确的事情总是最快的被解决”,多线程总会带来某种程序的运行时开销,比如切换需要保存和恢复上下文等等,这就需要我们进行良好的并发程序设计 -
复杂度
正因为并发程序有许多不可控性,在设计和代码编写、以及问题定位上都会更加复杂,对编程人员的要求也会更高
三、总结
总结如下:
- 进程是为了解决多道程序并发而产生
- 线程则是为了解决进程模型缺陷,让进程实现同时做多件事产生
进一步扩展问题,也是实际面试过程中遇到的:
- 如何从操作系统层面理解进程与线程的概念?
- 线程共享进程的资源,哪些资源是共享的,哪些资源是线程私有的?