面试官:说说有了进程,为什么还要有线程?

面试官:说说有了进程,为什么还要有线程?

面试官:说说有了进程,为什么还要有线程?

1. 引子2. 进程是什么?3. 进程到底重在哪?3.1 上下文切换3.1.1 什么是上下文?3.1.2 有哪些类型的上下文切换?

3.2 进程通信3.2.1 为什么要进程通信?3.2.2 进程之间通信

4. 线程5. 总结

1. 引子

为什么说进程是操作系统进行资源分配的基本单位?既然有了进程,为什么还要引入线程?都说进程很重,那么到底重在哪?

2. 进程是什么?

前文【进程的前世今生】我们一起学习了进程的诞生。

到底什么是进程?

从进程的结构看:进程是一个应用程序的运行实例,是程序运行时的一个实体。从进程的功能看:进程是应用程序运行时所需资源的容器。从操作系统对进程实现的角度来说:进程是一堆数据结构。

对于程序员来说,程序就是用计算机语言写的一系列指令,告诉计算机怎么完成任务,解决问题。

简单来说,程序就是教计算机该怎么干活。

但程序本身并没有生命周期,它只是在磁盘上的一些指令和数据。要让它运行,操作系统需要为程序提供资源和管理,进而引出了【进程】这一概念。

程序本身不涉及资源的管理与保护,进程的作用是管理程序所需的资源。如果每个程序都能随意访问计算机资源,就会引发各种问题。这就是为什么说:进程是资源管理的基本单位。

进程的出现不仅解决了资源分配的问题,还解决了另一个重要问题:并发。 通过进程,多个任务可以同时运行,从而更高效地利用CPU。

无论是单核CPU还是多核CPU,操作系统都需要调度进程,合理安排每个进程的运行时间。因此,进程是操作系统调度的独立单位。

然而,CPU是有限的,操作系统需要调度进程,合理分配CPU资源。这就引出了进程间通信的问题:多个进程共享资源或数据时,需要保持一致,确保数据正确。

有了进程以后,为什么还会出现线程呢?

3. 进程到底重在哪?

进程虽然在资源管理和并发执行上发挥了重要作用,但也存在一些问题:

上下文切换成本高:操作系统在切换进程时需要保存和恢复大量的状态信息,这会消耗相当一部分CPU时间,影响性能。进程间通信麻烦:由于进程之间是相互独立的,不能直接共享数据,它们必须通过复杂的机制(如消息队列、共享内存等)来进行通信,这增加了开发难度和系统开销。

3.1 上下文切换

3.1.1 什么是上下文?

操作系统通过时间片分配来让多个进程轮流执行。比如,有三个进程:A、B、C。进程A首先获得CPU时间片执行。当A的时间片用完,操作系统就会切换到进程B或C。

问题是,A未执行完成,如何确保下次继续执行?为了解决这个问题,操作系统引入了“上下文”的概念。

在每个任务运行前,CPU需要知道任务是从哪儿运行的,然后从哪儿开始的,也就是说,需要系统给它设置好CPU寄存器和程序计数器。

CPU寄存器:CPU内置的容量小,但是速度极快的内存;程序计数器:是用来存储正在执行的指令位置,或者即将执行的下一条指令位置。

他们是CPU在运行任何任务前,都必须依赖的环境,因此也被叫做CPU上下文。

那什么是上下文切换呢?

CPU 上下文切换,就是先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。

比如操作系统暂停进程A,执行进程B时:

保存A的上下文:当进程 A 暂停时,操作系统将A的状态(如寄存器、程序计数器等)保存下来。加载B的上下文:然后,操作系统加载进程B的状态,确保B能从上次停止的地方继续执行。

当A再次运行时,操作系统会恢复A的状态,让它从上次停止的地方继续执行。这样一来,多个进程可以交替执行,而不互相干扰。

3.1.2 有哪些类型的上下文切换?

根据任务的不同,上下文切换可以分为几种主要场景:系统调用上下文切换、进程上下文切换、线程上下文切换和中断上下文切换。

①. 进程上下文切换

在进程上下文切换过程中,操作系统需要完成以下操作:

保存当前进程的上下文(如寄存器状态、程序计数器等)加载新进程的上下文更新内存管理单元(MMU)以映射新进程的地址空间切换到新进程的执行环境

②. 系统调用上下文切换

操作系统的核心是内核(Kernel),它是独立于普通应用程序的,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。

为了保证内核的安全,现在的操作系统一般都强制用户进程不能直接操作内核。具体的实现方式基本上都是由操作系统把内存空间划分为两部分。一部分为内核空间,一部分为用户空间。

内核空间是操作系统内核访问的区域,独立于普通的应用程序,是受保护的内存空间。 用户空间是普通应用程序可访问的内存区域。

用户态是应用程序运行时的正常状态。在用户态下,程序的权限受限,无法直接访问硬件资源或修改操作系统的核心数据。内核态是操作系统核心代码运行时的状态。在内核态下,操作系统拥有完全的权限,可以直接访问硬件、管理内存、执行系统级操作等。

对于进程来说,通常运行在用户态,但当需要访问内存、磁盘等硬件设备时,需要通过系统调用切换到内核态。

举个例子,假如我们进行文件操作:

调用 open() 打开文件。使用 read() 读取文件内容。调用 write() 输出内容。最后使用 close() 关闭文件。

每次调用这些函数时,实际上都会涉及两次CPU上下文切换:

从用户态切换到内核态,CPU先保存当前的用户态状态。内核操作完成后,再切回用户态,恢复原来的状态,继续执行进程。

系统调用中的上下文切换与进程上下文切换的区别:

系统调用的上下文切换:在同一进程内,从用户态切换到内核态再回到用户态,不涉及虚拟内存或进程资源的切换。进程上下文切换:涉及从一个进程切换到另一个进程,需要切换虚拟内存、栈、全局变量等资源,成本更高。

③. 线程上下文切换

线程上下文切换就可以分为两种情况:

线程属于同一个进程,由于属于同一个进程,那么虚拟内存等资源不需要切换,只需要切换线程的私有资源,例如栈、寄存器等资源即可。线程属于不同进程,这个时候切换过程跟进程上下文切换没有区别。

也就是说,在同一进程内线程上下文切换的代价是比进程切换小的。

3.2 进程通信

3.2.1 为什么要进程通信?

进程间通信的需求可以类比人与人之间的交流。有两个主要原因。

首先是因为你有和对方沟通的需求,如果你都不想搭理对方,那就肯定不用通信了。其次是因为有空间隔离,如果你俩在一起,对方就站在你面前,你有话直说就行了,不需要通信。

同样,在软件系统中也需要进程间通信,原因和上面类似。

任务需求:软件中常常需要多个进程协同工作,比如一个进程请求另一个进程提供服务,或者某进程需要向其他进程发送消息。隔离限制:每个进程都有独立的用户空间,互相看不到对方的数据。这种隔离虽然保障了系统安全,但阻碍了直接交流,因此必须通过特定的通信机制来实现信息共享。

3.2.2 进程之间通信

我们之前说过,每个进程的用户地址空间是独立的,通常无法直接访问其他进程的内存。但所有进程通过内核空间进行管理和调度,进程间的通信必须通过内核提供的机制进行。

Linux 内核提供了不少进程间通信的机制:

管道(Pipe)消息队列(Message Queue)共享内存(Shared Memory)信号(Signal)套接字(Socket)信号量(Semaphore)内存映射文件(Memory-Mapped Files)

进程间通信存在的问题:

性能开销:由于进程间内存隔离,数据必须通过内核缓冲区传递,导致频繁的数据复制和上下文切换,增加性能开销。同步与互斥:多个进程同时操作共享资源时,必须确保数据一致性,防止竞态条件和冲突,这需要使用复杂的同步机制,如锁、信号量等。

4. 线程

简单理解,线程是进程当中的一条执行流程。

同一个进程内多个线程之间可以共享代码段、数据段、打开的文件等资源,但每个线程都有独立一套的寄存器和栈,这样可以确保线程的控制流是相对独立的。

为了更通俗地理解,我们做一个简单的类比:

计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。一个车间里,可以有很多工人。他们协同完成一个任务。线程就好比车间里的工人。一个进程可以包括多个线程。

5. 总结

①. 操作系统如何保证进程隔离的?

操作系统中运行了大量进程,为了不让它们互相干扰,可以考虑为它们分配彼此完全隔离的内存区域,即便进程内部程序读取了相同地址,而实际的物理地址也不会相同。

这就好比 A 小区的 10 号楼 808 和 B 小区的 10 号楼 808 不是一套房子,这种方法叫作地址空间。

②. 什么是上下文?

上下文可以理解为“进程在运行时的全部状态和资源”。比如,进程正在做什么、它在哪个位置、它的内存数据是什么、它的计算结果是什么等等。这些信息都被称为上下文。

③. 为什么要引入线程?

简单来说,进程是资源的拥有者,每个进程需要分配内存、CPU时间等资源。创建、销毁进程和切换进程需要比较大的开销。如果我们每次都频繁切换进程,系统的效率会下降。

为了提高效率并减少开销,操作系统提出了“线程”这个概念。

线程就是在进程内部进行调度的小单位,它不需要像进程一样分配和管理资源。多个线程可以共享同一个进程的资源(比如内存),但每个线程都有自己的执行任务。

这样,通过使用线程,操作系统可以在进程内更高效地分配时间和资源,减少不必要的开销,同时也能让多个任务并行处理,提高系统的整体并发能力。

④. 进程到底重在哪?

上下文切换开销高:进程之间的上下文切换需要保存和恢复大量的状态信息,涉及虚拟内存、寄存器、堆栈等,增加了系统负担。通信复杂:进程之间有严格的内存隔离,这虽提高了安全性,但也导致了进程间通信复杂,必须依赖内核来传递数据,造成性能瓶颈。资源消耗大:进程需要独立的内存空间和资源,创建和销毁进程开销大,导致系统效率低。

相关推荐

愿时光清浅,许你安然是什么意思呢?
28365365体育投注

愿时光清浅,许你安然是什么意思呢?

07-28 🌱 1830
很多人无法理解为什么有如此多的人喜欢看直播并一掷千金,其实很简单
挤压屏能用多久,挤压屏幕会不会伤害屏幕