操作系统(三)进程和线程的基础知识

前言

本文主要涉及操作系统的简介、硬件结构、内存管理、进程管理、文件系统、设备管理等内容,可以作为学习操作系统的辅助文本记录。撰写本文的目的主要是针对操作系统整体做一个相对完整的梳理,以便后续回顾之用。
本文是第三篇,讲述操作系统的进程和线程的基础知识
第一篇:操作系统(一)基础知识及操作系统启动
第二篇:操作系统(二)内存管理的基础知识

进程和线程

进程

进程的定义: 进程是指一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程。

程序= 文件(静态的可执行文件)
进程 = 执行中的程序 = 程序+执行状态

同一个程序的多次执行过程对应着不同进程,如命令“ls”的多次执行对应多个进程。

进程执行需要的资源:

  • 内存:保存代码和数据
  • CPU:执行指令

进程与程序的区别:

  • 进程是动态的,程序是静态的;

    程序是有序代码的集合,进程是代码的执行,进程有内核态/用户态;

  • 进程是暂时的,程序是永久的;

    进程是一个状态变化的过程,而程序则可以永久保存

  • 进程和程序的组成不同;

    进程的组成包括程序、数据和进程控制块。

进程的状态

一个进程的活动期间至少具备三种基本状态:运行状态、就绪状态、阻塞状态,另外还有创建状态和结束状态

图片来源:https://tangjiayang.github.io/2023/06/02/操作系统/

  • 运行状态(Running):该时刻进程占用 CPU;

  • 就绪状态(Ready):可运行,由于其他进程处于运行状态而暂时停止运行;

  • 阻塞状态(Blocked):该进程正在等待某一事件发生(如等待输入/输出操作的完成)而暂时停止运行,这时,即使给它CPU控制权,它也无法运行;

  • 创建状态(new):进程正在被创建时的状态;

  • 结束状态(Exit):进程正在从系统中消失时的状态;

如果有大量处于阻塞状态的进程,进程可能会占用大量物理内存空间,被阻塞状态的进程占用着物理内存就一种浪费物理内存的行为。在虚拟内存管理的操作系统中,通常会把阻塞状态的进程的物理内存空间换出到硬盘,等需要再次运行的时候,再从硬盘换入到物理内存。而这些被换出到硬盘的进程,没有占用实际物理内存空间的情况,这个状态就是挂起状态

另外,挂起状态可以分为两种:

  • 阻塞挂起状态:进程在外存(硬盘)并等待某个事件的出现;
  • 就绪挂起状态:进程在外存(硬盘),但只要进入内存,即刻立刻运行;****
进程控制块(PCB,Process Control Block)

操作系统管理控制进程运行所用的信息集合称为 进程控制块

操作系统用PCB来描述进程的基本情况以及运行变化的过程。

PCB是进程存在的唯一标志。每个进程都在操作系统中有一个对应的PCB

进程控制块的内容:

  • 进程标识信息;
  • 处理机现场保存;
  • 进程控制信息
    • 调度和状态信息:进程和处理机使用情况调度;
    • 进程间通信信息:进程间通信相关的各种标识;
    • 存储管理信息:指向进程映像存储空间数据结构;
    • 进程所用资源:进程使用的系统资源,如打开文件等;
    • 有关数据结构连接信息:与PCB相关的进程队列;

PCB通过链表进行组织,通过把具有相同状态的进程组在一起。所有处于就绪状态的进程链在一起,形成 就绪队列; 所有因等待某事件而处于等待状态的进程链在一起,组成 阻塞队列

进程的上下文切换

什么是上下文切换?

一个进程切换到另一个进程运行,称为进程的上下文切换。暂停当前运行进程,从运行状态变成其他状态;调度另一个进程从就绪状态变为运行状态。

CPU一般是运行多任务的,为了记住每个任务运行到了那里,CPU配备了CPU寄存器和程序计数器。

CPU寄存器是CPU内部一个容量小但速度极快的内存(缓存) (是不是CPU cache?)

程序计数器是用来存储CPU正在执行的指令位置或者即将执行的下一条指令位置。

CPU寄存器和程序计数器是CPU在运行任何任务前所必须依赖的环境,这些环境叫做CPU的 上下文

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

上面所说的任务,主要包括 线程、进程以及中断

  • 进程是由内核管理和调度的,所以进程的切换只能发生在内核态。

所以,进程的上下文切换不仅包含了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的资源。

线程

Q: 为什么要引入线程?

A: 单进程的实现方式在实现某些问题时会不太好用,比如代码中间需要读一个很大的文件,每次到这里就要耗费事件,影响程序运行,而如果采用多进程并行执行,又要考虑进程的通信和数据共享问题,并且维护进程 的系统开销较大。

线程是进程当中的一条执行流程。

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

在进程内部增加一类实体,满足以下特性:

  • 实体之间可以并发执行;
  • 实体之间共享相同的地址空间

这种实体就是线程(Thread)

线程是进程的一部分,描述指令流执行状态,它是进程中的指令执行流的最小单元,是CPU调度的基本单位。

图片来源:https://tangjiayang.github.io/2023/06/02/操作系统/

线程的优点

  • 一个进程之中可以同时存在多个线程;
  • 各个线程之间可以并发地执行;
  • 各个线程之间可以共享地址和文件等资源

线程的缺点:

  • 一个线程崩溃,会导致其所属进程的所有线程崩溃;

    Q: 为什么?一个线程崩溃,会导致其所属进程的所有线程崩溃;

    1. 共享资源的破坏: 如果多个线程共享某些资源(如内存、文件句柄等),并且其中一个线程崩溃导致这些共享资源被破坏或异常使用,那么其他线程在访问这些资源时可能会受到影响。这可能导致其他线程出现错误、崩溃或异常行为。
    2. 异常处理不当: 如果一个线程在发生异常时没有适当地捕获和处理,异常可能会传播到其他线程。在多线程环境下,异常很容易跨越线程边界,并最终导致整个进程中的所有线程崩溃。
    3. 信号处理: 在某些操作系统中,当一个线程接收到一个致命信号(如段错误)时,整个进程可能会被终止,从而导致所有线程的崩溃。

进程与线程的比较:

  • 进程是资源分配单位,线程是CPU调度单位;

  • 进程拥有一个完整的资源平台,而线程只独享指令流执行的必要资源,如寄存器和栈;

  • 线程具有就绪、等待、运行三种基本状态和状态间的转换关系;

  • 线程能够减少并发执行的时间和空间开销

    • 线程的创建时间比进程短;
    • 线程的终止时间比进程短;
    • 同一进程内的线程切换时间比进程短;
    • 由于同一进程的各个线程间共享内存和文件资源,因此这些线程可以不通过内核进行直接通信。
  • 对于,线程相比进程能减少开销,体现在:

    • 线程的创建时间比进程快,因为进程在创建的过程中,还需要资源管理信息,比如内存管理信息、文件管理信息,而线程在创建的过程中,不会涉及这些资源管理信息,而是共享它们;
    • 线程的终止时间比进程快,因为线程释放的资源相比进程少很多;
    • 同一个进程内的线程切换比进程切换快,因为线程具有相同的地址空间(虚拟内存共享),这意味着同一个进程的线程都具有同一个页表,那么在切换的时候不需要切换页表。而对于进程之间的切换,切换的时候要把页表给切换掉,而页表的切换过程开销是比较大的;
    • 由于同一进程的各线程间共享内存和文件资源,那么在线程之间数据传递的时候,就不需要经过内核了,这就使得线程之间的数据交互效率更高了;

Q: 线程上下文切换的是什么?

这还得看线程是不是属于同一个进程:

  • 当两个线程不是属于同一个进程,则切换的过程就跟进程上下文切换一样;
  • 当两个线程是属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据
线程的实现
  • 用户线程(User Thread:在用户空间实现的线程,不是由内核管理的线程,是由用户态的线程库来完成线程的管理;
  • 内核线程(Kernel Thread:在内核中实现的线程,是由内核管理的线程;
  • 轻量级进程(LightWeight Process:在内核中来支持用户线程;

图片来源:https://tangjiayang.github.io/2023/06/02/操作系统/

用户线程是基于用户态的线程管理库来实现的,那么线程控制块(*Thread Control Block, TCB*) 也是在库里面来实现的,对于操作系统而言是看不到这个 TCB 的,它只能看到整个进程的 PCB。

所以,用户线程的整个线程管理和调度,操作系统是不直接参与的,而是由用户级线程库函数来完成线程的管理,包括线程的创建、终止、同步和调度等

用户线程的优点

  • 每个进程都需要有它私有的线程控制块(TCB)列表,用来跟踪记录它各个线程状态信息(PC、栈指针、寄存器),TCB 由用户级线程库函数来维护,可用于不支持线程技术的操作系统;
  • 用户线程的切换也是由线程库函数来完成的,无需用户态与内核态的切换,所以速度特别快;

用户线程的缺点

  • 由于操作系统不参与线程的调度,如果一个线程发起了系统调用而阻塞,那进程所包含的用户线程都不能执行了。
  • 当一个线程开始运行后,除非它主动地交出 CPU 的使用权,否则它所在的进程当中的其他线程无法运行,因为用户态的线程没法打断当前运行中的线程,它没有这个特权,只有操作系统才有,但是用户线程不是由操作系统管理的。
  • 由于时间片分配给进程,故与其他进程比,在多线程执行时,每个线程得到的时间片较少,执行会比较慢;

图片来源:https://tangjiayang.github.io/2023/06/02/操作系统/

内核线程是由操作系统管理的,线程对应的 TCB 自然是放在操作系统里的,这样线程的创建、终止和管理都是由操作系统负责。

内核线程的优点

  • 在一个进程当中,如果某个内核线程发起系统调用而被阻塞,并不会影响其他内核线程的运行;
  • 分配给线程,多线程的进程获得更多的 CPU 运行时间;

内核线程的缺点

  • 在支持内核线程的操作系统中,由内核来维护进程和线程的上下文信息,如 PCB 和 TCB;
  • 线程的创建、终止和切换都是通过系统调用的方式来进行,因此对于系统来说,系统开销比较大;

轻量级进程(Light-weight process,LWP)是内核支持的用户线程,一个进程可有一个或多个 LWP,每个 LWP 是跟内核线程一对一映射的,也就是 LWP 都是由一个内核线程支持,而且 LWP 是由内核管理并像普通进程一样被调度

进程加载

  • 允许进程“加载“一个完成不同的程序,并从main开始执行;

  • 允许进程加载时指定启动参数(argc, argv)

  • exec调用成功时

    • 它是相同的进程
    • 但是运行了不同的程序
  • 代码段、堆栈和堆(heap)等完全重写。

进程等待和退出

等待
  • wait()系统调用用于父进程等待子进程的结束;

    • 子进程结束时通过exit()向父进程返回一个值;
    • 父进程通过wait()接受并处理返回值;
  • wait()系统调用的功能

    • 有子进程存活时,父进程进入等待状态等待子进程的返回结果

      当某子进程调用exit()时,唤醒父进程,将exit()返回值作为父进程中wait()的返回值

    • 有僵尸子进程等待时,wait()立即返回其中一个值;

    • 无子进程存活时,wait()立刻返回。

退出
  • 进程结束执行时调用exit(),完成进程资源回收;

  • exit()系统调用的功能

    • 将调用参数作为进程的”结果“

    • 关闭所有打开的文件等占用资源;

    • 释放内存

    • 释放大部分进程相关的内核数据结构;

    • 检查父进程是否是存活着的

      如果存货,保留结果的值直到父进程需要它,进入僵尸(zombie/defunct)状态

      如果没有,它释放所有的数据结构,进程结束

    • 清理所有等待的僵尸进程

  • 进程终止是最终的垃圾收集(资源回收)

优先级控制

  • 指定进程的初始优先级;
  • Unix系统中进程优先级会随执行时间而衰减;

进程调试支持

  • 允许一个进程控制另一个进程的执行
  • 设置断点和查看寄存器等

定时

可以让进程在定时器的等待队列中等待指定时间

小结

本节主要操作系统的进程和线程的一些基础知识。
本文参考:

  1. 《深入理解计算机系统》
  2. 《操作系统》–清华大学网课
  3. 小林coding

如果您觉得我写的不错,麻烦给我一个免费的赞!如果内容中有错误,也欢迎向我反馈。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值