一 为什么需要知道异常控制流 异常控制流发生在程序执行的各个阶段 异常控制流使程序更加简洁和优雅,而且它为程序并行执行提供了条件 了解异常控制流机制对了解计算机程序的执行原理有帮助 二 知道了异常控制流能干什么 了解计算机程序执行原理 设计更优雅的程序 三 什么是异常控制流 1. 两个小定义 异常控制流是用户程序和系统程序交互的基础 计算机系统供一种用户级的异常控制流,让开发者可以在代码的异常逻辑里面,无需解开嵌套多层的调用栈,直接跳转到异常处理的代码里面 2. 异常控制流是用户程序和系统程序交互&软硬件交互的基础 要说明这点,先要说一下什么是用户程序,什么是系统程序。 为了限制用户程序的权限,和将一些系统操作抽象出来,计算机系统将一些高等操作称为系统程序,如申请内存,读取内存信息到寄存器/读取磁盘信息到内存,从一个外部设备读取信息到磁盘,等。 同时这样分离还有一个好处,就是提高系统的吞吐率。众所周知,cpu寄存器-一级缓存-二级缓存-内存-磁盘-网络存储设备,越往后读取的速度越慢,而让cpu盲目的等待IO的数据到位,然后再执行后续的程序,是极其浪费cpu计算资源的。而另一方面,由于计算机系统的多进程特性,一个用户程序在一个进程调用系统程序,进行耗时的IO时,完全可以对这个进程进行挂起,让cpu资源去处理其他进程的逻辑,待IO完毕,给挂起的程序一个通知,让挂起的程序继续进行IO后的逻辑。 这种通知进程的行为,就是异常,而这个通知,就是后面要讲到的信号。 计算机系统的异常有4种:中断,陷阱,故障,终止 中断 中断通常发生在用户程序和硬件交互。中断的过程是异步的,即发生中断时用户程序被挂起(被中断了),待硬件返回后,用户程序继续发生中断代码后续的命令。 陷阱 陷阱和中断类似,只不过陷阱是同步的。陷阱像是一种故意的异常,发生在软件程序上的调用,如用户程序向系统程序申请fock一个子进程,或者执行另外一个程序。所以实现系统程序的封装和系统程序和用户程序的隔离,接口化,使用的也是陷阱这种异常。 故障 故障是由于错误引起的。故障发生时,程序的控制转移到故障处理程序,待故障处理程序逻辑结束后,程序计数器会尝试回到故障出现的行命令。一个典型的故障就是内存缺页故障。当程序读取内存的某一个虚拟地址,发现读取的内存页面不在内存里面,于是引起缺页故障,处理程序将数据从磁盘读取到内存对应的页面,故障处理结束,程序再次回到读取内存页面的那行,这时候,读取的代码再次执行,就可以正常运行了。 终止 终止发生在程序出现致命错误的时候,系统在执行完故障处理程序后,会停止该程序。如:内存损坏时。 3. 信号 信号有很多种类型,系统给不同的信号定义了不同的意义,其中也保留了用户可以自定义的信号 (图出自<<Computer Systems Third edition») 比如在当前进程按下ctrl+c..实际上就是键盘通过操作系统给当前进程发送了一个2信号 4. 除了系统程序可以给用户程序发送信号,用户程序也可以给用户程序发送信号 通过kill命令,传入信号和进程id,可以给进程发送一个信号。常见的nginx通过kill命令重启nginx,平滑重新加载配置,就是用了这个特性。 在程序逻辑里,可以调用c的kill函数给进程id传入信号。如果进程id大于0,向进程id发送信号;如果进程id为0,则为自己和自己所在的进程组每个进程发送信号;如果进程id小于0,则为进程id的绝对值以及进程id绝对值所在的进程组所有的进程都发送信号。 (图出自<<Computer Systems Third edition») 在程序逻辑里面,用户也可以注册信号的handler函数,当进程被传入信号时,程序控制就会交给handler函数。处理完后,控制交还给故障发生时的后一句命令,程序继续执行。 (图出自<<Computer Systems Third edition») 5. 计算机系统的用户级异常控制流 c语言提供setjump和longjump方法。setjmp方法保存当前进程的运行状态(包括程序计数器,栈指针和通用目的寄存器),而longjmp根据之前保存的状态还原程序执行的状态,继续执行setjmp处后面的逻辑。
一. 信息在计算机里面的存储 无论是保存在硬盘里面作永久储存,还是保存在内存/寄存器里面作为计算的临时储存,信息在计算机里面需要一个表示方法,或者叫做保存方法. 首先,可以先来看一下c语言都有哪些信息的类型,或者说我们编程时候都用到哪些信息类型: 布尔型,表示是与否 整型,表示整数 浮点型,表示小数 字符串,表示一系列字符的集合 数组,一系列上述类型的集合 对象,一系列上述类型的索引集合 其中,布尔型可以用简单的 整型0&整型1 表示,就不单独说明. 由于一些基本的概念,比如字符串用ASCII码/UTF-8编码表示,整型根据不同的长度有,是否有符号,会有不同的范围,下面只挑选不同信息类型(或者叫数据类型)的值得提一下的点作说明. 整型: 整型无符号形式不用多说,而有符号的表示形式除了简单的使用最高位作为符号位(1000 0001 表示-1),还有两种表现形式:补码表示和反码表示. 实际上,补码相对于原码和反码,对于计算机的计算是更优化的.大多数现代计算机都用补码表示有符号数. 补码中, 如 1000 0001,将最高有效位解释成负权,而后, 所以 1000 1111 = **-**2^7 **+**2^3 **+**2^2 **+**2^1 **+**2^0 = -113 而反码就与补码类似,不过补码的负权为 - 2 ^(w-1), 反码的负权为 -2^(w-1) - 1, 其中w为整型长度 补码相比反码和原码(即最高位表示符号位的形式)的差别在于: 补码的0000 0000表示0,没有-0表示,而反码和原码都存在一个-0表示 (反码为1111 1111, 原码为 1000 0000) 补码的加减乘除运算,都等价于将补码转换为无符号数的加减乘除,再转换回补码,而反码和原码都要先作相应的转换.(至于为什么反码和补码不能直接运算,可以参考这个文章: 原码, 反码, 补码 详解) 浮点型 书中介绍了一种浮点型表示的标准: IEEE浮点表示.