异常控制流
-
异常
异常是异常控制流的一种形式,它一部分由硬件实现,一部分由操作系统实现。异常就是控制流中的突变,用来响应处理器状态中的某些变化。
异常可以分为四类:中断、陷阱、故障、终止。
陷阱最重要的用途是在用户程序和内核之间提供一个像过程一样的接口,叫做系统调用。
C程序用syscall函数可以直接调用任何系统调用。在x86-64系统上,系统调用是通过一条称为syscall的陷阱指令来提供的。
2.异常处理
系统中可能的类型的异常分配了一个唯一的非负整数的异常号。
处理器的设计者:被除零、缺页、存储器访问违例、断点以及算数溢出。
操作系统内核的设计者分配的:系统调用和来自意外不I/O设备的信号。
异常号:到异常表中的索引
异常表基址寄存器:异常表的起始地址存放的位置。
异常与过程调用的异同:
过程调用时,在跳转到处理器之前,处理器将返回地址压入栈中。然而,根据异常的类型,返回地址要么是当前指令,要么是下一条指令。
处理器把一些额外的处理器状态压入栈里,在处理程序返回时,重新开始被中断的程序会需要这些状态。
如果控制从一个用户程序转移到内核,那么所有这些项目都被压到内核栈中,而不是压到用户栈中。
异常处理程序运行在内核模式下,意味着它们对所有的系统资源都有完全的访问权限。
3.异常的类别
中断:异步发生,是来自处理器外部的I/O设备的信号的结果。
硬件异常中断处理程序通常称为中断处理程序。
异步异常是有处理器外部的I/O设备中的时间产生的,同步异常是执行一条指令的直接产物。
陷阱、故障、终止时同步发生的,是执行当前指令的结果,我们把这类指令叫做故障指令。
陷阱和系统调用:
陷阱最重要的用途是在用户程序和内核之间提供一个像过程一样的接口,叫做系统调用。
普通的函数运行在用户模式中,用户模式限制了函数可以执行的指令的类型,而且它们只能访问与调用函数相同的栈。系统调用运行在内核模式中,内核模式允许系统调用执行指令,并访问定义在内核中的栈。
陷阱是有意的异常,最重要的用途是在用户程序和内核之间提供一个向过程一样的接口,叫做系统调用。
为了允许内核服务的受控访问,使用“syscall n”指令,跳转到一个异常处理程序的陷阱,处理程序对参数解码并调用适当的内核程序。
故障:是由错误情况引起的。
例如:abort例程会终止引起故障的应用程序。 根据故障是否能够被修复,故障处理程序要么重新执行引起故障的指令,要么终止。例如:缺页故障。
终止:是不可恢复的致命错误造成的结果,通常是一些硬件错误。
终止处理程序从不将控制返回给应用程序。
终止处理程序将控制直接返回给abort例程,直接终止该应用程序。
进程和并发
进程提供给应用程序的关键抽象:
- 一个独立的逻辑控制流,它提供一个假象,好像我们的程序独占地使用处理器。
- 一个私有的地址空间,它提供一个假象,好像我们的程序独占地使用内存系统。
一个逻辑流的执行在时间上与另一个流重叠,称为并发流。多个流并发地执行的一般现象被称为 并发 ,一个进程和其他进程轮流运行的概念称为 多任务 ,一个进程执行它的控制流的一部分的每一时间段叫做 时间片 ,多任务也叫做 时间分片 。
并发流:两个流在时间上重叠,即使它们是运行在同一个处理器上;
并行流:是并发流的一个真子集,如果两个流并发地运行在 不同的 处理器核或者计算机上。
地址空间底部是保留给用户程序的,包括通常的代码、数据、堆和栈段。
代码段总是从地址0x400000开始。地址空间顶部保留给内核。
地址空间的这个部分包含内核在代表进程执行指令时使用的代码、数据和栈。
用户程序必须通过系统调用接口间接地访问内核代码和数据。
调度是由内核中称为调度器的代码处理的。在内核调度了一个新的进程运行后,它就抢占当前进程,并使用一种称为上下文切换的机制来将控制转移到新的进程。
- 1.保存当前进程的上下文,
- 2.恢复某个先前被抢占的进程被保存的上下文,
- 3.将控制传递给这个新恢复的进程。
进程创建和控制的系统调用及函数
1.getpid和getppid
每个进程都有一个唯一的正数(非零)进程ID(PID).getpid函数返回调用进程的PID。getppid函数返回它的父进程的PID(创建调用进程的进程)。getpid和getppid函数返回一个类型为pid_t的整数值,在Linux系统上它在types.h中被定义为int。
2.exit
从程序员的角度,我们可以认为进程总是处于下面三种状态之一:运行、停止、终止。
进程会因为三种原因终止:1.收到一个信号,该信号的默认行为是终止进程,2.从主程序返回,3.调用exit函数。
exit函数以status退出状态来终止进程。
3.wait
调用wait(&status)等价于调用waitpid(-1,&status,0)。
4.fork
调用一次,返回两次。一次返回到父进程,一次返回到新创建的子进程。
并发执行。父进程和子进程是并发运行的独立进程。
相同但是独立的地址空间。
共享文件。
5.exec
在使用exec函数族时,一定要加上错误判断语句。因为exec很容易执行失败,其中最常见的原因有:
- 找不到文件或路径,此时errno被设置为ENOENT。
- 数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT。
- 没有对用可执行文件的运行权限,此时errno被设置为EACCES。
数组指针、指针数组、函数指针、指针函数的区别
数组指针是一个指针,该指针指向的是一个数组;
指针数组是一个数组,数组的元素保存的是指针;
函数指针是一个指针,该指针指向一个函数;
指针函数是一个函数,该函数返回的是一个指针。
信号机制
进程组:每个进程都只属于一个进程组,进程组是由一个正整数进程组ID来标识的。getpgrp函数返回当前进程的进程组ID。默认地,一个子进程和它的父进程同属于一个进程组。一个进程可以通过使用setpgid来改变自己或者其他进程的进程组。
/bin/kill程序可以向另外的进程发送任意的信号。
在任何时刻,至多只有一个前台作业和0个或多个后台作业。
进程通过调用kill函数发送信号给其他进程(包括它们自己)。
进程可以通过用alarm函数向它自己发送SIGALRM信号。