16.2 How would you measure the time spent in a context switch?
上下文转换发生在两个进程之间,比如让一个等待进程进入执行和让一个运行进程进入等待,这些在多任务中发生。操作系统需要把等待进程的信息放入内存和把当前运行的进程信息保存下来。为了解决这个问题,我们记录在切换进程时的最后和第一个指令的时间点,比如我们有两个进程P1和P2,P1在执行P2在等待,当我们切换P1和P2时,假设发生在P1的第N个指令,我们用tx,k来表示进程x的第k个指令的时间点,那么切换的时间为t2,1-t1,n。
那么问题是我们怎么知道什么时候转换发生,我们又不能记录进程的每个指令的时间点。另外还有转换是由操作系统的安排算法管理的,而且有很多核心线程也要用上下文转换。其他进程也可能竞争CPU或内核处理中断。用户不能控制外部的上下文转换,比如在时间t1,n,内核决定处理中断,那么上下文转换的时间就会延长。为了克服这些困难,我们需要建立一个上下文使得P1执行完,P2马上就能运行。我们可以建立一个数据通道Data Chanel,例如管道Pipe,在P1和P2之间,就像两个进程在打乒乓球,使用数据权标Data Token。
我们让P1当发送者,P2当接受者。开始时,P2在等待,当P1执行了,它发送了数据权标给P2,然后等着读取一个回执。但是由于P2还没能执行,所以P1没有回执,进行被阻碍了,释放了CPU。上下文转换发生了,任务分配器必须选另一个进程执行,且P2处于准备执行状态。当P2运行了,P1和P2的角色互换了,P2现在是发送者而P1是接受者,当P2发回执给P1,整个过程就完成了:
1. P2等待P1发送消息。
2. P1记录时间点。
3. P1给P2发送消息。
4. P1试图读取回执,这包括了一个上下文转换。
5. P2准备就绪并接到了消息。
6. P2发送回执给P1。
7. P2尝试读取P1的回执,这包括了一个上下文转换。
8. P1准备就绪且接到了消息。
9. P1记录时间点。
那么步骤2到9之间的时间差T,表示为T = 2 * (Td + Tc + Tr),其中Td和Tr表示发送和接受消息时间,Tc表示状态转换的时间。那么现在问题是要求Td + Tr的值,即为P1发送信息给自己到接受到消息的时间,这不包括状态转换的时间因为是发给自己。由于可能存在的未知中断,所以我们需要重复多次,取最小的Tc当做结果。
这也只是一个近似结果,因为我们这么做有个假设就是当P2接到消息后马上就开始运行,这得根据任务分配器的实现方法,所以只是个近似值。