CSAPP笔记(第八章 异常控制流)-01

第八章的内容为p537~p586, 分2部分, p537~p562, p563~p586.

摘要

本章主要讲了操作系统是如何处理任务, 任务之间是如何进行协调的.

异常

异常就是用来改变响应处理器状态变化的, CPU会暂时放下手上的任务, 进行异常的处理. 处理完之后有3种结果:

  1. 返回当前指令
  2. 跳到下一个指令
  3. 结束当前进程

异常需要硬件和软件共同工作. 异常都有编号, 其中一部分由CPU设计者分配, 剩下的由操作系统内核设计者分配. 异常表的起始地址放在一个叫异常表基址寄存器的特殊CPU寄存器中.

常见的异常有被零除, 缺页, 内存访问违例, 断点, 算术运算溢出, 系统调用, I/O设备信号等.

异常的类别, 中断(interrupt), 陷阱(trap), 故障(fault), 终止(abort)

x86-64系统定义了256种不同的异常类型, 0~31号由Intel架构师定义, 因此对任何x86-64系统都是一样的. 其他号码由操作系统定义, 所以不同的操作系统有差异.

进程

进程是一个执行中程序的实例. 每个程序都运行在某个进程的上下文(context)中. 上下文包含一组状态, 包括代码, 数据, 栈, 通用目的寄存器的内容, 程序计数器, 环境变量以及打开文件描述符的集合.

多个流并发地执行的一般现象被称为并发(concurrency), 一个进程和其他进程轮流运行称为多任务(multitasking), 一个进程执行它的控制流的一部分的每个时间段叫做时间片(time slice), 多任务也叫时间分片(time slicing). 并发与CPU核数无关. 如果2个流并行地运行在不同CPU核上或者不同的计算机上, 叫做并行流(parallel flow), 并行的运行(running in parallel), 并行地执行(parallel execution).

内存的分配. 底部留给用户程序, 顶部留给内核.

用户模式与内核模式

处理器通常用某个控制寄存器中的一个模式位(mode bit)来提供标识是否内核模式, 当设置了模式位时, 进行就运行在内核模式, 可以执行所有指令, 且可以访问系统中的任何内存位置. 没有设置模式位时, 进行运行在用户模式中, 不运行执行特权指令(privileged instruction), 比如停止处理器, 改变模式位, 或者发起一个I/O操作, 也不允许直接引用内核区中的代码和数据, 任何这样的尝试都会导致致命的保护故障. 必须通过系统调用接口间接地访问内核代码和数据.

Linux提供了/proc文件系统, 可以在用户模式下访问内核数据结构的内容. 如/proc/cpuinfo访问cpu类型, /proc//maps访问某个特殊进行使用的内存段.

上下文切换

当内核代表用户执行系统调用时, 可能会发生上下文切换, 如果系统调用因为等待某个事件而发生阻塞时, 内核可以让当前进行休眠, 切换到另一个进程. 比如一个read系统调用需要访问磁盘, 而磁盘数据可能要几十毫秒才能到达内存, 内核可以切换到另一个进程, 而当数据从磁盘到达后, 会有一个磁盘中断, 内核再切换回原进程, 继续执行.

进程控制

pid = fork()函数会复制当前进程, 开启一个子进程, 根据pid的不同, 程序内可以判断是父进程还是子进程, pid为0则在子进程, pid为负则报错, pid为正则在父进程.

#include <sys/types.h>
#include <unistd.h>

pid_t getpid(void); //返回调用进程PID
pid_t getppid(void); //返回调用进程的父进程PID

进程三状态

  1. 运行(running). 要么在cpu上执行, 要么等待被执行且最终被内核调度.
  2. 停止(stopped). 进程被挂起(suspended), 且不会被调度, 可以认为是暂停了, 直到等待SIGCONT信号才会再次运行.
  3. 终止(terminated). 进程永远停止了. 有三种原因1. 收到终止信号. 2. 从主程序返回. 3. 调用exit函数.
#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);

子进程得到与父进程用户级虚拟地址空间相同的一份副本, 包括代码, 数据段, 堆, 共享库, 用户栈, 子进程还获得与父进程任何打开文件描述符相同的副本(子进程可以读写父进程打开的任何文件).

关于fork的理解去看了左耳朵耗子的几篇文章. 一个FORK的面试题, VFork挂掉的一个问题.

回收子进程

当一个进程终止时, 内核不是立刻将它清除, 进程被保持在终止状态, 直到被父进程回收(reaped), 终止还未被回收称为僵死进程(zombie).

如果父进程终止了, 内核会安排init进程进行回收. init进程的PID为1, 是系统启动时内核创建的, 不会终止, 是所有进程的祖先.

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *statusp, int options);

waitpid用来挂起父进程, 等待子进程被回收.

posted @ 2019-10-27 21:26  Panda110  阅读(182)  评论(0编辑  收藏  举报