进程基础知识
进程的概念
我们编译的代码可执行文件只是存储在硬盘的静态文件,运行时被加载到内存,CPU执行内存中指令,这个运行的程序被称为进程。进程是对运行时程序的封装,操作系统进行资源调度和分配的基本单位。
进程的实现
中断发生后,操作系统底层的工作步骤
-
硬件压入堆栈程序计数器等
-
硬件从中断向量装入新的程序计数器
-
汇编语言过程保存寄存器值
-
汇编语言过程设置新的堆栈
-
C中断服务例程运行
-
调度程序决定下一个将运行的进程
-
C过程返回至汇编代码
-
汇编语言过程开始运行新的当前进程
进程表:
- 为了实现进程模型,操作系统维护着一张表格(一个结构数组),即进程表。
- 每个进程占用一个进程表项
- 该表项包含进程的重要信息
- 包括程序计数器、堆栈指针、内存分配状况、所打开文件的状态、账号的调度信息,以及其他在进程从运行态转换成就绪态或阻塞态时必须保存的信息,从而保障该进程随后能再次启动,就像从未中断一样
并发与并行
- 单个核心在很短时间内分别执行多个进程,称为并发
- 多个核心同时执行多个进程,称为并行
- 对于并发来说,CPU需要从一个进程切换到另一个进程,这个过程需要保存进程的状态信息
进程的状态与切换
某个进程在某种时刻所处的状态分为以下几种:运行态 就绪态 阻塞态。
对于阻塞状态:比如read系统调用阻塞,进程会占用内存空间,这是一种浪费行为,于是操作系统把阻塞的进程置换到磁盘中,此时进程未占用物理内存,称为挂起。
除了创建和结束有三个状态:
-
运行态:该时刻进程占用CPU
-
就绪态:可运行,由于其他进程处于运行状态而暂时停止运行
-
阻塞态:该进程正在等待某一事件的发生(如等待输入输出的操作完成)而暂时停止运行
-
挂起态:描述进程没有占用实际物理内存空间的情况
-
阻塞挂起状态:进程在外存(硬盘)并等待某个事件的出现
-
就绪挂起状态:进程在外存(硬盘),但只要进入内存,马上运行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kolADi7s-1663501130485)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20220719103344444.png)]
切换:
- 就绪态和运行态可以相互切换,其他的都是单向转换。就绪态的进程通过调度算法从而获得CPU时间,转为运行状态。
- 运行态的进程,在分配给他的时间片用完后就会转为就绪态,等待下一次调度。
- 阻塞态是缺少资源从而从运行态转换而来,但是该资源不包括CPU时间,缺少CPU时间会从运行态转换为就绪态。
- 当进程等待的外部事件发生时(如一些输入到达),则会从阻塞态转为就绪态,如果此时没有其他进程运行,便转换为运行态,否则进程将处于就绪态,等待调度。
进程控制块(PCB)
操作系统对进程的感知,是通过进程控制块PCB数据结构来描述的。他是进程存在的唯一标识,包含以下信息:
- **进程描述信息:**进程标识符、用户标识符等。
- **进程控制和管理信息:**进程状态、进程优先级等。
- **进程资源分配清单:**虚拟内存地址空间信息,打开文件列表,IO设备信息等。
- **CPU相关信息:**当进程切换时,CPU寄存器的值都被保存在相应PCB中,以便CPU重新执行该进程时能从断点处继续执行。
PCB通过链表形式组织起来,比如有就绪队列、阻塞队列等,方便增删、方便进程管理。
进程切换为何比线程慢
涉及到虚拟内存的问题,进程切换涉及虚拟地址的空间切换但线程不会。
每个进程都有自己的虚拟地址空间,而线程是共享所在进程的虚拟地址空间的,所以同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换
把虚拟地址转换为物理地址需要查找页表,页表查找是一个很慢的过程(至少访问两次内存),因此通常使用Cache来缓存常用的地址映射,这样可以加速页表查找,这个Cache就是TLB(快表)
由于每个进程都有自己的虚拟地址空间,所以每个进程都有自己的页表,那么当进程切换后,页表也要切换,页表切换了TLB就失效了,cache失效导致命中率降低,那么虚拟地址转换为物理地址就会变慢,表现出来就是程序运行会变慢,而线程切换则不会导致TLB失效,因为线程切换无需切换地址空间。
守护进程
守护进程是指在后台运行的,没有控制终端与之相连的进程,它独立于控制终端,周期性地执行某种任务。
Linux的大多数服务器就是用守护进程的方式实现的,如web服务器HTTP等。
(创建守护进程过程略)
僵尸进程
多进程程序,父进程一般需要跟踪子进程的退出状态,当子进程退出父进程正在运行,子进程必须等到父进程捕获到了子进程的退出状态才算真正结束,在子进程结束后,父进程读取状态前,此时子进程为僵尸进程。
设置僵尸进程的目的是为了维护子进程的信息,以便父进程在以后某个时候获取。这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait时就可获取这些信息。
但是子进程停止在僵尸态会占据内核资源,所以需要避免僵尸进程的产生或尽快结束子进程的僵尸态。
多进程
进程结构由以下几个部分构成:代码段、堆栈段、数据段。代码是静态的二进制代码,多个程序可以共享。
父进程创建子进程之后,父、子进程除了PID外,其他所有部分基本一模一样。
父、子进程共享全部数据,子进程在写数据时会拷贝一份,之后在拷贝出的数据上进行操作。