- 不同指令的地址在程序计数器中的过渡称为控制转移,这样的控制转移序列称为控制流
- 现代系统通过使控制流发生突变来对系统状态的变化做出反应,把这些突变称为异常控制流
异常
- 异常是控制流中的突变,用来响应处理器状态中的某些变化
- 在处理器中,状态被编码为不同的位和信号,状态变化称为事件
- 系统中可能的每种类型的异常都分配了一个唯一的非负整数的异常号
- 异常表是操作系统在系统启动时分配和初始化的跳转表,使得表目k包含异常k的处理程序的地址
- 触发异常时,处理器执行间接过程调用,通过异常表的表目k转到相应的处理程序。异常号是到异常表中的索引,异常表的起始地址放在异常表基址寄存器里
- 异常的类别
类别 | 原因 | 同步/异步 | 返回行为 |
---|---|---|---|
中断 | 来自I/O设备的信号 | 异步 | 总是返回到下一条指令 |
陷阱 | 有意的异常 | 同步 | 总是返回到下一条指令 |
故障 | 潜在可恢复的错误 | 同步 | 可能返回到当前指令 |
终止 | 不可恢复的错误 | 同步 | 不会返回 |
- 异步和同步异常的区别在于异常产生的原因来自CPU外部还是内部
进程
- 进程是一个执行中程序的实例,系统中的每个程序都运行在某个进程的上下文中
- 执行程序时一系列的程序计数器值的序列叫做逻辑控制流
- 一个逻辑流的执行在时间上与另一个流重叠,称为并发流
- 多个流并发地执行的一般现象称为并发,一个进程和其他进程轮流运行的概念称为多任务,一个进程执行它的控制流的一部分的每一时间段叫做时间片
- 进程为每个程序提供它自己的私有地址空间,该部分不能被其他进程读或写
- 设置了模式位时,进程就运行在内核模式中,否则运行在用户模式中
- 进程从用户模式变为内核模式的唯一方法是通过诸如中断、故障或者陷入系统调用这样的异常,当异常发生时,控制传递到异常处理程序,处理器将模式从用户模式变为内核模式
- 当内核选择一个新的进程运行时,称为内核调度了这个进程,它会抢占当前进程,并使用一种称为上下文切换的机制来将控制转移到新的进程
- 调用read会进行磁盘读取,此时磁盘中断处理器,内核执行上下文切换,而不是在间歇时间内等待
系统调用错误处理
- 使用错误处理包装函数可以更进一步地简化代码
进程控制
- 每个进程都有一个唯一的正数进程ID,使用
getpid
函数可以返回 - 进程总是处于下面三种状态之一:
- 运行:在CPU上执行或在等待被执行且最终会被内核调度
- 停止:被挂起,且不会被调度
- 终止:进程永远停止
- 父进程通过调用
fork
函数创建一个新的运行的子进程 fork
函数被调用一次,会返回两次,一次是返回到父进程,一次是返回到新创建的子进程- 当一个进程由于某种原因终止时,保持在一种已终止的状态中,直到被它的父进程回收
- 一个终止了但未被回收的进程称为僵死进程,即使没有运行,仍然消耗系统的内存资源
- 如果一个父进程终止了,内核会安排init进程成为它的孤儿进程的养父去回收它们
- 一个进程可以通过调用
waitpid
函数来等待它的子进程终止或者停止 wait(&status)
函数等价于waitpid(-1, &status, 0)
sleep
函数将一个进程挂起一段指定的时间pause
函数让调用函数休眠,直到该进程收到一个信号execve
函数加载并执行目标文件filename,且带参数列表argv和环境变量列表envp- 程序是一堆代码和数据,可以作为目标文件存在于磁盘上,或者作为段存在于地址空间中;进程是执行中程序的一个具体实例;程序总是运行在某个进程的上下文中
fork
函数在新的子进程中运行相同的程序,新的子进程是父进程的一个复制品;execve
函数在当前进程的上下文中加载并运行一个新的程序,它会覆盖当前进程的地址空间,但并没有创建一个新的进程
信号
- 信号就是一个小消息,它通知进程系统中发生了一个某种类型的事件
- 发送信号:内核通过更新目的进程上下文中的某个状态,发送一个信号给目的进程
- 接受信号:当目的进程被内核强迫以某种方式对信号的发送做出反应时,它就接收了信号
- 一个发出而没有被接收的信号叫做待处理信号
- 每个进程都只属于一个进程组,进程组是由一个正整数进程组ID来标识的
/bin/kill
程序可以向另外的进程发送任意的信号,比如/bin/kill -9 pid
发送信号9(SIGKILL)信号给进程pid,负的pid会导致信号被发送到进程组pid中的每个进程signal
函数可以改变和信号signum
相关联的行为,忽略、恢复默认和信号处理程序,唯一的例外是SIGSTOP
和SIGKILL
的默认行为不能被修改- 当一个进程捕获了一个类型为k的信号时,会调用为信号k设置的处理程序,一个整数参数被设置为k,这个参数允许同一个处理函数捕获不同类型的信号
- 隐式阻塞机制:内核默认阻塞任何当前处理程序正在处理信号类型的待处理的信号
- 显式阻塞机制:应用程序可以使用
sigprocmask
函数和它的辅助函数,明确地阻塞和解除阻塞选定的信号 - 用
volatile
限定符定义的变量,编译器不会缓存这个变量,每次都会从内存中读取
非本地跳转
- 非本地跳转:将控制直接从一个函数转移到另一个当前正在执行的函数,而不需要经过正常的调用-返回序列