目录

一、引言

二、进程

2.1 PCB(进程控制块抽象)

2.3 内存管理

2.4 进程间通信

三、线程

3.1 进程和线程的区别

3.2 Java线程和操作系统线程的关系

四、总结


一、引言

    那么这一期就和大家简单聊一聊进程与线程的相关基础知识。其实这块基础概念并不算难,真正有难度的是后续的多线程深入原理和实战开发。本篇我只带大家梳理入门理论,不涉及任何代码,全程通俗易懂,只讲干货概念,那我们接下来就正式进入今天的内容。

二、进程

    所谓的进程其实就是操作系统对正在运行的程序的一种抽象描述,简单来说进程就是程序的一次运行过程,同时在操作系统的内部,进程又是操作系统进行资源分配的基本单位。

    你可以这么理解:程序是躺在硬盘上的代码文件,进程是跑起来之后的那个 “活的实体”。一个程序可以跑多次,变成多个独立的进程,互不干扰。

2.1 PCB(进程控制块抽象)

    这个东西有点像我们Java里面的类,它是操作系统把要管理的进程,抽象成一组有关联、互为一体的数据,而不是你说的“管理任何现实事物”——重点是管理“进程”,不是所有现实事物哦。

    直白点说,操作系统根本不认识什么程序、什么代码,它只认识PCB。每个进程一被创建,操作系统就会自动给它生成一个专属的PCB,里面记录着这个进程的所有关键信息,比如进程的ID(相当于进程的身份证)、进程当前的状态(正在运行、等待中)、进程的优先级、用到的内存地址、打开的文件等等。

    总结一下:PCB就是进程在操作系统里的“身份证+档案袋”,没有PCB,操作系统就不知道这个进程的存在,更没法管理它。

大家肯定都有过这样的体验:电脑上同时开着微信、浏览器、办公软件,明明CPU只有几个核心,却感觉所有软件都在同时运行,这背后靠的就是进程调度。

简单来说,进程调度就是操作系统“分配CPU时间”的一套规则。它会把CPU的运行时间切成很多极小的片段(毫秒级,我们肉眼根本察觉不到),然后快速轮流把这些时间片段分给各个正在运行的进程。

比如,先让微信运行1毫秒,再切换到浏览器运行1毫秒,再切换到办公软件运行1毫秒……因为切换速度太快,我们的肉眼就会觉得这些软件是同时在运行的。

进程调度核心就是做三件事:一是决定哪个进程先占用CPU(比如紧急的进程先跑),二是决定每个进程能占用CPU多久(避免某个进程一直霸占CPU,导致其他程序卡顿),三是决定什么时候把CPU从当前进程切换到另一个进程。

它的最终目的就是:让所有进程看起来是“同时执行”的,同时保证整个系统运行流畅,不卡顿、响应快。我们平时觉得电脑卡,很多时候就是进程调度没分配好资源,某个进程占用了太多CPU时间。

2.3 内存管理

        每个进程运行的时候,都需要占用一定的内存空间(比如存储程序代码、运行时的数据),而内存管理,就是操作系统给这些进程分配、管理内存的一套机制,相当于“内存管家”。

它主要做四件核心的事,用大白话讲大家一看就懂:

第一,给每个进程分配独立的内存空间。让每个进程都觉得自己独占了整个内存,不用和其他进程抢空间,也不用关心其他进程在哪里存储数据。

第二,隔离进程的内存。确保A进程不能随便访问、修改B进程的内存数据,这样能避免一个进程出错,导致其他进程也崩溃,保证系统的安全和稳定。

第三,管理虚拟内存。有时候进程需要的内存空间比电脑实际的物理内存还大,操作系统就会把硬盘的一部分空间当成“虚拟内存”来用,暂时存放不用的数据,等需要的时候再调到物理内存里,相当于给内存“扩容”。

第四,回收内存。当一个进程结束(比如关掉微信),操作系统会自动回收这个进程占用的内存空间,避免内存被浪费,也防止内存越用越少(也就是我们常说的内存泄漏)。

简单总结:内存管理就是给每个进程“分房子、划清界限、按需扩容、及时收房”,让所有进程都能安安全全、规规矩矩地使用内存。

2.4 进程间通信

    前面我们说过,进程之间是相互隔离的,每个进程都有自己独立的内存空间,默认情况下,谁也不能直接访问谁的数据——就像两个独立的房间,门是锁着的,里面的人不能互相串门。

    但实际开发中,进程之间经常需要配合工作、传递数据。比如你用聊天软件发消息,聊天软件的进程需要把消息传给网络模块的进程,再由网络模块发送出去;再比如游戏进程,需要把游戏画面数据传给显卡进程,才能在屏幕上显示出来。这时候,就需要一种机制让它们能互相传递数据,这就是进程间通信,简称IPC。

    常见的进程间通信方式有四种,不用记太复杂,知道各自的核心特点就行:

1. 管道:最简单、最基础的方式,就像一根“水管”,数据只能从一端传送到另一端,比如命令行里的“|”符号,就是用的管道通信。

2. 消息队列:像我们发邮件一样,一个进程把消息放到队列里,另一个进程从队列里取消息,不用两个进程同时在线,灵活度比管道高。

3. 共享内存:让多个进程共用一块内存空间,数据直接存在这块共享内存里,不用来回传递,是速度最快的一种通信方式。

4. 信号量:不是用来传递数据的,而是用来控制进程之间的“同步”,比如避免两个进程同时操作同一份数据,导致数据出错,相当于“红绿灯”,控制进程的执行顺序。

一句话总结:进程之间本来是“互不相干”的,进程间通信就是给它们搭起一座安全的“桥梁”,让它们能互相传数据、互相配合,完成更复杂的任务。

三、线程

    线程就是一个独立的执行流,每个线程都会按照顺序执行自己的代码,而多个线程之间,就可以同时执行多份代码,实现真正的并发执行。

    那为什么要有线程呢?其实很大一部分原因,就是进程太重了。进程的创建、销毁、切换,消耗的资源都非常大,效率并不高。

    再加上后来单核 CPU 的发展遇到了瓶颈,想要提高算力,就必须走向多核 CPU。而并发编程,就是为了充分利用多核 CPU 的能力。

    虽然多进程也能实现并发编程,但是进程太 “笨重”,所以我们就需要一个更轻量、更高效的角色来承担并发任务 —— 这就是线程。

    线程也被称为轻量级进程,它的创建速度比进程快、销毁速度比进程快、调度速度也比进程快,在并发场景下,效率远远高于进程。

3.1 进程和线程的区别

    首先最核心的一点:进程包含线程。一个进程里面,至少会有一个线程(主线程),也可以包含很多个线程。

它们之间最关键的区别有这几点:

1.进程和进程之间,内存空间完全独立、互不共享,隔离性很强;

2.但同一个进程里的所有线程,共享同一块内存空间,数据可以直接互相访问;

3.线程只有在创建第一个的时候,需要分配一些资源,后续再创建新线程,成本非常低;

4.线程是依附进程存在的,进程不退出,线程销毁和创建几乎不消耗额外资源;

5.一个进程挂了,通常不会影响其他进程;

6.但一个线程崩溃,很可能会把整个进程一起带走,导致所有线程都崩溃—— 这也是我们常说的线程安全问题。

简单记:进程是资源分配的最小单位,线程是调度执行的最小单位。

3.2 Java线程和操作系统线程的关系

    线程本身是操作系统层面的概念。操作系统内核实现了线程机制,并且向上提供了一组 API 供用户程序使用。

    而我们 Java 标准库里的 Thread 类,本质上就是对操作系统线程 API 的一层抽象、封装和简化

    也就是说:Java 线程 = 操作系统线程的包装类你在 Java 里创建一个线程,底层真的会创建一个操作系统线程来执行任务。

四、总结

     这期内容我们从头到尾简单聊了进程与线程的基础理论,全程没有涉及任何代码,只帮大家梳理底层概念和逻辑。

    首先我们搞懂了什么是进程,进程就是运行起来的程序,也是操作系统资源分配的基本单位。而 PCB 作为进程控制块,相当于进程的身份证和档案,是操作系统识别和管理进程的核心依托。

    接着我们了解了操作系统对进程的几大管理方式:依靠进程调度划分 CPU 时间片,让多个进程轮流执行,实现宏观上的并发效果;通过内存管理为每个进程分配独立内存、做好空间隔离、利用虚拟内存扩容,并且自动回收闲置内存,保证系统稳定运行;借助进程间通信,打破进程之间的隔离状态,通过管道、消息队列、共享内存、信号量这几种方式,让进程之间可以安全传数据、协同工作。

    然后我们又认识了线程,线程属于轻量级进程,是真正的代码执行流。相比进程,线程创建、销毁和调度开销都更小,更适合用来充分发挥多核 CPU 的性能,实现并发编程。

    同时也分清了进程和线程的核心区别:进程相互独立、内存不共享,而同一个进程下的多个线程共享内存资源;进程之间互不影响,但单个线程崩溃很容易带垮整个进程,这也是线程安全问题的由来。

    最后我们也理清了 Java 线程和操作系统线程的关系,Java 里的 Thread 类并不是凭空实现的,只是对操作系统原生线程 API 做了一层封装,本质上 Java 线程底层依旧依赖操作系统线程来运行。

   总的来说,进程负责分配资源,线程负责执行任务,弄懂这些基础概念,也能为后续学习多线程、并发编程打下一个很好的入门基础。

    那么在下一期将会带来多线程的一些讲解那个时候就会涉及到代码啦。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐