异常是异常控制流的一种形式,它一部分是由硬件实现的,一部分是由操作系统实现的。有一部分是由硬件实现的,所以具体细节将随系统的不同而有所不同。
异常的剖析:
当异常处理程序完成处理后,根据引起异常的事件的类型,会发生以下三种情况中的一种:
处理程序将控制返回给当前指令Icurr,即当事件发生时正在执行的指令。
处理程序将控制返回给Inext,即如果没有发生异常将会执行的下一条指令。
*处理程序终止被中断的程序。
异常的类别——中断、陷阱、故障和终止
中断处理:异步是指硬件中断不是由任何一条指令造成的,而是由外部I/O设备的事件造成的。
陷阱和系统调用:系统调用是一些封装好的函数,内部通过指令int n实现。
陷阱最重要的用途是提供系统调用。系统调用运行在内核模式中,并且可以访问内核中的栈。
系统调用的参数是通过通用寄存器而不是栈来传递的,如,%eax存储系统调用号,%ebx,%ecx,%edx,%esi,%edi,%ebp最多存储六个参数,%esp不能用,因为进入内核模式后,会覆盖掉它。
故障
一个经典的的故障示例是缺页异常,当指令引用一个虚拟地址,而该虚拟地址相对应的物理页面不在存储器中,因此必须从磁盘中取出时,就会发生故障。
终止
终止是不可恢复的致命错误造成的结果,通常是一些硬件错误,比如DRAM或者SRAM位被损坏时发生的奇偶错误。终止处理程序从不将控制返回给应用程序。处理程序将控制返回给一个abort例程,该例程会终止这个应用程序。
见书P491
- 每个进程都有一个唯一的正数(非零)进程 ID (PID). getpid 函数返回调用进程的 PID。getppid 画数返回它的父进程的 PID (创建调用进程的进程〉。
- getpid getppid 函数返回一个类型为 pid_t 的整数值,在 Linux 系统上它在 types.h中被定义为 int。
进程总是处于下面三种状态之-:
- 运行。进程要么在CPU上执行,要么在等待被执行且最终会被内核调度。
- 停止。进程的执行被挂起(suspend),且不会被调度。当收到 SIGSTOP、SIGTSTP、SIDTTN或者 SIGTTOU 信号时,进程就停止,并且保持停止直到它收到一个 SIGCONT
信号,在这个时刻,进程再次开始运行。- 终止。进程永远地停止了。进程会因为三种原因终止:
1) 收到一个信号,该信号的默认行为是终止进程。
2) 从主程序返回。
3) 调用 exit 函数。fork函数的特点
- 调用一次,返回两次
- 并发执行
- 相同但是独立的地址空间
- 共享文件
- 当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除。相反,进程被保持在一种己终止的状态中,直到被它的父进程回收 (reap)。当父进程回收已终止的子进程时,内核
将子进程的退出状态传递给父进程,然后抛弃已终止的进程,从此时开始,该进程就不存在了。一个终止了但还未被回收的进程称为僵死进程 (zombie)。- waitpid函数
- 判断等待集合的成员
等待集合的成员是由参数 pid 来确定的:
- 如果 pid>0,那么等待集合就是一个单独的子进程,它的进程lD等于 pid
- 如果 pid = -1 ,那么等待集合就是由父进程所有的子进程组成的。
- 修改默认行为
可以通过将 optioins 设置为常量 WNOHANG WUNTRAα 的各种组合,修改默认行为:
- WNOHANG: 如果等待集合中的任何子进程都还没有终止,那么就立即返回(返回值为0)。默认的行为是挂起调用进程,直到有子进程终止。在等待子进程终止的同时,如果还想做些有用的工作,这个选项会有用。
- WUNTRACED :挂起调用进程的执行,直到等待集合中的一个进程变成已终止或者被停止。返回的 PID 为导致返回的己终止或被停止子进程的 PID。默认的行为是只返回己终止的子进程。当你想要检查已终止和被停止的子进程时,这个选项会有用。
- WNOHANG UNTRACED: 立即返回,如果等待集合中没有任何子进程被停止或已终止,那么返回值为 ,或者返回值等于那个被停止或者己终止的子进程的 PID
- 检查已回收子进程的退出状态
如果 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) 返回为真时,才定义这个状态。
- 错误条件
- 如果调用进程没有子进程,那么waitpid返回-1,并且设置 errno为ECHILD。如果waitpid函数被一个信号中断,那么它返回一1,并设置 errno为EINTR
- wait函数
- wait函数是waitpid函数的简单版本。
- 调用 wait(&status) 等价于调用 waitpid(-l &status , 0)
- sleep函数将一个进程挂起一段指定的时间。
- exceve函数在当前进程的上下文中加载并运行一个新程序
- execve 函数加载并运行可执行目标文件filename,且带参数列表 argv和环境变量列表envp。
在操作系统和应用程序之间:进程之间传送信号
一种更高层次的软件形式的异常,称为unix信号,它允许进程中断其他进程。
发送信号:/bin/kill , kill函数,键盘,alarm函数
用kill函数发送信号:发送SIGKILL信号
用alarm函数发送信号:发送SOGALARM信号
进程可以通过使用signal函数来修改和信号相关的默认行为。唯一的例外是SIGSTOP和SIGKILL,它们的默认行为不能被修改。
18
每个进程都只属于一个进程组。
进程组是由一个正整数进程组ID来标识的。
getpgrp函数返回当前进程组id
setpgid函数修改自己或其他进程组
/bin/kill程序可以向另外的进程发送任意的信号,比如 /bin/kill -9 15213
即为:发送信号9给进程15213
在任何时刻,至多只有一个前台作业和0个或多个后台作业。外壳为每个作业创建一个独立的进程组,一个作业对应一个进程组。
图19
发送SIGKILL信号
发送SOGALARM信号
当内核从一个异常处理程序返回,准备将控制传递给进程P时,他会检查进程P的未被阻塞的处理信号的集合。如果这个集合为空,那么内核将控制传递到P的逻辑控制流中的下一条指令;如果集合是非空的,那么内核选择集合中的某个信号K(通常是最小的K0,并且强制P接收信号K。收到这个信号会触发进程的某种行为。一旦进程完成了这个行为,那么控制就传递回P的逻辑控制流中的下一条指令。
进程终止
进程终止并转储存储器
进程停止直到被SIGCONT型号重启
进程忽略该信号
当一个程序要捕获多个信号时,一些细微的问题就产生了。
待处理信号被阻塞。Unix信号处理程序通常会阻塞当前处理程序正在处理的类型的待处理信号。
待处理信号不会排队等待。任意类型至多只有一个待处理信号。因此,如果有两个类型为K的信号传送到一个目的进程,而由于目的进程当前正在执行信号K的处理程序,所以信号K时阻塞的,那么第二和信号就简单地被简单的丢弃,他不会排队等待。
系统调用可以被中断。像read、wait和accept这样的系统调用潜在地会阻塞进程一段较长的时间,称为慢速系统调用。在某些系统中,当处理程序捕获到一个信号时,被中断的慢速系统调用在信号处理程序返回时不再继续,而是立即返回给用户一个错误的条件,并将errno设置为EINTR。
Signal包装函数设置的信号处理程序的信号处理语义:
只有这个处理程序当前正在处理的那种类型的信号被阻塞
和所有信号实现一样,信号不会排队等候
只要有可能,被中断的系统调用会自动重启。
一旦设置了信号处理程序,它就会一直保持,知道signal带着handler参数为SIGIGN或者SIGDFL被调用。
20
以某种方式同步并交流,从而得到最大的可行的交错的集合,每个可行的交错都能得到正确的结果。
- C语言提供了一种用户级异常控制流形式,称为非本地跳转,它将控制直接从一个函数转移到另一个当前正在执行的函数,而不需要经过正常的调用-返回序列。非本地跳转是通过setjmp和longjmp函数来提供的。
- Linux 系统提供了大量的监控和操作进程的有用工具:
- STRACE:打印一个正在运行的程序和它的子进程调用的每个系统调用的轨迹。
- PS: 列出当前系统中的进程(包括僵死进程)。
- TOP: 打印出关于当前进程资源使用的信息。
- PMAP: 显示进程的存储器映射。
- /proc: 一个虚拟文件系统,以ASCII文本格式输出大量内核数据结构的内容,用户程序可读取这些内容。
原文:http://www.cnblogs.com/hw00332012/p/4986338.html