20135207-信息安全系统设计基础第十一周学习总结
第八章异常控制流
作为程序员,理解ECF(异常控制流)很重要,这有很多原因:
- 理解ECF将帮助你理解重要的系统概念。
- 理解ECF将帮助你理解应用程序是如何与操作系统交互的。
- 理解ECF将帮助你编写有趣的新应用程序。
- 理解ECF将帮助你理解并发。
- 理解ECF将帮助你理解软件异常如何工作。
8.1 异常
·异常就是控制流中的突变,用来影响处理器状态中的某些变化。
·状态变化称为事件。
8.1.1 异常处理
·异常表:当处理器检测到有事件发生时,它会通过跳转表,进行一个间接过程调用(异常),到异常处理程序。
·异常号:系统中可能的某种类型的异常都分配了一个唯一的非负整数的异常号。异常号是到异常表中的索引。
·异常类似于过程调用,但有一些重要的不同之处:
(1)过程调用时,在跳转到处理程序之前,处理器将返回地址压入栈中,然而,根据异常的类型,返回地址要么是当前指令(当事件发生时正在执行的指令)要么是下一条指令(如果事件不发生,将会在当前指令后执行的指令)。
(2)处理器也把一些额外的处理器状态压到栈里,在处理程序返回时,重新开始被中断的程序会需要这些状态。比如,一个A32系统将包含当前条件码和其他内容的EHAGS寄存器压入栈中。
(3)如果控制从一个用户程序转移到内核,那么所有这些项目都被压到内核栈中,而不是压到用户栈中。
(4)异常处理程序运行在内核模式下,这意味着它们对所有的系统资源都有完全的访问权限
- 一旦硬件触发了异常,异常处理程序则由软件完成。
8.1.2 异常的类别
异常可分为四类:中断、陷阱、故障和终止
1.中断
异步是指硬件中断不是由任何一条指令造成的,而是由外部I/O设备的事件造成的。
2.陷阱和系统调用
·系统调用运行在内核模式中,并且可以访问内核中的栈。
3.故障
·一个经典的的故障示例是缺页异常,当指令引用一个虚拟地址,而该虚拟地址相对应的物理页面不在存储器中,因此必须从磁盘中取出时,就会发生故障。
4.终止
终止是不可恢复的致命错误造成的结果,通常是一些硬件错误,比如
DRAM或者SRAM位被损坏时发生的奇偶错误。终止处理程序从不将控
制返回给应用程序。处理程序将控制返回给一个abort例程,该例程会终
止这个应用程序。
8.1.3linux/ia32系统中的异常
1.Linux/IA32故障和终止
·除法错误
·一般保护故障
·缺页
·机器检查
2.Linux/IA32系统调用
8.2 进程
进程提供给应用程序的关键抽象:
·一个独立的逻辑控制流
·一个私有的地址空间
8.2.1 逻辑控制流
·程序计数器(PC)值的序列叫做逻辑控制流,简称逻辑流。
8.2.2并发流
·并发流:并发流一个逻辑流的执行在时间上与另一个流重叠,叫做并行流
·并发:多个流并发执行的一般现象称为并发。
·多任务:多个进程并发叫做多任务。
·并行:并发流在不同的cpu或计算机上,叫做并行。
8.2.3 私有地址空间
·一个进程为每个程序提供它自己的私有地址空间。
8.2.4 用户模式和内核模式
- 运行应用程序代码的进程初始时是在用户模式中的。进程从用户模式变为内核模式的唯一方法是通过异常。
- linux提供了/proc文件系统,它允许用户模式进程访问内核数据结构的内容。
8.2.5上下文切换
·上下文切换:操作系统内核使用叫上下文切换的异常控制流来实现多任务。
- 上下文切换:a)保存当前进程的上下文;b)恢复某个先前被抢占的进程被保存的上下文; c)将控制传递给这个新恢复的进程
- 调度:内核中的调度器实现调度。
8.3 系统调用错误处理491
·当Unix系统级函数遇到错误时,它们典型地会返回―1,并设置全
局整数变量errno来表示什么出错了。程序员应该总是检查错误,但是不幸的是,许多人都忽略了错误检查,因为它使代码变得臃肿,而且难以读懂。
·通过使用错误处理包装函数,我们可以更进一步地简化我们的代码。对于一个给定的基本函数foo,我们定义一个具有相同参数的包装函数Foo,但是第一个字母大写了。包装函数调用基本函数,检查错误,如果有任何问题就终止。
8.4 进程控制
8.4.1 获取进程
8.4.2 创建和终止进程
从程序员的角度,我们可以认为进程总是处于下面三种状态之一:
·运行
·停止
·终止
8.4.3 回收子进程
·当一个进程终止时,内核并不立即把它从系统中清除。相反,进程被
保持在一种已终止的状态中,直到被它的父进程回收。一个终止了但是还未被回收的进程称为僵死进程。
1.判定等待集合的成员
2.修改默认行为
3.检查已回收子进程的退出状态
4.错误条件
5.wait函数
6.使用waitpid的示例
8.4.4让进程休眠
·如果请求的时间量已经到了,sleep返回0,否则返回还剩下的要休眠的秒数。后一种情况是可能的,如果因为sleep函数被一个信号中断而过早地返回。我们将在8.5节中详细讨论信号
·pause函数让调用函数休眠,直到该进程收到一个信号。
8.4.5加载并运行程序
·execve函数加载并运行可执行目标文件filename,且带参数列表
argv和环境变量列表envp。只有当出现错误时,例如找不到filename,execve才会返回到调用程序。所以,与fork一次调用返回两次不同,e
xecve调用一次并从不返回。
8.5 信号
·一种更高层次的软件形式的异常,称为unix信号,它允许进程中断其他进程。
8.5.1 信号术语
传送一个信号到目的进程是由两个步骤组成的
·发送信号。内核通过更新目的进程上下文中的某个状态,发送(递送)一个信号给目的进程。
发送信号可以有如下两种原因:
1)内核检测到一个系统事件。
2)一个进程调用了kill函数,显式地要求内核发送一个信号给目的进程,一个进程可以发送信号给它自己。
·接收信号。当目的进程被内核强迫以某种方式的发送做出反应时,目的进程就接收了信号。进程可以忽略这个信号,终止或者通过执行一个称为信号处理程序的用户层函数不活这个信号。
8.5.2 发送信号
1.进程组
- 每个进程都只属于一个进程组,进程组是由一个正整数进程组ID来标识的。 一个子进程和它的父进程同属于一个进程组,一个进程组可以通过使用setpgid函数来改变自己或者其他进程的进程组。
2.用/bin/kill程序发送信号
3.从键盘发送信号
4.用kill函数发送信号
5.用alarm函数发送信号
8.5.3 接收信号
每个信号类型都有一个预定的默认行为:
·进程终止
·进程终止并转储存储器
·进程停止直到被SIGCONT型号重启
·进程忽略该信号
signal函数可以通过下列三种方法之一来改变和信号signum相关联的行为:
·如果handler是SIG_IGN,那么忽略类型为signum的信号
·如果handler是SIG_DFL,那么类型为signum的信号行为恢复为默认行为
·否则,handler就是用户定义的函数的地址,这个函数成为信号处理程序,只要进程接收到一个类型为signum的信号,就会调用这个程序,通过把处理程序的地址传递到signal函数从而改变默认行为,这叫做设置信号处理程序。
8.5.4 信号处理问题
当一个程序要捕获多个信号时,一些细微的问题就产生了。
·待处理信号被阻塞。
·待处理信号不会排队等待。
·系统调用可以被中断。
8.5.5 可移植的信号处理
Signal包装函数设置的信号处理程序的信号处理语义如下:
·只有这个处理程序当前正在处理的那种类型的信号被阻塞
·和所有信号实现一样,信号不会排队等候
·只要有可能,被中断的系统调用会自动重启。
·一旦设置了信号处理程序,它就会一直保持,知道signal带着handler参数为SIG_IGN或者SIG_DFL被调用。
8.5.6 显式地阻塞和取消阻塞信号
8.6 非本地跳转
·c语言提供了一种用户级异常控制流形式,称为本地跳转。通过setjmp和longjmp函数来提供。
8.7 操作进程的工具
Linux系统提供了大量的监控和操作进程的有用工具:
- STRACE:打印一个正在运行的程序和它的子进程调用的每个系统调用的轨迹。对于好奇的的工具。用-StatiC编译你的程序,能传到一个更干净的、不带学生而言,这是一个令人着迷有大量与共享库相关的输出的轨迹。
- PS:列出当前系统中的进程(包括僵死进程)
- TOP:打印出关于当前进程资源使用的信息。
- PMAP:显示进程的存储器映射。proc:一个虚拟文件系统,以ASCII文本格式输出大量内核数数据结构的内容,用户程序可 cat 2 / proc / load avg” , 观察在Linux系统上的平均负载。
8.8 小结524
- 异常控制流(ECF)发生在计算机系统的各个层次,是计算机系统中提供并发的基本机制在硬件层,异常是由处理器中的事件触发的控制流中的突变。控制流传递给一个软件处理程序,该处理程序进行一些处理,然后返回控制给被中断的控制流。
- 有四种不同类型的异常:中断、故障、终止和陷阱。当一个外部旧设备,例如定时器芯片或者一个磁盘控制器,设置了处理器芯片上的中断引脚时(对于任意指令)中断会异步地发生控制返回到故障指令后面的那条指令。一条指令的执行可能导致故障和终止同时发生故障处理程序会重新启动故障指令,而终止处理程序从不将控制返回给被中断的流。最后,陷阱就像是用来实现向应用提供到操作系统代码的受控的入口点的系统调用的函数调用。
- 在操作系统层,内核用ECF提供进程的基本概念。进程提供给应用两个重要的抽象:(1)逻辑控制流,它提供给每个程序一个假象,好像它是在独占地使用处理器(2)私有地址空间,它提供给每个程序一个假象,好像它是在独占地使用主存。
- 在操作系统和应用程序之间的接口处,应用程序可以创建子进程,等待它们的子进程停止或者终止,运行新的程序,以及捕获来自其他进程的信号。信号处理的语义是微妙的,并且随系统不同而不同。然而,在与POSIX兼容的系统上存在着一些机制,允许程序清楚地指定期望的信号处理语义。
- 最后,在应用层,C程序可以使用非本地跳转来规避正常的调用/返回栈规则,并且直接从一个函数分支到另一个函数。
参考资料
- 教材:第八章,详细学习指导:http://group.cnblogs.com/topic/73069.html
- 课程资料:https://www.shiyanlou.com/courses/413 实验十,课程邀请码:W7FQKW4Y
- 教材中代码运行、思考一下,读代码的学习方法:http://www.cnblogs.com/rocedu/p/4837092.html。