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 实验九,课程邀请码:W7FQKW4Y
3. 教材中代码和习题中代码运行、思考一下,读代码的学习方法见这。
1. 阅读教材,完成课后练习(书中有参考答案)
2. 考核:练习题把数据变换一下
3. 加分题:课后作业最多两人一组,互相不能重复,1星题目每人最多加一分,2星题目每人最多加二分,3星题目每人最多加三分,4星题目每人最多加四分。
从处理器运行开始到结束,程序计数器假设一个序列的值a0a1......an-1,这个控制转义序列叫做处理器的控制流,异常,就是控制流中的突变,用来响应处理器状态中的某些变化。
状态的变化称为事件,在任何情况下,当处理器检测到有事件发生时,会通过一张叫做异常表的跳转表,进行一个间接过程调用到专门处理程序——异常处理程序。当异常处理程序完成之后,根据引起引起异常的事件类型,会发生以下三种情况之一:
异常处理
系统中可能的每种异常都被分配了唯一一个非负整数的异常号,异常表中的条目k中包含异常k的处理程序地址。异常表的起始地址存放在一个叫做异常表基址寄存器的特殊寄存器中。
异常类和过程调用的不同之处:
异常的类别
1.中断
中断是异步发生的,是来自处理器外部的I/O设备的信号的结果。因此是异步的。硬件中断的异常处理程序通常称为中断处理程序。
其余异常类型都是同步发生的,是执行当前指令的结果。这一类指令称为故障指令。
2.陷阱
陷阱是有意的异常,最重要的用途是在用户程序和内核之间提供一个向过程一样的接口,叫做系统调用。
为了允许内核服务的受控访问,使用“syscall n”指令,跳转到一个异常处理程序的陷阱,处理程序对参数解码并调用适当的内核程序。
3.故障
故障由错误情况引起,可能能够被故障处理程序修正。故障发生时,处理器将控制转移给故障处理程序,若能修正,则将控制返回到引起故障的指令,重新执行;若不能修正,处理程序返回abort例程,终止引起故障的应用程序。
4.终止
终止是不可恢复的致命错误造成的结果,通常是硬件错误。终止处理程序将控制直接返回给abort例程,直接终止该应用程序。
Linux/A32系统中的异常
Linux/A32故障和终止
Linux/A32系统调用
每个系统调用都对应着唯一的整数号,对应于一个到内核中跳转表的偏移量。
IA32系统调用是通过一条称为 int n 的陷阱指令提供的。
C程序通过syscall函数可以直接调用任何系统调用。
所有Linux系统调用都是通过通用寄存器而不是栈传递的,%eax包含系统调用号,%ebx、%ecx、%edx、esi%、%edi和%ebp包含最多6个参数。栈指针%esp不能使用,因为当进入内核模式时,内核会覆盖它。
进程的经典定义:一个执行中的程序的实例。
系统中的每个程序都是运行在某个进程的上下文中的。
上下文:由程序正确运行所需的状态组成的。
进程提供给应用程序的关键抽象:
一个独立的逻辑控制流:独占的使用处理器
一个私有的地址空间:独占的使用存储器系统
1.逻辑控制流
(1)含义:
一系列的程序计数器PC的值,分别唯一的对应于包含子啊程序的可执行目标文件中的指令,或者是包含在运行时动态链接到程序的共享对象中的指令,这个PC值的序列就叫做逻辑控制流。
(2)运行:
进程轮流使用处理器,每个进程执行流的一部分,然后被抢占(暂时挂起)。每个进程执行它的流的一部分,然后被抢占,然后轮到其他进程。但是进程可以向每个程序提供一种假象,好像它在独占的使用处理器。
(3)示例:
异常处理程序、进程、信号处理程序、线程、Java进程
2.并发流
(1)含义
一个逻辑流的执行在时间上与另一个流重叠。【与是否在同一处理器无关】
这两个流并发的运行。
(2)几个概念
并发:多个流并发的执行
多任务:一个进程和其他进程轮流运行(也叫时间分片)
时间片:一个进程执行它的控制流的一部分的每一时间段
(3)并行
两个流并发的运行在不同的处理机核或者计算机上。
并行流并行的运行,并行的执行。
3.私有地址空间
进程为程序提供的假象,好像它独占的使用系统地址空间。一般而言,和这个空间中某个地址相关联的那个存储器字节是不能被其他进程读写的。
4.用户模式和内核模式
处理器通常是用某个控制寄存器中的一个模式位来提供这种功能的,该寄存器描述了进程当前享有的特权。
当设置了模式位时,进程就运行在内核模式中(超级用户模式)。
没有设置模式位时,进程就运行在用户模式中。
进程从用户模式变为内核模式的唯一方法是通过中断、故障或者陷入系统调用这样的异常实现的。
Linux提供/proc文件系统,允许用户模式进程访问内和数据结构的内容。
5.上下文切换
上下文:内核重新启动一个被抢占的进程所需的状态
操作系统内核使用上下文切换的较高层形式的异常控制流来实现多任务。
内核为每个进程维持一个上下文。
在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占的进程,成为调度。
由内核中成为调度器的代码处理的。
使用上下文切换的机制来控制转移到新的进程
保存当前进程的上下文
恢复某个先前被抢占的进程被保存的上下文
将控制传递给这个新恢复的进程
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void); 返回调用进程的PID
pid_t getppid(void);返回父进程的PID(创建调用进程的进程)
getpid和getppid函数返回一个类型为pid_t的整数值,在Linux系统上它在types.h中被定义为int
进程总是处于下面三种状态之一
终止:进程永远停止
终止原因:
父进程通过调用fork函数创建一个新的运行子进程:
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
父进程和新创建的子进程之间最大的区别:
fork函数只被调用一次,却返回两次。
fork函数的并发执行
fork函数的相同的但是独立的地址空间
fork函数的共享文件
调用fork函数n次,产生2的n次方个进程
一个进程可以通过调用waitpid函数来等待它的子进程终止或者停止。
判定等待集合的成员
修改默认行为
可以通过将optioins设置为常量WNOHANG和WUNTRACED的各种组合。
WNOHANG:如果等待集合中的任何子进程都还没有终止,那么就立即返回(返回值为0)。默认的行为是挂起调用进程,直到有子进程终止。
WUNTRACED:挂起调用进程的执行,直到等待集合中的一个进程变成已终止或者被停止。返回的PID为导致返回的已终止或被停止子进程的PID。默认的行为是只返回已终止的子进程。
WNOHANG|WUNTRACED:i级返回如果等待集合中没有任何子进程被停止或已终止的子进程。检查已终止和被停止的子进程时,该选项会有用。
检查已回收子进程的退出状态
wait.h头文件定义了结束status参数的几个宏:
错误条件
sleep函数使一个进程挂起一段指定的时间。定义如下:
#include <unistd.h>
unsigned int sleep(unsigned int secs);
返回值是剩下还要休眠的秒数,如果到了返回0.
#include <unistd.h>
int pause(void);
让调用函数休眠,直到该进程收到一个信号。
#include <unistd.h>
int execve(const char *filename, const char *argv[], const char *envp[]);
成功不返回,失败返回-1.
execve函数调用一次,从不返回。
#include <stdlib.h>
char *getenv(const char *name);
若存在则为指向name的指针,无匹配是null
在环境数组中搜寻字符串"name=value",如果找到了就返回一个指向value的指针,否则返回null。
#include <stdlib.h>
int setenv(const char *name, const char *newvalue, int overwrite);
若成功返回0,错误返回-1
void unsetenv(const char *name);
无返回值
如果环境数组包含"name=oldvalue"的字符串,unsetenv会删除它,setenv会用newvalue代替oldvalue,只有在overwrite非零时成立。
如果name不存在,setenv会将"name=newvalue"写进数组。
※fork函数和execve函数的区别
fork函数是创建新的子进程,是父进程的复制体,在新的子进程中运行相同的程序,父进程和子进程有相同的文件表,但是不同的PID
execve函数在当前进程的上下文中加载并运行一个新的程序,会覆盖当前进程的地址空间,但是没有创建一个新进程,有相同的PID,继承文件描述符。
Unix信号:更高层的软件形式的异常允许进程中断其他进程。
传递一个信号到目的进程的两个步骤:
发送信号的原因:
一个进程可以发送信号给它自己。
接收信号:
待处理信号:
blocked:被阻塞信号集合。
进程组
用/bin/kill程序发送信号
/bin/kill程序可以向另外的进程发送任意的信号,格式是:
当n<0时,使信号|n|发送到进程组m中的所有进程。
从键盘发送信号
用kill函数发送信号
用alarm函数发送信号
接收信号
信号类型的预定义的默认行为:
signal函数通过下列三种方法来改变和信号signum相关联的行为:
* 待处理信号被阻塞
* 带处理信号不会排队等待
* 系统调用可以被中断
* 不可以用信号来对其他进程中发生的事件计数
Signal包装函数设置了一个信号处理程序,其信号处理语义如下:
sigprocmask函数改变当前已阻塞信号的集合,具体行为依赖于how值:
用户级的异常控制流形式,通过setjmp和longjmp函数提供。
setjump函数在env缓冲区中保存当前调用环境,以供后面longjmp使用,并返回0.
调用环境:程序计数器,栈指针,通用目的寄存器
longjmp函数从env缓冲区中恢复调用环境,然后触发一个从最近一次初始化env的setjmp调用的返回。然后setjmp返回,并带有非零的返回值retval。
setjmp函数只被调用一次,但返回多次;
longjmp函数被调用一次,但从不返回。
原文:http://www.cnblogs.com/20135213lhj/p/4986881.html