zoukankan      html  css  js  c++  java
  • Linux 内核任务0的运行(切换到用户模式)move_to_user_mode

    本文分析基于Linux 0.11内核,转载请标明出处,http://blog.csdn.net/yming0221/archive/2011/06/01/6459119.aspx

    在main.c的程序中,当设备初始化完毕后,程序将从内核模式切换到用户模式,也就是所说的任务0,执行fork()函数(该函数使用内嵌的汇

    编,防止函数调用弄乱堆栈,造成写时复制COPY_ON_WRITE)切换到用户堆栈并执行任务0使用iret指令,首先将有关的值压入堆栈,然后执

    行iret指令。将刚刚压入对堆栈的数据根据标志位弹入相应的寄存器。

    1. //// 切换到用户模式运行。   
    2. // 该函数利用iret 指令实现从内核模式切换到用户模式(初始任务0)。   
    3. #define move_to_user_mode() /   
    4. __asm__ ( "movl %%esp,%%eax/n/t" /   // 保存堆栈指针esp 到eax 寄存器中。   
    5. "pushl $0x17/n/t" /     // 首先将堆栈段选择符(SS)入栈。   
    6.   "pushl %%eax/n/t" /       // 然后将保存的堆栈指针值(esp)入栈。   
    7.   "pushfl/n/t" /        // 将标志寄存器(eflags)内容入栈。   
    8.   "pushl $0x0f/n/t" /       // 将内核代码段选择符(cs)入栈。   
    9.   "pushl $1f/n/t" /     // 将下面标号1 的偏移地址(eip)入栈。   
    10.   "iret/n" /            // 执行中断返回指令,则会跳转到下面标号1 处。   
    11.   "1:/tmovl $0x17,%%eax/n/t" /  // 此时开始执行任务0,   
    12.   "movw %%ax,%%ds/n/t" /    // 初始化段寄存器指向本局部表的数据段。   
    13. "movw %%ax,%%es/n/t" "movw %%ax,%%fs/n/t" "movw %%ax,%%gs":::"ax")  

    执行iret前的堆栈如下:

    一下这段话引用自http://faydoc.tripod.com/cpu/iret.htm


    In Protected Mode, the action of the IRET instruction depends on the settings of the NT (nested task) and VM flags in the EFLAGS register and the VM flag in the EFLAGS image stored on the current stack. Depending on the setting of these flags, the processor performs the following types of interrupt returns:

    • Return from virtual-8086 mode.
    • Return to virtual-8086 mode.
    • Intra-privilege level return.
    • Inter-privilege level return.
    • Return from nested task (task switch).

    If the NT flag (EFLAGS register) is cleared, the IRET instruction performs a far return from the interrupt procedure, without a task switch. The code segment being returned to must be equally or less privileged than the interrupt handler routine (as indicated by the RPL field of the code segment selector popped from the stack). As with a real-address mode interrupt return, the IRET instruction pops the return instruction pointer, return code segment selector, and EFLAGS image from the stack to the EIP, CS, and EFLAGS registers, respectively, and then resumes execution of the interrupted program or procedure. If the return is to another privilege level, the IRET instruction also pops the stack pointer and SS from the stack, before resuming program execution. If the return is to virtual-8086 mode, the processor also pops the data segment registers from the stack.

    -----------------------------------

    由于在sched_init()中已经设置了标志寄存器中的vm标志为0,所以iret掉用后不会发生任务切换,而是继续执行EIP指向的指令故继续执行

    1标号的代码,开始执行任务0,任务0的堆栈段选择符为0x17,在sched_init()中已设置了任务0 的任务描述符和局部描述符为INIT_TASK

    1. set_tss_desc (gdt + FIRST_TSS_ENTRY, &(init_task.task.tss));  
    2. set_ldt_desc (gdt + FIRST_LDT_ENTRY, &(init_task.task.ldt));  

    1. /* 
    2. * INIT_TASK is used to set up the first task table, touch at 
    3. * your own risk!. Base=0, limit=0x9ffff (=640kB) 
    4. */  
    5. /* 
    6. * INIT_TASK 用于设置第1 个任务表,若想修改,责任自负?! 
    7. * 基址Base = 0,段长limit = 0x9ffff(=640kB)。 
    8. */  
    9. // 对应上面任务结构的第1 个任务的信息。   
    10. #define INIT_TASK /   
    11. /* state etc */ { 0,15,15, /    // state, counter, priority   
    12. /* signals */ 0,  
    13. {  
    14.   {  
    15.   }  
    16. ,}  
    17. , 0, /              // signal, sigaction[32], blocked   
    18.                     /* ec,brk... */ 0, 0, 0, 0, 0, 0, /  
    19.                     // exit_code,start_code,end_code,end_data,brk,start_stack   
    20.                     /* pid etc.. */ 0, -1, 0, 0, 0, /  
    21.                     // pid, father, pgrp, session, leader   
    22.                     /* uid etc */ 0, 0, 0, 0, 0, 0, /  
    23.                     // uid, euid, suid, gid, egid, sgid   
    24.                     /* alarm */ 0, 0, 0, 0, 0, 0, /  
    25.                     // alarm, utime, stime, cutime, cstime, start_time   
    26.                 /* math */ 0, /  
    27.                 // used_math   
    28.                             /* fs info */ -1, 0022, NULL, NULL, NULL, 0, /  
    29.                             // tty,umask,pwd,root,executable,close_on_exec   
    30. /* filp */  
    31. {  
    32. NULL,}  
    33. , /             // filp[20]   
    34. {  
    35.   /             // ldt[3]   
    36.   {  
    37.   0, 0}  
    38.   ,  
    39. /* ldt */  
    40.   {  
    41.   0x9f, 0xc0fa00}  
    42.   , /               // 代码长640K,基址0x0,G=1,D=1,DPL=3,P=1 TYPE=0x0a   
    43.   {  
    44.   0x9f, 0xc0f200}  
    45.   , /               // 数据长640K,基址0x0,G=1,D=1,DPL=3,P=1 TYPE=0x02   
    46. }  
    47. ,  
    48. /*tss*/  
    49. {  
    50.   0, PAGE_SIZE + (long) &init_task, 0x10, 0, 0, 0, 0, (long) &pg_dir, / // tss   
    51.     0, 0, 0, 0, 0, 0, 0, 0,  
    52.     0, 0, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, _LDT (0), 0x80000000,  
    53.   {  
    54.   }  
    55. }  
    56. ,}  

  • 相关阅读:
    类的访问级别
    包和静态引入
    数组
    流程控制
    适合初学者的ROS机器人教程(3): ROS下使用Python对UR5机器人建模与控制
    适合初学者的强化学习教程(1): python使用gym实践和注意事项
    适合初学者的ROS机器人教程(1): Ubuntu下ROS创建自己的包和使用github下载的包
    适合初学者的ROS机器人教程(2): Ubuntu下ROS使用Gazebo和Rviz对UR5机器人建模
    spyder使用IPython的ipdb调试
    mysql创建外键
  • 原文地址:https://www.cnblogs.com/wangfengju/p/6173172.html
Copyright © 2011-2022 走看看