- 🍁 个人主页:爱编程的Tom
- 💫 本篇博文收录专栏:JavaEE初阶
- 👉 目前其它专栏:c系列小游戏 c语言系列--万物的开始_ 等
- 🎉 欢迎 👍点赞✍评论⭐收藏💖三连支持一下博主🤞
- 🧨现在的沉淀就是对未来的铺垫🎨
前言
本篇博客将讲述进程调度的基本过程,熟悉进程与线程之间的关系,掌握并理解进程以及线程更深层的含义
-
什么是进程/任务(Process/Task)
通俗的来讲:即电脑里运行的可执行文件
进程是操作系统对⼀个正在运行的程序的⼀种抽象
也可以理解为,可以把进程看做程序的⼀次运行过程;
同时,在操作系统内部,进程⼜是操作系统进行资源分配的基本单位。
-
如何管理进程
想知道如何管理进程,首先就要了解其预备知识,
即进程控制块(Process Control Block),就是PCB
PCB的相关属性:
pid:进程的id,即进程标识符
内存指针:指向进程执行所需要的指令位置(在内存中)
文件描述符表:记录打开的文件,用于跟踪进程打开的所有文件和I/O资源。每个进程都有一个文件描述符表,它是一个数组,其中每个元素都是一个文件描述符,这些文件描述符是整数,用于标识进程打开的文件和其他I/O资源(如管道、套接字等)。
-
管理进程的过程
1. 创建进程
- 系统调用:操作系统提供系统调用(如
fork
、exec
)用于创建新进程。fork
创建一个当前进程的副本,exec
用新的程序替换当前进程的内存空间。 - 进程控制块(PCB):创建进程时,操作系统会初始化一个进程控制块(PCB),其中包含进程的状态、程序计数器、寄存器、内存管理信息、I/O状态信息等。
2. 进程调度
- 调度算法:操作系统使用调度算法决定哪个进程获得CPU时间。常见调度算法包括先来先服务(FCFS)、短作业优先(SJF)、优先级调度、轮转调度(RR)等。
- 上下文切换:当调度器选择一个新进程运行时,操作系统会进行上下文切换,保存当前进程的状态并加载新进程的状态。
3. 进程同步
- 信号量和互斥锁:用于解决进程间的同步问题,防止多个进程同时访问共享资源导致数据不一致。信号量可以实现复杂的同步机制,互斥锁用于保护临界区。
- 条件变量:结合互斥锁使用,使线程能够等待某个条件成立,再继续执行。
4. 进程通信
- 管道(Pipe):用于在父子进程间通信,数据以字节流的形式在进程间传递。
- 消息队列:允许进程通过消息的形式进行通信,支持复杂的消息传递机制。
- 共享内存:多个进程可以直接访问同一块内存,实现高速数据共享。
- 信号:用于通知进程某个事件的发生,如定时器到期、中断等。
如上所述,进程是操作系统进行资源分配的最小单位,这意味着各个进程互相之间是无法感受到对方存在的,这就是操作系统抽象出进程这⼀概念的初衷
这样便带来了进程之间互相具备”隔离性 (Isolation)"。
5. 进程终止
- 正常终止:进程完成任务后,自行终止,调用
exit
系统调用。 - 异常终止:进程发生错误或收到某个信号(如
SIGKILL
)被强制终止。 - 父进程等待:父进程可以调用
wait
或waitpid
等待子进程终止,并获取其退出状态。
-
进程调度的基本过程
并行+并发的模式,支持多任务,简单来说,进程调度就是CPU如何给不同的进程分配资源的问题
与下面的一些属性有着密不可分的联系
状态
就绪状态:随时可以在CPU上执行
阻塞/等待状态:无法在CPU上执行,需等待其它进程执行完毕
优先级:允许谁的进程先执行,时间上的顺序,涉及到一些进程调度算法
上下文:
- 保存当前进程状态:操作系统保存当前正在运行的进程的状态(如寄存器值、程序计数器等)到该进程的PCB中。
- 加载新进程状态:从被选中的新进程的PCB中恢复该进程的状态,使其能够继续执行之前暂停的操作。
- 切换内核栈和内存映射:必要时,切换内核栈和内存映射,以确保新进程能够正确访问其内存空间和资源。
记账信息:记录系统资源分配的多少,以及进程调度的时间和内容
接下来简单地讨论一下线程是什么
-
什么是线程
⼀个线程就是⼀个"执行流".每个线程之间都可以按照顺序执行自己的代码.多个线程之间"同时"执行着多份代码. 线程是现代操作系统中一个基本的执行单元。它是一个轻量级的进程,也称为轻量级进程(Lightweight Process, LWP)。一个进程可以包含一个或多个线程,它们共享进程的资源(如内存、文件描述符等),但每个线程都有自己独立的运行栈、程序计数器和寄存器。线程的引入主要是为了提高程序的并发性和性能。
我们知道,频繁地创建和销毁进程会导致开销非常大
要想解决这个问题,我们可以使用以下两个方法:
进程池:
预先创建一定数量的进程,并将这些进程保留在一个池中,以便在需要执行并发任务时可以重复使用这些进程
轻量级的进程---线程:
由于线程共享进程资源的特性,可以降低开销,实现并发编程
-
进程和线程的简单例子
这里我们举一个简单的例子来分辨一下进程和线程:
- 进程
想象一个餐馆有多个独立的厨房,每个厨房是一个进程。每个厨房有自己独立的设备、食材和厨师队伍。不同厨房之间互不干扰,每个厨房独立处理各自的订单。
- 独立资源:每个厨房有自己独立的炉灶、锅碗瓢盆和食材储备。厨房之间不能直接共享资源(需要特殊安排,如外送食材)。
- 独立运行:每个厨房独立接单和处理订单,即使一个厨房出现问题(如火灾),不会直接影响其他厨房的正常运行。
- 开销大:每个厨房需要配备完整的设备和人员,所以开设和维持多个厨房的成本较高。
- 线程
现在想象一个餐馆有一个大厨房,厨房里有多个工作台,每个工作台是一个线程。工作台共享整个厨房的资源(如炉灶、冰箱、食材等),但是每个工作台由不同的厨师处理不同的订单。
- 共享资源:所有工作台共享同一个厨房的资源,厨师们可以直接使用公共资源(如冰箱里的食材、灶台等)。
- 协作运行:如果一个工作台需要等待某个资源(如锅),其他工作台仍然可以继续工作,但需要协调和避免争抢资源(如使用不同的时间段)。
- 开销小:所有工作台共享一个厨房的资源和设备,新增一个工作台的成本较低。
-
进程和线程的区别和联系
-
区别
- 进程是包含线程的.每个进程⾄少有⼀个线程存在,即主线程。
- 进程和进程之间不共享内存空间.同⼀个进程的线程之间共享同⼀个内存空间. 比如每个客户来银行办理各自的业务,但他们之间的票据肯定是不想让别⼈知道的,否则钱不就被其他⼈取⾛了么。⽽上⾯我们的公司业务中,张三、李四、王五虽然是不同的执 行流,但因为办理的都是⼀家公司的业务,所以票据是共享着的。这个就是多线程和多进程的最⼤区别。
- 进程是系统分配资源的最小单位,线程是系统调度的最小单位。
- ⼀个进程挂了⼀般不会影响到其他进程.但是⼀个线程挂了,可能把同进程内的其他线程⼀起带走(整 个进程崩溃).
更具体的一些区别如下:
资源管理
- 进程:每个进程有自己独立的地址空间和资源,包括内存、文件描述符等。进程之间不共享内存,通信需要使用进程间通信(IPC)机制,如管道、消息队列、共享内存等。
- 线程:线程是进程的子单位,同一进程内的所有线程共享进程的地址空间和资源,包括内存、文件描述符等。这使得线程之间的数据共享和通信更加直接和高效。
创建和销毁
- 进程:创建和销毁进程的开销较大,因为操作系统需要分配和回收独立的资源。通常使用
fork
或exec
系统调用创建进程。 - 线程:创建和销毁线程的开销较小,因为线程共享进程的资源。通常使用线程库(如POSIX线程库
pthread
或Java的Thread
类)创建线程。
执行方式
- 进程:进程是独立运行的,每个进程都有自己的程序计数器、堆栈和寄存器。进程切换涉及上下文切换,开销较大。
- 线程:线程是进程中的独立执行单元,每个线程有自己的程序计数器、堆栈和寄存器,但共享进程的全局变量和堆内存。线程切换的开销比进程切换小。
通信方式
- 进程:进程间通信(IPC)较复杂,需要借助操作系统提供的机制,如管道、消息队列、共享内存、信号等。
- 线程:线程间通信更加直接,因为线程共享进程的地址空间,可以通过共享变量进行通信。需要使用同步机制(如互斥锁、条件变量)防止竞争条件。
错误隔离
- 进程:进程间相互独立,一个进程的崩溃不会影响其他进程。这提供了良好的错误隔离。
- 线程:线程间不完全独立,一个线程的崩溃可能导致整个进程的崩溃。错误隔离较差。
-
联系
- 并发性:进程和线程都是用于实现并发性的方法,可以在单核和多核处理器上并行执行多个任务。
- 调度:操作系统调度器负责管理进程和线程的执行,决定哪个进程或线程在何时运行。
- 资源使用:进程和线程都消耗系统资源,如CPU时间、内存等。操作系统需要合理分配这些资源,确保系统稳定运行。
- 状态转换:进程和线程都有生命周期,包括创建、就绪、运行、等待和终止等状态。操作系统通过调度和上下文切换管理这些状态转换。