Chinaunix首页 | 论坛 | 博客
  • 博客访问: 55739
  • 博文数量: 47
  • 博客积分: 2095
  • 博客等级: 大尉
  • 技术积分: 560
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-01 18:42
文章分类

全部博文(47)

文章存档

2011年(1)

2008年(46)

我的朋友

分类: LINUX

2008-04-26 13:52:45

Performing the Process Switch

A process switch may occur at just one well-defined point: the schedule( ) function, which is discussed at length in Chapter 7. Here, we are only concerned with how the kernel performs a process switch.

进程切换仅可能发生在一个定义好的位置:schedule()函数。

Essentially, every process switch consists of two steps:

1. Switching the Page Global Directory to install a new address space; we'll describe this step in Chapter 9.

2. Switching the Kernel Mode stack and the hardware context, which provides all the information needed by the kernel to execute the new process, including the CPU registers.

Again, we assume that prev points to the descriptor of the process being replaced, and next to the descriptor of the process being activated. As we'll see in Chapter 7, prev and next are local variables of the schedule( ) function.
再次假设prev指针指向被取代的进程描述符,next指向激活的进程描述符。它们都是schedule()函数的局部变量。

The switch_to macro

The second step of the process switch is performed by the switch_to macro. It is one of the most hardware-dependent routines of the kernel, and it takes some effort to understand what it does.

进程切换的第二步由宏switch_to实现。这是内核中硬件依赖性很强的过程之一,理解它有点困难。

First of all, the macro has three parameters, called prev, next, and last. You might easily guess the role of prev and next: they are just placeholders for the local variables prev and next, that is, they are input parameters that specify the memory locations containing the descriptor address of the process being replaced and the descriptor address of the new process, respectively.

首先,这个宏有三个参数:prev、next和last。很容易猜到prev和next的作用:它们放置了局部变量prev和next,即它们是输入参数,分别指定被取代和新的进程描述符的地址的内存位置。

What about the third parameter, last? Well, in any process switch three processes are involved, not just two. Suppose the kernel decides to switch off process A and to activate process B. In the schedule( ) function, prev points to A's descriptor and next points to B's descriptor. As soon as the switch_to macro deactivates A, the execution flow of A freezes.

那么第三个参数,last呢?在任意一次进程切换中,牵涉了三个进程,而不是两个。假设内核决定将进程A换出,并激活进程B。在函数schedule()中,prev指向A的描述符,next指向B的。一旦宏switch_to停止了A,A的执行序列就冻结了。

Later, when the kernel wants to reactivate A, it must switch off another process C (in general, this is different from B) by executing another switch_to macro with prev pointing to C and next pointing to A. When A resumes its execution flow, it finds its old Kernel Mode stack, so the prev local variable points to A's descriptor and next points to B's descriptor. The scheduler, which is now executing on behalf of process A, has lost any reference to C. This reference, however, turns out to be useful to complete the process switching (see Chapter 7 for more details).

稍后,当内核想重新激活A时,它必须从另外一个进程C(通常,它不同于B)切换过去,通过执行另外一次宏switch_to,这次prev指向C,next指向A。当A恢复了它的执行序列,它会找到它的旧的内核栈,它会看到prev局部变量指向A的描述符,而next指向B的描述符。调度器,已经代表A执行了(处在A的进程上下文?),丢失了任何C的引用。然而这个引用对完成这次进程切换很有用。

The last parameter of the switch_to macro is an output parameter that specifies a memory location in which the macro writes the descriptor address of process C (of course, this is done after A resumes its execution). Before the process switching, the macro saves in the eax CPU register the content of the variable identified by the first input parameter prev that is, the prev local variable allocated on the Kernel Mode stack of A. After the process switching, when A has resumed its execution, the macro writes the content of the eax CPU register in the memory location of A identified by the third output parameter last. Because the CPU register doesn't change across the process switch, this memory location receives the address of C's descriptor. In the current implementation of schedule( ), the last parameter identifies the prev local variable of A, so prev is overwritten with the address of C.

因此,宏switch_to的最后一个参数是一个输出参数,指明一个内存位置,这个宏将进程C的进程描述符的地址写入这个位置(当然,这在A恢复执行后进行)。在进程切换前,宏将第一个参数prev,也就是A的内核栈内的局部变量prev,保存在eax寄存器中。进程切换结束后,A恢复了执行,宏将eax寄存器的内容写入A的内存位置,由第三个参数last指明。因为进程切换中CPU寄存器不会改变,这个位置指向C的描述符地址。在schedule()的当前实现中,last参数指明了A的prev局部变量,因此prev被C的地址覆盖。

The contents of the Kernel Mode stacks of processes A, B, and C are shown in Figure 3-7, together with the values of the eax register; be warned that the figure shows the value of the prev local variable before its value is overwritten with the contents of the eax register.

图示了A、B、C的内核栈内容以及eax寄存器;需要注意的是这个图显示的prev局部变量是在它被eax寄存器覆盖前的值。

The switch_to macro is coded in extended inline assembly language that makes for rather complex reading: in fact, the code refers to registers by means of a special positional notation that allows the compiler to freely choose the general-purpose registers to be used. Rather than follow the cumbersome extended inline assembly language, we'll describe what the switch_to macro typically does on an 80x86 microprocessor by using standard assembly language:

宏switch_to是用扩展内联汇编语言编写的,读起来相当吃力:事实上,引用寄存器的代码是通过一种特殊的位置标记,允许编译器自由地选择通用寄存器。这里没有引用那些繁琐的代码,仅用标准的语言描述switch_to宏在x86处理器做了哪些工作

1. Saves the values of prev and next in the eax and edx registers, respectively:
   分别将prev和next的值保存在eax和edx寄存器中:

       movl prev, %eax
       movl next, %edx

2. Saves the contents of the eflags and ebp registers in the prev Kernel Mode stack. They must be saved because the compiler assumes that they will stay unchanged until the end of switch_to:
   将eflags和ebp寄存器的内容保存在prev的内核栈内。必须保存它们的值,是因为编译器假设直到switch_to结束它们会保持不变:

       pushfl
       pushl %ebp

3. Saves the content of esp in prev->thread.esp so that the field points to the top of the prev Kernel Mode stack:
   将esp寄存器的内容保存在prev->thread.esp中,(prev->thread是prev的硬件上下文)使得这个成员指向prev内核栈的栈顶:

       movl %esp,484(%eax)

The 484(%eax) operand identifies the memory cell whose address is the contents of eax plus 484.
操作数484(%eax)标识了地址为eax的内容加484的内存单元。

4. Loads next->thread.esp in esp. From now on, the kernel operates on the Kernel Mode stack of next, so this instruction performs the actual process switch from prev to next. Because the address of a process descriptor is closely related to that of the Kernel Mode stack (as explained in the section "Identifying a Process" earlier in this chapter), changing the kernel stack means changing the current process:
加载next->thread.esp到esp中。从现在开始,内核在next的内核栈上操作,所以这个指令实现了真正的从prev到next的切换。因为进程描述符的地址跟内核栈的地址有很深的联系,改变内核栈意味着改变了当前的进程:

        movl 484(%edx), %esp

5. Saves the address labeled 1 (shown later in this section) in prev->thread.eip. When the process being replaced resumes its execution, the process executes the instruction labeled as 1:
   将标签1(稍后会看到)的地址保存在prev->thread.eip中。当这个被取代的进程恢复它的执行时,它从标签1开始执行。

        movl $1f, 480(%eax)

6. On the Kernel Mode stack of next, the macro pushes the next->thread.eip value, which, in most cases, is the address labeled as 1:
   在next的内核栈中,这个宏将next->thread.eip的值压栈,这个值通常就是标签1的地址。

        pushl 480(%edx)
7. Jumps to the _ _switch_to( ) C function (see next):
   跳到函数_ _switch_to():

    jmp _ _switch_to

8. Here process A that was replaced by B gets the CPU again: it executes a few instructions that restore the contents of the eflags and ebp registers. The first of these two instructions is labeled as 1:
这里进程A被B替换,重新得到CPU:它执行了一小段指令重新装载eflags和ebp寄存器的内容。这两条指令的第一条被标记为1:

    1:
        popl %ebp
        popfl

Notice how these pop instructions refer to the kernel stack of the prev process. They will be executed when the scheduler selects prev as the new process to be executed on the CPU, thus invoking switch_to with prev as the second parameter. Therefore, the esp register points to the prev's Kernel Mode stack.

注意pop指令是如何指向prev进程的内核栈的。当调度程序选择prev作为新的进程来执行,因此switch_to使用prev作为第二个参数。因此esp寄存器指向prev的内核栈。

9. Copies the content of the eax register (loaded in step 1 above) into the memory location identified by the third parameter last of the switch_to macro:
   将eax寄存器的内容(第一步放进去的)拷贝到switch_to宏第三个参数lask标识的内存位置:

       movl %eax, last

As discussed earlier, the eax register points to the descriptor of the process that has just been replaced.[*]
[*] As stated earlier in this section, the current implementation of the schedule( ) function reuses the prev local variable, so that the assembly language instruction looks like movl %eax,prev.

正如前面讨论的,eax寄存器指向已经被替换的进程的描述符。(由于schedule()函数的当前实现中,重用了prev局部变量,所以这个汇编指令看起来像 movl %eax, prev.)
阅读(380) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~