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

全部博文(47)

文章存档

2011年(1)

2008年(46)

我的朋友

分类: LINUX

2008-04-26 14:20:11

The _ _switch_to ( ) function

The _ _switch_to( ) function does the bulk of the process switch started by the switch_to( ) macro. It acts on the prev_p and next_p parameters that denote the former process and the new process. This function call is different from the average function call, though, because _ _switch_to( ) takes the prev_p and next_p parameters from the eax and edx registers (where we saw they were stored), not from the stack like most functions. To force the function to go to the registers for its parameters, the kernel uses the _ _attribute_ _ and regparm keywords, which are nonstandard extensions of the C language implemented by the gcc compiler. The _ _switch_to( ) function is declared in the include /asm-i386/system.h header file as follows:

函数_ _switch_to()做了大量的进程切换的工作。它操作prev_p和next_p参数,它们分别表示前一个进程和新的进程。这个函数调用不同于一般的函数调用,因为_ _switch_to从eax和edx寄存器中(我们看着放进去的)得到perv_p和next_p参数,而不是像大多数函数那样从栈那里得到。为了强迫这个函数去寄存器那里拿它的参数,内核使用了_ _attribute_ _和regparm关键字,它们是gcc编译器对C语言的非标准扩展。这个函数_ _switch_to()在/asm-i386/system.h头文件中声明如下:

    _ _switch_to(struct task_struct *prev_p,
                struct task_struct *next_p)
       _ _attribute_ _(regparm(3));

The steps performed by the function are the following:
  1. Executes the code yielded by the _ _unlazy_fpu( ) macro (see the section "Saving and Loading the FPU , MMX, and XMM Registers" later in this chapter) to optionally save the contents of the FPU, MMX, and XMM registers of the prev_p process.

    执行宏_ _unlazy_fpu()代表的代码,有选择地保存prev_p进程中FPU、MMX、XMM寄存器的内容。

        _ _unlazy_fpu(prev_p);
  2. Executes the smp_processor_id( ) macro to get the index of the local CPU, namely the CPU that executes the code. The macro gets the index from the cpu field of the thread_info structure of the current process and stores it into the cpu local variable.

    执行宏smp_processor_id()得到本地CPU的索引,即执行这段代码的CPU。这个宏从当前进程的thread_info结构的cpu成员得到这个索引,并将它存放在cpu局部变量中。
  3. Loads next_p->thread.esp0 in the esp0 field of the TSS relative to the local CPU; as we'll see in the section "Issuing a System Call via the sysenter Instruction " in Chapter 10, any future privilege level change from User Mode to Kernel Mode raised by a sysenter assembly instruction will copy this address in the esp register:

    将next_p->thread.esp0放入对应CPU的TSS的esp0成员;将来任何执行权限从用户态到内核态的改变,都会复制这个地址到esp寄存器。

        init_tss[cpu].esp0 = next_p->thread.esp0;
  4. Loads in the Global Descriptor Table of the local CPU the Thread-Local Storage (TLS) segments used by the next_p process; the three Segment Selectors are stored in the tls_array array inside the process descriptor (see the section "Segmentation in Linux" in Chapter 2).

    装载next_p进程使用的本地CPU TLS段的GDT;这三个段选择子存放在进程描述符的tls_array数组中。

         cpu_gdt_table[cpu][6] = next_p->thread.tls_array[0];
         cpu_gdt_table[cpu][7] = next_p->thread.tls_array[1];
         cpu_gdt_table[cpu][8] = next_p->thread.tls_array[2];
  5. Stores the contents of the fs and gs segmentation registers in prev_p->thread.fs and prev_p->thread.gs, respectively; the corresponding assembly language instructions are:

    将fs和gs段寄存器的内容分别保存到prev_p->thread.fs和prev_p->thread.gs中;对应的汇编指令是:

        movl %fs, 40(%esi)
        movl %gs, 44(%esi)

    The esi register points to the prev_p->thread structure.

    esi寄存器指向prev_p->thread结构。
  6. If the fs or the gs segmentation register have been used either by the prev_p or by the next_p process (i.e., if they have a nonzero value), loads into these registers the values stored in the thread_struct descriptor of the next_p process. This step logically complements the actions performed in the previous step. The main assembly language instructions are:

    如果fs或gs段寄存器已经被prev_p或next_p进程使用过(比如,如果它们有非零值),将next_p进程的thread_struct描述符中存放的值加载到这些寄存器中。这一步逻辑地补充了前一步的工作:

        movl 40(%ebx),%fs
        movl 44(%ebx),%gs

    The ebx register points to the next_p->thread structure. The code is actually more intricate, as an exception might be raised by the CPU when it detects an invalid segment register value. The code takes this possibility into account by adopting a "fix-up" approach (see the section "Dynamic Address Checking: The Fix-up Code" in Chapter 10).

    寄存器ebx指向next_p->thread结构。代码实际上更复杂,因为CPU可能会产生异常,当它检测到无效的段寄存器值。代码采用“fix-up”方法考虑了这种可能性。
  7. Loads six of the dr0,..., dr7 debug registers [*] with the contents of the next_p->thread.debugreg array. This is done only if next_p was using the debug registers when it was suspended (that is, field next_p->thread.debugreg[7] is not 0). These registers need not be saved, because the prev_p->thread.debugreg array is modified only when a debugger wants to monitor prev:

    将next_p->thread.debugreg数组的内容加载到dr0、…、dr7六个调试寄存器中。仅在next_p进程被暂停,并使用调试寄存器是做这个动作。这些寄存器不需要保存,是因为prev_p->thread.debugreg数组仅当调试器想监视prev时被修改。

    [*] The 80x86 debug registers allow a process to be monitored by the hardware. Up to four breakpoint areas may be defined. Whenever a monitored process issues a linear address included in one of the breakpoint areas, an exception occurs.

        if (next_p->thread.debugreg[7]){
            loaddebug(&next_p->thread, 0);
            loaddebug(&next_p->thread, 1);
            loaddebug(&next_p->thread, 2);
            loaddebug(&next_p->thread, 3);
            /* no 4 and 5 */
            loaddebug(&next_p->thread, 6);
            loaddebug(&next_p->thread, 7);
        }
  8. Updates the I/O bitmap in the TSS, if necessary. This must be done when either next_p or prev_p has its own customized I/O Permission Bitmap:

    如果必要,更新TSS中I/O bitmap。当next_p或prev_p有自己的自定义的I/O权限bitmap:

       if (prev_p->thread.io_bitmap_ptr || next_p->thread.io_bitmap_ptr)
            handle_io_bitmap(&next_p->thread, &init_tss[cpu]);

    Because processes seldom modify the I/O Permission Bitmap, this bitmap is handled in a "lazy" mode: the actual bitmap is copied into the TSS of the local CPU only if a process actually accesses an I/O port in the current timeslice. The customized I/O Permission Bitmap of a process is stored in a buffer pointed to by the io_bitmap_ptr field of the thread_info structure. The handle_io_bitmap( ) function sets up the io_bitmap field of the TSS used by the local CPU for the next_p process as follows:

    因为进程很少修改I/O权限bitmap,所以用“懒惰”模式处理bitmap:仅当一个进程在当前时隙确实访问I/O端口时,将bitmap复制到本地CPU的TSS中。进程的自定义的I/O权限bitmap存放在thread_info结构中的io_bimap_ptr成员指向的buffer。本地CPU为next_p进程使用函数handle_io_bitmap()设置TSS的io_bimap成员:

    o If the next_p process does not have its own customized I/O Permission Bitmap, the io_bitmap field of the TSS is set to the value 0x8000.
      如果next_p进程没有自己的自定义的I/O权限bitmap,TSS的io_bimap成员设为0x800

    o If the next_p process has its own customized I/O Permission Bitmap, the io_bitmap field of the TSS is set to the value 0x9000.
      否则,TSS的io_bimap成员设为0x900

    The io_bitmap field of the TSS should contain an offset inside the TSS where the actual bitmap is stored. The 0x8000 and 0x9000 values point outside of the TSS limit and will thus cause a "General protection " exception whenever the User Mode process attempts to access an I/O port (see the section "Exceptions" in Chapter 4). The do_general_protection( ) exception handler will check the value stored in the io_bitmap field: if it is 0x8000, the function sends a SIGSEGV signal to the User Mode process; otherwise, if it is 0x9000, the function copies the process bitmap (pointed to by the io_bitmap_ptr field in the thread_info structure) in the TSS of the local CPU, sets the io_bitmap field to the actual bitmap offset (104), and forces a new execution of the faulty assembly language instruction.

    TSS的io_bimap成员应该包含一个偏移值,表示在实际bitmap存放在TSS中的位置。0x800和0x900指定了TSS的限制,并在用户态进程试图访问I/O端口时产生一个“通用保护”异常。异常处理函数do_general_protection()将检查存储在io_bitmap成员的值:如果是0x800,这个函数发送一个SIGSEGV信号给用户态进程;否则,是0x900,函数拷贝进程的bitmap(由thread_info结构的io_bimap_ptr成员指向)到本地CPU的TSS,设置io_bimap成员为实际的bitmap偏移(104),并强制执行一个新的汇编指令。

  9. Terminates. The _ _switch_to( ) C function ends by means of the statement:
    结束,函数_ _switch_to()以下面这个语句结束:

        return prev_p;

    The corresponding assembly language instructions generated by the compiler are:
    编译器生成的对应的汇编指令为:

        movl %edi,%eax
        ret

    The prev_p parameter (now in edi) is copied into eax, because by default the return value of any C function is passed in the eax register. Notice that the value of eax is thus preserved across the invocation of _ _switch_to( ); this is quite important, because the invoking switch_to macro assumes that eax always stores the address of the process descriptor being replaced.

    参数prev_p(现在在edi中)被拷贝到eax中,因为C函数的默认返回值都放在eax寄存器中。注意eax的值因此在函数_ _switch_to()中被保留;这一点很重要,因为调用的宏switch_to假设eax总是存储被替换的进程描述符的地址。

    The ret assembly language instruction loads the eip program counter with the return address stored on top of the stack. However, the _ _switch_to( ) function has been invoked simply by jumping into it. Therefore, the ret instruction finds on the stack the address of the instruction labeled as 1, which was pushed by the switch_to macro. If next_p was never suspended before because it is being executed for the first time, the function finds the starting address of the ret_from_fork( ) function (see the section "The clone( ), fork( ), and vfork( ) System Calls" later in this chapter).

    汇编指令ret将存储在栈顶的返回地址加载到eip程序计数器中。然而,函数_ _switch_to()是通过跳过去调用的。因此,ret指令在栈中找到标签1的指令的地址,它是由switch_to宏压入栈的。如果next_p之前没有被暂停,因为是第一次调用,函数找到ret_from_fork()函数的开始地址。
阅读(518) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~