进程与线程

146 阅读5分钟

进程

一个进程本质上就是一个 PCB,用来描述一个程序运行起来后的在内存中的一系列状态

PCB

PCB 就是一个数据结构,包含着进程的一些核心信息【操作系统用一个双向链表来进行组织若干个PCB】

PCB属性

进程标识 pid

内存指针

操作系统要把一些必要的数据【如:运行的指令(代码)、运行时依赖的数据(全局变量)等】加载进内存

内存指针就描述了哪一部分属于指令,哪一部分是数据

描述了进程的内存资源

文件描述符表

标识当前进程打开了哪些文件

描述了进程的文件资源,所以文件描述符表 + 内存指针 就描述了进程持有哪些系统资源,也认为 进程 就是操作系统分配资源的基本单位

实现进程调度的属性

调度:CPU 核心数固定,但是 进程 数量就非常多了。不可能让一些进程一直执行,某些进程不执行把,所以就需要进行调度【操作系统就有很多的调度算法】

并发:一段时间上,多个进程同时执行

并行:一个时间点上,多个进程在多个CPU上同时运行

进程优先级

进程的状态

就绪态、运行态、等待态【当一个线程处于等待时,就会被挂起,不参与CPU调度】

进程的记账信息

操作系统在安排进程的时候,也会记录每个进程以往的 CPU 上执行的时间,如果发现某个进程的执行时间很少,就会更改调度策略

进程上下文

具体指的就是 CPU 里一堆寄存器里面的值,上下文就会在进程被切出 CPU 的时候把寄存器的状态保存到 PCB 中【内存】,下次再次被调度上 CPU 的时候,再从PCB中把上下文信息读到寄存器里

虚拟地址空间

若没有虚拟地址空间的概念:C 语言中就有指针操作,解引用就直接访问真实的内存地址了,万一一个差错指针指到了别的进程的空间了,就可以导致整个程序崩溃了【一直用人来保证不靠谱】,所以就引入了虚拟地址空间

image.png

比如进程3中 *p = xxx,p成了野指针,指到错误的地址上了,进行操作 p 指向的内存的时候会经过 MMU 进行映射,此时 MMU 就会知道当前访问的是一个有问题的地址,于是就会找操作系统告状:有进行访问内存错误了,这时操作系统就会给该进程发一个 “信号”,告诉它访问内存出错

虚拟地址空间就让进程有了一个很重要的特性:隔离性【一个进程崩溃不会影响另一个进程】

每个线程一个虚拟地址空间,会不会多个线程的虚拟空间加到一起,超过了物理内存,咋办呢

虽然系统里的进程那么多,但实际上

  1. 同一时刻执行的进程没几个【最多CPU核心数】
  2. 即使同一时刻,有好几个线程在跑,但是这些进程也不是同时把所有的虚拟地址都用上了,物理内存只需要把真实使用的这部分数据表示出来就行
  3. 极端情况,可能导致物理内存不够【这样算 bug 了,因此需要程序猿优化内存占用 或者 扩容或一个更大的内存机器】

隔离性带来的问题

进程间的隔离性就让多个进程之间想要配合工作就麻烦了,所以操作系统引入了“进程间通信”【搞一个公共资源,所有进程都可访问到】如:网络通信、socket 套接字通信

线程

多进程是为了能够充分使用 多核CPU 资源【并发编程】,但是在某些情况下多进程就会存在问题了,这时就需要线程了。 线程又被称作“轻量级进程”

线程包含在进程里面的,一个进程包含多个线程,并且多个线程共用该进程的内存空间【每次使用都直接在 进程 中拿,用户态操作比较快,销毁的时候直接还给进程即可,线程的创建就减少了分配资源的时间了】

  1. 如果需要频繁的创建/销毁进程,这时候效率就比较低消

创建进程的步骤

  1. 创建 PCB
  2. 分配系统资源【这个消耗时间】
  3. 将 PCB 加入到内核的双向链表中

image.png

image.png

image.png

两者区别

  • 进程:一个进程本质上就是一个 PCB,用来描述一个程序运行起来后的在内存中的一系列状态,也是操作系统分配资源的基本单位
  • 线程:线程是包含在进程内部的“逻辑执行流”(线程可以执行一段独立的代码,多个线程之间是并发执行的),线程也是操作系统随机调度的基本单位
  • 区别:
    1. 进程包含线程,线程是在进程内部的
    2. 每个进程都有自己独立的虚拟地址空间,也有自己独立的文件描述符表;同一个进程的多个线程之间,则共用这一份虚拟地址空间和文件描述符表
    3. 进程是操作系统中资源分配的基本单位。线程是操作系统中调度执行的基本单位
    4. 多个进程同时运行时,如果一个进程挂了,一般不会影响别的进程;而同一个进程里面的多个线程之间,如果一个线程挂了,很可能把整个进程带走了,当前进程的其他线程也就没了