【操作系统入门】进程与线程(一)

【操作系统入门】第三章:进程与线程(一)—— 程序为何"运行"起来?

本系列共10篇,这是第3/10篇。在第二章,我们回顾了操作系统的演进与架构。今天,我们将深入最核心的动态概念——进程与线程,探索操作系统实现并发执行的魔法。

开篇:从静态代码到动态执行

当我们双击一个可执行文件(比如 notepad.exe),一个神奇的变化发生了:硬盘上一段静态的二进制代码,变成了屏幕上可以与用户交互的、“活着"的程序。这个"活着的实体”,就是进程。理解进程,是理解现代操作系统如何工作的关键第一步。


第一部分:进程——程序的执行实例

1.1 进程的正式定义

一个进程,本质上是一个正在执行的程序的实例。它不仅包含程序的代码(文本段),还包括程序运行时所需要的一切资源与状态:

  • 程序的代码(文本段)
  • 当前活动:如程序计数器(PC)的值和处理器寄存器的内容
  • 堆栈:存放临时数据(如函数参数、返回地址、局部变量)
  • 数据段:存放全局变量
  • :程序运行时动态分配的内存
  • 一系列系统资源:如打开的文件、I/O设备等

关键洞察:程序本身只是存储在磁盘上的被动实体(一堆指令和数据),而进程是主动实体,它拥有一个程序计数器,指示下一个要执行的指令,以及一组相关的资源。

1.2 进程控制块(PCB)—— 进程的"身份证"

操作系统是如何管理成百上千个进程的呢?答案是:通过进程控制块(PCB)。每个进程在创建时,操作系统都会为它分配一个唯一的PCB。当进程终止时,其PCB也随之被回收。PCB是进程存在的唯一标志

一个完整的PCB通常包含以下信息:

  • 进程标识符(PID):唯一的数字ID,如同我们的身份证号。
  • 进程状态:运行、就绪、等待/阻塞等。
  • 程序计数器(PC):指向该进程下一个要执行的指令的地址
  • CPU寄存器:当进程被切换出CPU时,必须保存所有寄存器的值,以便后续能精确地恢复现场。
  • CPU调度信息:进程优先级、调度队列指针等。
  • 内存管理信息:基址/限址寄存器的值、页表或段表的指针。
  • 记账信息:CPU已使用时间、时间限制等。
  • I/O状态信息:分配给该进程的I/O设备列表、打开的文件列表等。

PCB是使进程能够"挂起"和"恢复"的关键。当操作系统决定切换到另一个进程时,它只需要将当前进程的寄存器状态保存到其PCB中,然后从下一个进程的PCB中加载寄存器状态即可。这个过程称为上下文切换


第二部分:进程的状态——生命周期的演绎

一个进程在其生命周期中,会经历几种状态。理解这些状态及其转换,是理解进程调度的基础。

经典的进程五状态模型:

  1. 新建:进程正在被创建。
  2. 就绪:进程已获得了除CPU之外的所有必要资源,万事俱备,只欠CPU。在就绪队列中等待调度。
  3. 运行:指令正在CPU上被执行。
  4. 阻塞/等待:进程等待某种事件的发生而无法继续执行(如等待I/O操作完成、等待信号量)。即使CPU空闲,它也无法运行
  5. 终止:进程已结束执行。

状态转换的典型场景:

  • 新建 -> 就绪:操作系统完成进程创建,准备好投入运行。
  • 就绪 -> 运行进程被调度器选中。这是操作系统的核心决策之一。
  • 运行 -> 就绪:最常见的原因是时间片用完,或被更高优先级的进程抢占。
  • 运行 -> 阻塞:进程主动请求一个必须等待的操作,如发起I/O请求、尝试获取一个已被占用的锁。
  • 阻塞 -> 就绪:进程所等待的事件发生了(如I/O完成),它具备了继续运行的条件。
  • 运行 -> 终止:进程执行完毕,或出现致命错误被强制终止。
第三部分:为什么需要线程?—— "进程内"的并发

进程的概念很好地实现了程序间的并发与隔离。但随着应用越来越复杂,人们发现进程作为调度和拥有的单位,存在一些沉重的问题:

  1. 创建进程开销大:分配内存、建立页表、创建PCB等,需要大量的系统调用和内存操作。
  2. 撤销进程开销大:需要回收所有资源。
  3. 进程间切换开销大:上下文切换需要保存和恢复整个内存映像(寄存器、内存管理信息等)。
  4. 进程间通信(IPC)复杂且慢:由于进程间内存空间相互隔离,通信必须通过内核介入(管道、消息队列、共享内存等),开销巨大。

线程的引入:轻量级进程

为了解决这些问题,线程被引入了。线程被称为"轻量级进程",它是CPU调度的基本单位

核心思想: 将传统进程的两个功能分离开:

  • 资源拥有的单位:这仍然是进程。一个进程拥有一个完整的资源集合,如地址空间、打开的文件等。
  • 调度和执行的单位:这变成了线程。一个进程内可以有一个或多个线程,它们共享进程的所有资源,但每个线程有自己的执行流。

一个生动的比喻:

  • 进程像一个办公室,里面有公共的资源:桌椅、打印机、文件柜(对应内存空间、打开的文件)。
  • 线程像办公室里的员工。多个员工(线程)共享办公室的所有公共资源,但他们各自独立地工作(有自己的任务和进度)。

线程的组成:
一个线程拥有自己独立的:

  • 线程ID
  • 程序计数器(PC)
  • 寄存器集合
  • 堆栈(用于存放局部变量、返回地址)

但线程共享其所属进程的:

  • 代码段
  • 数据段
  • 打开的文件和I/O设备等操作系统资源

线程的优势:

  1. 响应性:在一个单线程进程中,如果一个操作(如复杂的I/O)阻塞了,整个进程都会停止响应。而在多线程程序中,即使一个线程被阻塞,其他线程仍然可以继续执行,保持程序的响应性(如UI线程与工作线程分离)。
  2. 资源分享:线程默认共享内存和资源,使得通信和数据共享变得极其方便和高效。
  3. 经济性:创建和销毁线程比进程快得多。线程上下文切换也比进程上下文切换快,因为它们在同一个地址空间内,不需要切换页表等内存管理结构。
  4. 可伸缩性:在多处理器架构上,多线程可以真正实现并行执行,每个CPU核心运行一个线程,极大地提高了性能。

第四部分:线程的实现模型

线程是如何在底层实现的?主要有三种模型:

1. 用户级线程

  • 实现者:在用户空间通过线程库(如Pthreads)实现。内核对此一无所知,内核视角下仍只有一个进程。
  • 管理方式:线程的创建、调度、同步全部在用户空间的线程库中完成,无需内核介入
  • 优点
    • 极快的线程操作,因为不需要陷入内核态。
    • 可以自定义调度算法。
  • 致命缺点“一荣俱荣,一损俱损”。如果一个用户级线程执行了一个阻塞式系统调用(如读文件),由于内核只知道这个进程,它会将整个进程阻塞,导致该进程内的所有线程都无法执行。此外,无法利用多核CPU的真正并行。

2. 内核级线程

  • 实现者:由操作系统内核直接支持。内核负责线程的创建、调度和管理。
  • 管理方式:内核为每个线程维护一个线程控制块(TCB)。
  • 优点
    • 一个线程阻塞时,内核可以调度该进程内的其他就绪线程或别的进程的线程。
    • 能够很好地利用多处理器,将不同线程分配给不同CPU核心。
  • 缺点
    • 线程操作(创建、销毁、同步)需要系统调用,模式切换开销比用户级线程大。

3. 混合模型

  • 实现方式:结合上述两者。用户级线程被多路复用到数量等于或小于内核级线程的集合上。
  • 工作方式:程序员可以在用户空间创建任意数量的用户级线程,但它们在运行时被映射到一组由内核调度的内核级线程上。
  • 优点:结合了二者的优点,既保持了用户级线程的灵活性和部分性能优势,又能利用多核并行,且不会因为一个线程的阻塞导致整个进程阻塞。
  • 代表:现代操作系统如Windows、Linux的线程实现本质上都是内核级线程,但通过高效的库和机制,提供了良好的使用体验。
总结与展望

今天我们深入探讨了:

  • 进程资源分配的基本单位,是一个正在执行的程序的实例,拥有独立的地址空间。
  • 线程CPU调度的基本单位,是进程内的一条执行流,共享进程的资源。
  • PCB是进程的灵魂,记录了进程的全部信息。
  • 引入线程是为了实现更细粒度的并发,减少上下文切换开销,并简化通信。

理解进程与线程的区别与联系,是理解现代软件(从Web服务器到你的手机App)如何高效运行的基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

star _chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值