《深入理解计算机系统》第7周

本周主要学习了异常控制流ECF(Exception Control Flow)相关知识,它可以发生在计算机系统的各个层次,是操作系统用来实现I/O、进程和虚拟内存的基本机制。应用程序通过ECF和操作系统进行交互,如磁盘写数据、网络读取数据、创建新进程、终止当前进程等。理解ECF可以帮助我们编写有趣的应用程序并且更好得理解并发。C++/Java语言提供的异常处理机制,允许程序运行非本地跳转来相应错误,这些都是ECF的一种应用层形式。

  • 异常是控制流中的突变,用来响应处理器状态中的某些变化。在任何情况下,处理器检测到有事件发生时,它会通过异常表来进行间接过程调用,到异常处理程序来处理。完成处理后根据异常类型有以下3种情况:

    • 处理程序将控制返回给当前指令;
    • 处理程序将控制返回给下一条指令并执行;
    • 处理程序终止被中断的程序
  • 异常和过程调用的区别:

    • 过程调用时,在跳转前处理器将返回地址压栈。异常处理时根据异常的类型返回地址要么是当前指令要么是下一条指令;
    • 处理器也把一些额外的处理器状态压到栈里,在处理程序返回时,重新开始执行被中断的程序会需要这些状态。如X86-64会压入包含当前条件码的EFLAGS寄存器
    • 如果控制从用户程序转移到内核,则压入内核栈中
    • 异常处理程序运行在内核模式下
  • 异常类型: 中断interrupt、陷阱trap、故障fault、终止abort

    • 中断: 来自I/O设备的信号/异步/总是返回到下一条指令
    • 陷阱:有意的异常/同步/总是返回到下一条指令 (用途: 在用户程序和内核之间提供一个像过程一样的接口,叫做系统调用)
    • 故障:潜在的可恢复的错误/同步/可能返回当前指令 (如缺页异常)
    • 终止:不可恢复的错误/同步/不会返回
  • 上下文切换是内核使用的一种较高层形式的异常控制流,用来实现多任务。

  • 父进程通过fork函数创建子进程,子进程得到与父进程用户级虚拟地址空间相同的一份副本,包括代码和数据段、堆、共享库以及用户栈。子进程还获得与父进程打开文件描述符相同的副本,可以读写父进程中打开的任何文件。两者最大的区别在于不同的PID。 fork函数被父进程调用一次,返回两次—- 一次是返回到父进程,一次返回到新创建的子进程。在父进程中fork返回子进程的PID;在子进程中fork返回0.
  • Linux信号是一种更高层的软件形式的异常,允许进程和内核中断其他进程。一个发出来没有被接收的信号叫做待处理信号,在任何时刻一种类型至多只会有一个待处理信号。(后续发送的不会排队等待,被简单丢弃)