目录
一、什么是进程(Process)?
进程的管理
进程调度(重点)
二、什么是线程(Thread)?
三、进程和线程的区别与联系
进程(Process)
线程(Thread)
总结比较
一、什么是进程(Process)?
进程和线程是操作系统中一个非常核心的话题,也是程序员面试的高频考点。在计算机科学中,进程和线程是操作系统管理和调度任务的基本单位,对于理解计算机程序的执行、资源管理以及多任务处理都非常重要。
一个应用程序,运行起来,在操作系统中就会出现一个对应的进程。进程,就是一个跑起来的应用程序。
打开任务管理器,这里就会把当前运行的所有进程都列出来(除了自己运行的应用程序,还会有很多系统自带/安装某些应用给你添加的进程)
而对于一个操作系统,它管理的系统资源是有限的。操作系统需要管理计算机的各种资源,包括CPU、内存、硬盘空间、网络连接等等。这些资源都是有限的,而操作系统需要合理地分配和管理这些资源,以确保不同程序和用户能够有效地共享和利用这些资源。因此,进程是操作系统进行 资源分配/管理 的基本单位。
进程的管理
在操作系统中,对于进程管理,通常的做法是先描述,再组织。
描述:通常使用PCB(进程控制块)这样的结构体/数据结构来描述进程。结构体中会包含一些进程的核心信息。
组织:通常使用链表这样的结构(简化的说法),来把多个PCB串起来。
PCB 包含了很多关于进程的重要信息,此处只讨论几个比较关心的:
1. PID 进程标识信息:即进程的ID,同一个机器,同一时刻,进程ID一定是不同的。
2. 内存指针/内存管理信息:用于指向记录这个进程的指令和数据。
3. 文件描述符表:记录进程打开的文件和文件描述符。
接下来的几个属性更重要,更抽象,是用来支持进程调度的。
在此之前,先了解一下并行和并发。
在系统中包含了很多的进程,而进程的数量,是远远多于CPU的数量的,这就需要进程调度来负责了。一个核心,同一时刻只能运行一个进程,16个核心同一时刻就能同时运行16个进程。这就是并行执行。
但是,一个核心,不同时刻,可以执行不同的进程,这一刻执行进程1、下一刻执行进程2......为了能够同时处理这么多进程,CPU就会把总的执行时间,切换成若干个小的片段,每个片段执行一个进程,每个片段称为时间片。由于时间片比较短,CPU切换进程的速度非常快,人感知不到,站在人的角度看,这是若干个进程在同时执行(其实不是同时),这个就是并发执行。
并行(Parallelism):
- 并行是指系统同时执行多个任务或操作,实现多个操作同时进行,可以显著提高系统的运行效率。
- 在并行计算中,多个处理单元可以同时执行不同的指令或操作,以加快整体计算速度。
- 并行通常指在多个处理器上同时执行多个任务或操作,以提高整体性能。
并发(Concurrency):
- 并发是指系统能够同时处理多个任务或操作,但并不一定是同时执行这些任务,可能是通过时间片轮转的方式来实现看似同时处理的效果。
- 在并发编程中,多个任务可以交替执行,通过合理的调度和资源管理来提高系统的吞吐量和效率。
- 并发通常指系统能够处理多个任务的能力,可以是在单个处理器上通过多线程实现,也可以是在多个处理器上进行。
并行,本质上就是同时执行。而并发,并不是本质上的同时执行。但由于操作系统内核内部管理好了,编写应用程序的时候无法干预,普通用户也感知不到。
因此,往往把并行和并发统称为"并发",把编写解决并发问题的程序,称为"并发编程"。
进程调度(重点)
注意:这里的属性也属于PCB管理进程的信息。进程调度可以帮助处理并发编程中的一些问题。
1. 进程状态:就绪态、运行态、阻塞态。
我们可以通过一个生活中常见的例子来解释进程状态,比如做菜的过程可以用来说明进程状态。
-
就绪态:假设你打算做一顿晚餐,首先收集了所有需要的食材和调料,并且准备好了所有的厨具。此时,你已经准备好开始做菜,处于就绪态,等待开始动手。
-
运行态:当你开始处理食材、烹饪菜肴时,你处于运行态,正在实际执行做菜的过程,不断地进行下一步操作,直到菜肴做好为止。
-
阻塞态:在烹饪的过程中,有可能会出现一些情况使得你需要停下手头的工作,比如等待水开、等待肉类腌制等。这时候,你处于阻塞态,暂时停止了当前的操作,等待某个条件满足后才能继续往下进行。
-
就绪态到运行态:当等待的条件满足后,比如水开了或者肉腌制好了,你又可以继续进行烹饪,从就绪态转变为运行态。
-
运行态到阻塞态:在烹饪的过程中,可能会遇到需要等待的情况,比如等待煮面、等待炒菜等,这时候你会从运行态切换到阻塞态。
2. 优先级
假设有一个妹子(漂亮,有才华),有很多人追她。但是她找对象的标准几乎所有人都不能同时满足,于是她决定以每个标准找一个男朋友,然后她就找了三个小哥哥谈恋爱。
A. 有钱的小哥 B. 长的很帅的小哥 C. 非常会舔的小哥
那在约会的时候,妹子给男朋友们安排的时间不一定是公平的!
有的小哥,妹子会花更多的时间,那得到更多时间的小哥对应的等待时间也就更少。即进程的优先级决定了其在就绪队列中被调度的顺序,优先级高的进程会更早地得到处理器时间片,从而更快地执行。
周一周二周三:和A约(有钱,投入时间多,回报更多)
周四周五:和B约(长得帅,提供情绪价值)
周六:和C约(提供的情绪价值不如B)
3. 上下文:确保进程能够正确地切换和执行
妹子在和三个小哥哥聊天时,要记录好每次约会的时间和约定好的事情,即每次聊天都产生了哪些信息,接下来要干嘛,都要记录好,并且下次约会时,能够准确将信息对应到正确的小哥。不然就穿帮了,完蛋了~~
进程在CPU执行过程中,也会产生很多的"中间结果",在进程切换出cpu之前,就需要把这些中间结果(cpu的各种寄存器中的值)保存到pcb的上下文里(程序计数器(PC)、寄存器内容、堆栈指针等等)下次这个进程再次执行的时候,就需要将之前记录的状态恢复回来。
4. 记账信息
延续刚才的优先级,有的小哥分配时间多(资源倾斜),有的小哥(C)分配的时间少。
此时,就需要通过表格,来统计每个小哥分配了多少时间。此时就能发现,C最近分配的时间有点少,于是接下来就得给C多分配点。
为了不失去C,让C还能继续积极的来舔妹子,妹子就需要给C适当加点甜头~~
同样的,操作系统也是要避免某个进程一直吃不到CPU资源,导致进程关闭,就会进行类似的统计,给资源分配少的进程,适当的多分配一些。
相信通过这些形象的例子,对进程调度会有更清楚的认识!
二、什么是线程(Thread)?
线程(Thread)是操作系统能够进行运算调度的最小单位,它被包含在进程(Process)之中,是进程的实际执行单元。一个进程可以包含多个线程,称为主线程,主线程是程序的入口点。这些线程共享相同的内存空间和系统资源,但拥有独立的执行流程。
当前市面的CPU,都是多核心CPU。需要通过一些特定的编程技巧(并发编程),把要完成的任务,拆解成多个部分,并且分别让他们在不同的CPU上运行,否则多核心CPU,多出来的就浪费了。
通过"多进程"编程的模式,其实就可以起到”并发编程“的效果,因为进程可以被调度到不同的CPU上运行,此时,就可以把多个CPU核心都利用起来。
但是,多进程编程虽然可以解决上述问题,但也有一些新的麻烦
- 假设我们正在开发一个Web服务器应用程序,该程序需要同时处理多个客户端请求。为了实现并发处理,我们可以使用进程和线程两种方式。
- 使用进程的方式:
- 我们将每个客户端请求看作一个独立的进程,每个进程都有自己的内存空间和系统资源。当收到一个新的客户端请求时,系统会为该请求创建一个新的进程,并在该进程中执行处理逻辑。这种方式的优点是进程之间相互独立,一个进程的崩溃不会影响其他进程。但是,这种方式的缺点是创建和销毁进程的开销比较大,进程之间的通信和同步也比较复杂。
而引入线程,就是为了解决多进程创建和销毁开销太大的问题。
前面说到:一个进程可以包含多个线程,线程,其实就是进程的一部分。同一个线程共享相同的内存空间和系统资源。
因此线程解决多进程创建和销毁开销太大的核心就在于,只有创建第一个线程的时候(也就是创建进程的时候),才去进行资源申请操作。后续在创建线程,都没有申请资源的过程了。
再回头看上面服务器的例子
- 使用线程的方式:
- 我们将每个客户端请求看作一个线程,在一个主线程中不断轮询客户端请求,当收到新的请求时,将该请求放入等待队列中,并创建一个新的线程来处理该请求。处理完成后,该线程结束,等待队列中的下一个请求继续被处理。这种方式的优点是线程的创建和销毁开销较小,线程之间的通信和同步也比较简单。
虽然多线程编程解决了多进程创建和销毁开销太大的问题,但它存在一个缺点:一个线程的崩溃可能会导致整个进程的崩溃。进程和线程这两个概念比较抽象,下面用生活中的场景来描述一下。
- 进程就像是一列火车,每个车厢都是一个独立的运行环境,车厢之间互不干扰,一个车厢的故障不会影响其他车厢。而线程就像是一列火车上的乘客,每个乘客都可以在火车上自由行动,但他们的行动是相互影响的,一个乘客的行为可能会影响其他乘客。
- 比如,一列火车从北京开往上海,这列火车可以看作是一个进程,每个车厢都可以看作是一个线程,每个车厢都有自己的座位、空调、电视等设施,互不干扰。如果有一个车厢的空调坏了,其他车厢的乘客不会受到影响。而如果有一个乘客在车厢里吸烟,会影响到其他乘客的乘车体验。
- 再比如,一家餐厅里有多个服务员在同时服务不同的客人,每个服务员可以看作是一个线程,他们都在同一个餐厅里工作,可以相互协作,提高服务效率。如果有一个服务员突然生病了,其他服务员可以顶替他的工作,保证服务质量。而如果餐厅里的客人太多,需要再招一个服务员来服务,这就相当于增加了进程,会增加餐厅的成本和管理难度。
多线程编程的难点:
- 一个线程出问题,会影响到别的线程。
- 一个进程出问题,一般不会影响到其他进程。
三、进程和线程的区别与联系
进程(Process)
- 定义:进程是程序执行时的一个实例,是操作系统分配资源的基本单位。
- 特点:
- 拥有独立的内存空间,进程之间互相隔离。
- 进程之间通信需要额外的机制,如管道、信号等。
- 进程拥有独立的地址空间和控制流程。
- 优点:
- 进程之间互相独立,一个进程崩溃不会影响其他进程。
- 进程可以充分利用多核处理器的并行性能。
- 缺点:
- 进程切换开销大,资源消耗较高。
线程(Thread)
- 定义:线程是进程内的一个独立执行流程,是操作系统调度的最小单位。
- 特点:
- 共享进程的资源,如内存空间、文件描述符等。
- 线程之间通信相对容易,可以直接访问共享数据。
- 线程切换开销小,适合处理多个任务。
- 优点:
- 线程之间共享资源,通信方便,效率高。
- 线程可以提高程序的并发性和响应速度。
- 缺点:
- 多线程编程需要考虑同步和竞态条件等问题,容易引发错误。
总结比较
- 进程是程序的执行实例,拥有独立的内存空间,进程之间通信复杂,切换开销大;而线程是进程内的执行单元,共享进程资源,通信简单,切换开销小。
- 进程之间互相独立,一个进程崩溃不会影响其他进程;而线程之间共享资源,一个线程的错误可能导致整个进程崩溃。
- 进程适合处理独立的任务,线程适合处理并发任务和提高程序性能。在实际应用中,通常会同时使用进程和线程来充分发挥系统资源和提高程序效率。