异常
异常的发生和捕捉,是在硬件层面完成的。但是异常的处理,是由软件来完成的
异常处理
异常表
IO发出的信号异常代码,是由操作系统来分配的,像加法移除的异常代码,由CPU触发
根据异常代码在异常表里找到相应的处理代码
异常处理似乎很像一个函数调用,但异常处理需要:
- 把 CPU 内当前运行程序用到的所有寄存器,都放到栈里面
- 像陷阱涉及用户态内核态的异常处理的栈要压到内核里
- 像故障这样的异常,在异常处理程序执行完成之后。从栈里返回出来,继续执行的不是顺序的下一条指令,而是故障发生的当前指令
异常的类别
类别 | 原因 | 异步/同步 | 返回行为 |
---|---|---|---|
中断 | 来自I/O设备的信号 | 异步 | 总是返回到下一条指令 |
陷阱 | 有意的异常 | 同步 | 总是返回到下一条指令 |
故障 | 潜在可恢复的错误 | 同步 | 可能返回到当前指令 |
终止 | 不可恢复的错误 | 同步 | 不会返回 |
Linux/X86-64系统中的异常
- 除法错误
- 一般保护故障
- 缺页
- 机器检查
Linux 中的系统调用
进程
一个执行中程序的实例
逻辑控制流
逻辑控制流。进程为每个程序提供了一种假象,好像程序在独占地使用处理器
并发流
一个逻辑流的执行在时间上与另一个流重叠
私有地址空间
进程为每个程序提供一个假象,好像它独占使用了系统的全部内存
用户模式与内核模式
寄存器中的一个模式位,设置该模式位可以用来切换用户模式或是内核模式
上下文切换
内核使用上下文切换来实现多任务
系统调用错误处理
包装系统调用进行错误处理
进程控制
获取进程ID
![批注 2019-07-12 103606](/assets/批注%202019-07-12%20103606.png)
创建和终止进程
void exit(int status); // 终止 pid_t fork(void); // 创建新子进程
回收子进程
pid_t waitpid(pid_t pid,int *statusp,int options);
让进程休眠
unsigned int sleep(unsigned int secs); int pause(void);
加载并运行程序
execve(cont char *filename,const char *argv[],const char *envp[]); // 使用当前的进程运行新的程序
信号术语
发送信号
进程组
pid_t getpgrp(void);
用kill发送信号
/bin/kill -9 1235
用kill函数发送信号
int kill(pid_t pid,int sig);
用alarm函数
unigned int alarm(unsigned int secs);
接收信号
sighandler_t signal(int signum,sighandler_t handler);
阻塞信号和解除阻塞信号
- 隐式阻塞
- 显式阻塞
编写信号处理程序
- 安全
- 正确
- 可移植
同步流
显式等待信号
非本地跳转
用户级异常控制流形式
// 保存当前调用环境,供后面的longjmp调用int setjmp(jmp_buf env);int sigsetjmp(sigjmp_buf env,int savesigs);
void longjmp(jmp_buf env,int retval);void siglongjmp(sigjmp_buf env,int retval);