一、学习目标
1. 了解异常及其种类
2. 理解进程和并发的概念
3. 掌握进程创建和控制的系统调用及函数使用:fork,exec,wait,waitpid,exit,getpid,getppid,sleep,pause,setenv,unsetenv,
4. 理解数组指针、指针数组、函数指针、指针函数的区别
5. 理解信号机制:kill,alarm,signal,sigaction
6. 掌握管道和I/O重定向:pipe, dup, dup2
二、学习资源
1. 教材:第八章《异常控制流》
2. 课程资料:https://www.shiyanlou.com/courses/413
3. 实验九,课程邀请码:W7FQKW4Y
从给处理器加电开始,直到断电为止,程序计数器假设一个值的序列:
a0,a1,...,a(n-1)
其中,每个ak是某个相应的指令Ik的地址。每次从ak到a(k+1)的过渡称为控制转移。这样的控制转移序列叫做处理器的控制流。
作为程序员,理解ECF很重要:
理解ECF将帮助你理解重要的系统概念
理解ECF将帮助你理解应用程序是如何和操作系统实现交互的
理解ECF将帮助你编写有趣的新应用程序
理解ECF将有助于你理解和开发
理解ECF将帮助你理解软件异常如何工作
异常就是控制流中的突变,用来响应处理器状态中的某些变化。
在异常处理程序完成处理后,根据引起异常的事件的类型,会发生以下以下三种情况中的一种:
处理程序将控制返回给当前指令Icurr,即如果没有发生异常将会执行的下一条指令
处理程序将控制返回给Inext,即如果没有发生异常将会执行的下一条指令
处理程序终止被中断的程序
系统中可能的每种类型的异常都分配了一个唯一的非负整数的异常号。
异常号是到异常表中的索引,异常表的起始地址放在一个叫做异常表基址寄存器的特殊CPU寄存器里。
异常的类别可以分为四类:
中断、陷阱、故障和终止
中断是异步发生的,是来自处理器外部的I/O设备的信号的结果。硬件中断不是由任何一条专门的指令造成的。
陷阱是有意的异常,是执行一条指令的结果。就像中断处理程序一样,陷阱处理程序将控制返回到下一条指令。陷阱最重要的用途是在用户程序和内核之间提供一个像过程一样的接口,叫做系统调用
故障由错误情况引起,它可能能够被故障处理程序修正。当故障发生时,处理器将控制转移给故障处理程序。
终止时不可恢复的致命错误造成的结果,通常是一些硬件上的错误
Linux提供上百种系统调用,在应用程序想要请求内核服务时可以使用,包括读文件、写文件或是创建一个新进程。
异常是允许操作系统提供进程的概念所需要的基本构造块,进程是计算机科学中最深刻最成功的概念之一。
进程提供给应用程序的关键抽象:
即使在系统中通常有许多其他程序在运行,进程也可以向每个程序提供一种假象,好像它在独占地使用处理器。如果想用调试器单步地执行程序,我们会看到一系列的程序计数器(PC)的值,这些值唯一地对应于包含在程序的可执行目标文件中的指令,或者是包含在运行时动态链接到程序的共享对象中的指令。这个PC值的序列叫做逻辑控制流,或者简称逻辑流。
一个逻辑流的执行时间上与另一个流重叠,称为并发流,这两个流被称为并发地运行。
多个流并发地执行的一般现象称为并发。
进程也为每个程序提供一种假象,好像它独占地使用系统地址空间。在一台有n位地址的机器上,地址空间是2n个可能地址的集合,0,1,。。。,2n-1.一个进程为每个程序提供它自己的私有地址空间。一般而言,和这个空间中某个地址相关联的那个存储器字节是不能被其他进程读或者写,从这个意义上说,这个地址空间是私有的。
处理器通常是用某个控制寄存器中的一个模式位来提供这种功能的,该寄存器描述了进程当前享有的特权。当设置了模式位时,进程就运行在内核模式中。一个运行在内核模式的进程可以执行指令集中的任何的指令,并且可以访问系统中任何存储器的位置。
操作系统内核使用一种称作上下文切换的较高的形式的异常控制流来实现多任务。
当Unix系统级函数遇到错误时,它们会典型地返回-1,并设置全局整数变量errno来表示什么出错了。程序员应该总是检查错误,但是不幸的是,许多人都忽略了错误检查,因为它使得代码变得臃肿,而且难以读懂。
通过使用错误处理包装函数,我么可以更进一步地简化我们的代码
每个进程都有一个唯一的整数进程ID。getpid函数返回调用进程的PID.
从程序员的角度看,我们可以认为进程总是处于下面三种状态之一:
P493的例子
当在Unix系统上运行程序时,我们得到下面的结果:
unix>./fork
parent:x=0
child:x=2
这个简单的例子有一些微妙的方面
当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除。相反,进程被保持在一种已终止的状态中,直到被它的父进程回收。当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程,从此时开始,该进程就不存在了。一个终止了但还未被回收的进程称为僵死进程。
1.判断等待集合的成员
2.修改默认行为
3.检查已回收子进程的退出状态
4.错误条件
5.wait函数
sleep函数将一个进程挂起一段指定的时间
如果请求的时间量已经到了,sleep返回0,否则返回还剩下的要休眠的秒数
exexve函数在当前进程的上下文中加载并运行一个新程序
如果成功,则不返回,如果错误,则返回-1
像Unix外壳和Web服务器这样的程序大量使用了fork和execve函数。外壳是一个交互型的应用级程序,它代表用户运行其他程序。
一种高层的软件形式的异常,称为Unix信号,它允许进程中断其他进程。
传送一个信号到目的进程是由两个不同的步骤组成的:
Unix系统提供大量向进程发送信号的机制。所有这机制都是基于进程组这个概念的
下面是默认行为中的一种:
signal函数可以通过下列三种方法之一来改变和信号signum相关联的行为
对于只捕获一个信号并终止的程序来说,信号处理是简单直接的。然而,当一个程序要捕获多个信号时,一些细微的问题就产生了。
不同系统之间,信号处理语义的差异,是Unix信号处理的一个缺陷。
为了处理这个问题,POSIX标准定义了sigaction函数,它允许像Linux和Solaris这样与Posix兼容的系统上的用户,明确地指定他们想要的信号处理语义。
signal包装函数设置了一个信号处理程序,其信号处理语义如下:
应用程序可以使用sigprocmask函数显式地阻塞和取消阻塞信号
C语言提供了一种用户级异常控制流形式,称为非本地跳转,它将控制直接从一个函数转移到另一个当前正在执行的函数,而不需要经过正常的调用。
大量的监控和操作进程的有用的工具
STRACE
PS
TOP
PMAP
异常控制流(ECF)发生在计算机系统的各个层次,是计算机系统中提供并发的基本机制