异常与进程

一.控制流

(1)什么是控制流

  • 从加电开始工作,到断电停止工作,CPU就是读并执行(解释)指令序列, 一次一条指令
  • 这个序列就是处理器控制流(CPU’s control flow or flow of control)

在这里插入图片描述

(2)两种机制可改变控制流

  • 分支与跳转 Jumps and branches
  • 调用与返回 Call and return
  • 上述两者均是相应程序状态的变化(分支,循环,子程序调用)

(3)异常控制流

在这里插入图片描述

  • 系统需要一种机制来处理上述“异常控制流(exceptional control flow)”—— 控制流的突变

在这里插入图片描述

二.异常

(1)异常

在这里插入图片描述
处理器状态中的变化(事件)触发 从应用程序到异常处理程序的突发的控制转移 ( 异常)。在异常处理程序完成处理后,它将控制 返回给被中断的程序或者终止
当异常处理程序完成处理后,根据引起异常的事件的类型,会发生以下三种情况中的一种:

  1. 处理程序将控制返回给当前指令 Icurr 即当事件发生时正在执行的指令。
  2. 处理程序将控制返回给 Inext 即如果没有发生异常将会执行的下一条指令。
  3. 处理程序终止被中断的程序。

(2)异常表

  • 在系统启动时(当计算机重启或者加电时),操作系统分配和初始化一张称为异常表的跳转表,使得条目 k包含异常 k 的处理程序的地址。在运行时(当系统在执行某个程序时),处理 器检测到发生了一个事件,并且确定了相应的异 常号 K。随后,处理器触发异常,方法是执行间接过程调用,通过异常表的条目 k 转到相应的处理程 序。

  • 异常号是到异常表中 的索引,异常表的起始地址放在一个叫做异常表 基址寄存器 (exception table base register) 的特殊 CPU 寄存器里。

在这里插入图片描述
在这里插入图片描述

  • 过程调用时,在跳转到处理程序之前,处理器将返回地址压入栈中。然而,根据异常的类 型,返回地址要么是当前指令(当事件发生时正在执行的指令),要么是下一条指令(如 果事件不发生,将会在当前指令后执行的指令)。
  • 处理器也把一些额外的处理器状态压到栈里,在处理程序返回时,重新开始被中断的程序 会需要这些状态。比如,一个 IA32 系统将包含当前条件码和其他内容的 EFLAGS 寄存器 压人栈中。
  • 如果控制从一个用户程序转移到内核,那么所有这些项目都被压到内核栈中,而不是压到 用户栈中。
  • 异常处理程序运行在内核模式下,这意味着它们对所有的系统资源都有完全 的访问权限。
  • 一旦硬件触发了异常,剩下的工作就是由异常处理程序在软件中完成。在处理程序处理完事件之 后,它通过执行一条特殊的“从中断返回”指令,可选地返回到被中断的程序,该指令将适当的 状态弹回到处理器的控制和数据寄存器中,如果异常中断的是一个用户程序,就将状态恢复为用 户模式,然后将控制返回给被中断的程序。

常见的异常如下:

在这里插入图片描述

(3)异常的分类

异常可以分为四类:中断 (interrupt)、陷阱 (trap)、故障 (fault) 和终止 (abort)。图 8-4 中的表对这些类别的属性做了小结

在这里插入图片描述

  • 除法错误。当应用试图除以零时,或者当一个除法指令的结果对于目标操作数来说太大了 的时候,就会发生除法错误(异常 0) 。 Unix 不会试图从除法错误中恢复,而是选择中止程序。 Linux 外壳通常会把除法错误报告为“浮点异常" (Floating exception) 。
  • 一般保护故障。许多原因都会导致不为人知的一般保护故障(异常 13), 通常是因为一个程 序引用了一个未定义的虚拟存储器区域,或者因为程序试图写一个只读的文本段。 Linux 不会尝 试恢复这类故障。 Linux 外壳通常会把这种一般保护故障报告为“段故障" (Segmentation fault) 。
  • 缺页(异常 14) 是会重新执行产生故障的指令的一个异常示例。处理程序将磁盘上物理存 储器相应的页面映射到虚拟存储器的一个页面,然后重新开始这条产生故障的指令。我们将在第 9 章中看到缺页是如何工作的细节。 机器检查。
  • 机器检查(异常 18) 是在导致故障的指令执行中检测到致命的硬件错误时发生 的。机器检查处理程序从不返回控制给应用程序。
1中断

来自处理器外部的事件引发 :

  • 向处理器芯片上的中断引脚发信号,异常号传送到系统总线,触发中断
  • 中断处理程序返回到下一条指令

例如:
 I/O 中断:

  • 用户在键盘上使用 Ctrl-C 组合键
  • 来自网络的一个数据包到达
  • 来自硬盘的数据到达

 硬复位中断 :触发reset按钮
 软复位中断 :使用 Ctrl-Alt-Delete组合键在这里插入图片描述

2陷阱

一种有意产生的异常

  • 例如:系统调用, 断点陷阱,其他特殊指令
  • 将控制返回到“下一条”指令

在这里插入图片描述举例:
在这里插入图片描述
在这里插入图片描述

3故障

非有意产生、可能可恢复的异常
例如 :

  • 页故障 (可恢复), 保护故障(不可恢复), 浮点异常
  • 控制返回:或者再次执行“当前”指令或者终止

在这里插入图片描述
举例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4终止

非有意产生,且不可恢复

  • 例如: 奇偶校验错误,机器校验
  • 终止当前程序

在这里插入图片描述
在这里插入图片描述

(4)系统调用

Linux 提供上百种系统调用,当应用程序想要请求内核服务时可以使用,包括读文件、写文 件或是创建一个新进程。图 8-10 给出了一些常见的 Linux 系统调用。每个系统调用都有一个唯 一的整数号,对应于一个到内核中跳转表的偏移量。
在这里插入图片描述

三. 进程

(1)进程的定义

  • 进程的经典定义就是一个执行中的程序的实例。
  • 上下文:系统中的每个程序都是运行在某个进程的上下文 (context) 中的,上下文是由程序正确运行所需的状态组成的。这个状态包括存放在存储器 中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件 描述符的集合。
  • 每次用户通过向外壳输入一个可执行目标文件的名字,并运行一个程序时,外壳就会创建一 个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。应用程序也能够创建新进 程,且在这个新进程的上下文中运行它们自己的代码或其他应用程序。

(2)进程为每个程序提供了两个关键抽象 :

  • 逻辑控制流 Logical control flow :每个程序看起来是独占使用处理器的
  • 私有地址空间 Private virtual address space : 每个程序看起来是独占使用主存系统

(3)并发进程

两个进程同时/并发(concurrently)运行,二者控制流的执行是交错的

  • 进程之间存在有序性
  • 示例 (在单核上执行):
  • 并发执行: A & B, A & C
  • 有序地相继执行: B & C

在这里插入图片描述

  • 用户角度看 并发进程:并发进程的控制流在时间上是不相交的 ,但可将并发进程视为彼此之间并行执行

(4)上下文切换

  • 内核为每个进程维持一个上下文 (contc:,xt)。上下文就是内核重新启动一个被抢占的进程所 需的状态。它由一些对象的值组成,这些对象包括通用目的寄存器、浮点寄存器、程序计数器、 用户栈、状态寄存器、内核栈和各种内核数据结构,比如描绘地址空间的页表、包含有关当前进 程信息的进程表,以及包含进程已打开文件的信息的文件表。

调度
在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占的进 程。这种决定就叫做调度 (schedule) , 是由内核中称为调度器 (scheduler) 的代码处理的。当 内核选择一个新的进程运行时,我们就说内核调度了这个进程,在内核调度了一个新的进程运 行后,它就抢占当前进程,并使用一种称为上下文切换的机制来将控制转移到新的进程,上下文切换 :

  1. 保存当前进程的上下文,
  2. 恢复某个先前被抢占的进程被保存的上下文
  3. 将控 制传递给这个新恢复的进程。

当内核代表用户执行系统调用时,可能会发生上下文切换。如果系统调用因为等待某个事件 发生而阻塞,那么内核可以让当前进程休眠,切换到另一个进程

在这里插入图片描述

(5)创建新进程fork()函数

  • 调用一次,返回两次。 fork 函数被父进程调用一次,但是却返回两次——一次是返回到 父进程,一次是返回到新创建的子进程。
  • 并发执行。父进程和子进程是并发运行的独立进程。内核能够以任意方式交替执行它们的 逻辑控制流中的指令。不能对不同进程中指令的交替执行做任何假设。
  • 相同的但是独立的地址空间。每个进程有相同的用户栈、相 同的本地变量值、相同的堆、相同的全局变量值,以及相同的代码。父进程和子进程是独立的进程,它们都有自己的私有地址空间。父进程和子进程对 x 所做的任何改变都是独立的,不会反映在另一个进程的存储器中。
  • 父进程中, fork 返回子进程的 PID。
  • ==在子进程中, fork 返回 0。==因为子进程的 PID 总是非零的,返回值就提供一个明确的方 法来分辨程序是在父进程还是在子进程中执行。

进程图绘制方法

(1) 遇到fork函数就分支,每个水平的箭头对应于从 左到右执行指令的进程,而每个垂直的箭头对应于 fork 函数的执行
(2) 分支的时候专注于父进程执行完,再处理另一个分支

(3) 父进程中, fork 返回子进程的 PID、在子进程中, fork 返回 0
(4) 在新的进程中可以认为执行的是上一个fork以下的部分。
(5)对于执行过程,由于程序不会按照特定的顺序回收子进程,子进程回收的顺序是这台特定的计算机的属性。所以这个输出结果不是确定的

举例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(6)结束进程exist()

结束一个进程 , 通常返回状态 0

在这里插入图片描述

(7)僵死进程

当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除。相反,进程被保持在 一种已终止的状态中,直到被它的父进程回收 (reap)。当父进程回收己终止的子进程时,内核 将子进程的退出状态传递给父进程,然后抛弃巳终止的进程,从此时开始,该进程就不存在了。 一个终止了但还未被回收的进程称为僵死进程 (zombie)

回收

  • 由父进程来回收已经终止的子进程
  • 给父进程提供相关终止状态信息
  • 内核丢弃进程

如果父进程没有回收,怎么办?

  • 如果在回收子进程之前,父进程均终止了,那么则由 init 进程来回收子进程
  • 即,需要显式地回收长期运行的进程
  • 例如, shells 与 servers

(8)与子进程同步waitpid()

一个进程可以通过调用 waitpid 函数来等待它的子进程终止或者停止。函数原型如下:

#include <sys/types. h> 
#include <sys/wait. h> 
pid_t waitpid(pid_t pid, int *Status, int options);
  • 返回:如果成功,则为子进程的 PID
  • 如果 WNOHANG, 则为 0
  • 如果其他错误,则为 -1

1. 判定等待集合的成员
等待集合的成员是由参数 pid 来确定的:

  • 如果 pid > o, 那么等待集合就是一个单独的子进程,它的进程 ID 等于 pid。
  • 如果 pid = -1, 那么等待集合就是由父进程所有的子进程组成的。

2. 修改默认行为2. 修改默认行为
可以通过将 optioins 设置为常量WNOHANG 和 Will叮凡.CED 的各种组合,修改默认行为:

  • WNOHANG: 如果等待集合中的任何子进程都还没有终止,那么就立即返回(返回值为 0)。默认的行为是挂起调用进程,直到有子进程终止。在等待子进程终止的同时,如果还 想做些有用的工作,这个选项会有用
  • WUNTRACED : 挂起调用进程的执行,直到等待集合中的一个进程变成己终止或者被停 止。返回的 PIO 为导致返回的已终止或被停止子进程的 PID。默认的行为是只返回己终止 的子进程。当你想要检查己终止和被停止的子进程时,这个选项会有用。
  • WNOH心GIWUNTRACED : 立即返回,如果等待集合中没有任何子进程被停止或已终止, 那么返回值为 0, 或者返回值等于那个被停止或者已终止的子进程的 PID。

3. 检查己回收子进程的退出状态
如果 status 参数是非空的,那么 waitpid 就会在 status 参数中放上关于导致返回的子 进程的状态信息。 wait.h 头文件定义了解释 status 参数的几个宏:

  • WIFEXITED (status) : 如果子进程通过调用 exit 或者一个返回 (return) 正常终止, 就返回真。
  • WEXITSTATUS (status) : 返回一个正常终止的子进程的退出状态。只有在 WIFEXITED 返回为真时,才会定义这个状态。
  • WIFSIGNALED (status): 如果子进程是因为一个未被捕获的信号终止的,那么就返回 真
  • WTERMSIG (status汃返回导致子进程终止的信号的数量。只有在 WIFSIGNALED (status) 返回为真时,才定义这个状态。
  • WIFSTOPPED (status) : 如果引起返回的子进程当前是被停止的,那么就返回真。 • WSTOPSIG (status): 返回引起子进程停止的信号的数量。只有在 WIFSTOPPED (status) 返回为真时,才定义这个状态。

4 错误条件

-如果调用进程没有子进程,那么 waitpid 返回 -1, 并且设置 errno 为 ECHILD。

  • 如果 waitpid 函数被一个信号中断,那么它返回 -1, 并设置 errno 为 EINTR。

(9)execve 加载并运行程序

函数原型:

int execve ( char *filename, char *argv[ ], char *envp[ ] )

在这里插入图片描述在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值