定义:从给处理器加点开始,知道你断电为止,程序计数器假设一个值的序列 \(a_0,a_1,...,a_{n-1}\)其中,每个\(a_k\)是某个响应的指令\(I_k\)的地址。每次从\(a_k\)到\(a_{k+1}\)的过渡称为控制转移。这样的控制转移序列交出处理器的控制流。一般情况我们把突变的称为异常控制流(ECF)。座位程序员,理解ECF很重要,原因有一下几点:
- 理解ECF将帮助你理解重要的系统概念。
- 理解ECF将帮助你理解应用程序是如何与操作系统交互的。
- 理解ECF将帮助你编写有趣的新应用程序。
- 理解ECF将帮助你理解并发。
- 理解ECF将帮助你理解软件异常如何工作。
异常
异常是异常控制流的一种形式,它的一部分由硬件时间,一部分由操作系统实现。
异常处理
系统中肯能的每种类型的异常都分配了一个唯一的非负整数的异常号。其中一些号码是由处理器的设计者分配的,其他号码由操作系统内核的设计者分配的。
异常表的起始地址放在一个叫做异常表基址寄存器的特殊CPU寄存器里。
异常类似于过程调用,但是有一些重要的不同之处:
- 过程调用时,在跳转到处理程序之前,处理器将返回地址压入栈中。
- 处理器也把额外的处理器状态压倒栈里,在处理程序返回时,重新开始执行被中断的程序会需要这些状态。
- 如果控制从用户程序转移到内核,所有这些项目都背压倒内核栈中,而不是压到用户栈中。
- 异常处理程序运行在内核模式下,这意味着他们对所有的系统资源都有访问权限。
异常的类别
异常可以分成四类:中断,陷阱,故障和终止。
1.中断是异步发生的,是来自处理器外部的I/O设备信号的结果
2.陷阱是有意的异常,是执行一条指令的结果。
3.故障由错误情况引起的,它可能能够背故障处理程序修正。
4.终止是不可恢复的致命错误造成的结果。
Linux/x86-64系统中的异常
1.Linux/x86-64 故障和终止
- 除法错误
- 一般保护故障
- 缺页
- 机器检查
2.Linuux/x86-64 系统调用
进程
异常是允许操作系统内核提供进程概念的基本构造块,进程是计算机科学中最深刻,最成功的概念之一。
- 一个独立的逻辑控制流,它提供一个假象,好像我们的程序独占地使用处理器。
- 一个私有的地址空间。它提供一个假象,好像我们的程序独占的使用内存系统。
逻辑控制流
并发流
一个逻辑流的执行在时间上与另一个流重叠,称为并发流,这两个流被称为并发地运行。
私有地址空间
进程也为每个程序提供一种假象,好像它独占的使用系统地址空间。
用户模式和内核模式
处理器通常是用某个控制寄存器中的一个模式位来提供这种功能的,改寄存器描述了进程当前享有的特权。
上下文切换
操作系统内核使用一种称为上下文切换的较高层形式的异常控制流来实现多任务。
内核为没一个进程维持一个上下文。在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程。这种决策就叫做调度,是由内核中称为调度器的代码处理的。
当内核代表用户执行系统调用时,可能会发生上下文切换。
终端也可能引发上下文切换。
系统调用错误处理
当Unix系统级函数遇到错误时,它们通常会返回-1,并设置全局整数变量errno来表示什么出错了。
进程控制
获取进程ID
没一个进程都有一个唯一的正数进程ID。
创建和终止进程
从程序员角度,我们可以认为进程总数初一下面三种状态之一:
- 运行
- 停止
- 终止
回收子进程
1.判定等待集合的成员
- 如果pid>0name等待集合就是一个单独的子进程
- 如果pid = -1,name等待集合就是由父进程所有的子进程组成的
2.修改默认行为
WNOHANG:如果等待集合中的任何子进程都还没有终止,那么就立即返回。
WUNTRACED:挂起调用进程的执行,直到等待集合中的一个进程变成已终止或者被停止。
WCOINTINUED:挂起调用进程的执行,直到等待集合中一个正在运行的进程终止或者等待集合中一个被停止收到SIGCONT次你好重新开始执行。
WNOHANG|WUNTRACED:立即返回,如果等待集合中的子进程都没有被停止或者终止,则返回值为0;如果有一个停止或者终止,则返回值为该子进程的PID。
3.检查已回收子进程的退出状态
WIFRXXITED:如果子进程通过调用exit或者一个返回正常终止。
WEXITSTATUS:返回一个正常终止的子进程的退出状态。
WIFSIGNALED:如果自建城是因为一个未被捕获的信号终止的,那么久返回真。
WTERMSIG:返回导致子进程终止信号的编号。
WIFSTOPPED:日过引起返回的子进程当前是停止的那么久返回真。
WSTOPSIG:返回引起子进程停止的信号的编号。
WIFCONTINUED:如果子进程收到SIGCONT信号重新启动,则返回真。
4.错误条件
如果调用进程没有子进程,那么waitpid返回-1,并且设置errno为ECHILD。绕过waitpid函数被一个信号中断,那么它返回-1,并设置errno为EINTR。
5.wait函数
wait是waitpid函数的简单版本
6.使用waitpid的示例
让进程休眠
加载并运行程序
利用fork和execve运行程序
信号
一个信号就是一条小消息,它通知进程系统中发生了一个某种类型的事件。
信号术语
- 发送信号
- 接受信号
发送信号
1.进程组
2.用/bin/kill 程序发送信号
3.从键盘发送信号
4.用kill函数发送信号
5.用alarm函数发送信号
接收信号
每个信号都有一个预定义的默认行为,是下面的一种:
- 进程终止
- 进程终止并转储内存
- 进程停止知道呗SIGCONT信号重启
- 进程忽略该信号
signal函数可以通过下列三种方法之一来改变信号 - 如果handler是SIG_IGN,那么忽略类型为signum的信号。
- 如果handler是SIG_DFL,那么类型为signum的信号行为恢复为默认行为。
- 否则,handler就是用户定义的函数的地址,这个函数被称为信号处理程序。
阻塞和解除阻塞信号
Linux提供阻塞信号的隐式和显式的机制。
编写信号处理程序
信号处理是Liunx系统编程最棘手的一个问题。处理程序有几个属性使他们很难推理分析:
1.处理程序与主程序并发运行,共享同样的全局变量,因此可能与主程序和其他处理程序互相干扰。
2.如何以及何时接收信号的规则常常有违人的直觉。
3.不同的系统有不同的信号处理语义。
编写安全,正确和可移植的信号处理程序的基本原则:
1.安全信号处理
2.正确的处理信号
3.可移植的信号处理
同步流已避免讨厌的并发错误
显式的等待信号
非本地跳转
C语言提供了一种用户级异常控制流形式,称为非本地跳转,它将控制直接从一个函数转移到另一个当前正在执行的函数,而不需要经过正常的调用-返回序列。非本地跳转是通过serjmp和longjmp函数来提供的。
操作进程的工具
STRACE:打印一个正在运行的程序和他的子程序调用的每个系统调动的轨迹。
PS:列出当前系统中的进程(包括僵死的进程)。
TOP:打印出关于当前进程资源使用的信息。
PMAP:显示进程的内存映射。
/proc:一个虚拟文件系统,以ASCII文本格式输出大量内核数据结构的内容,用户程序员可以读取这些内容。