注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

中断中如何找到内核态堆栈  

2013-09-08 23:54:51|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、堆栈切换
在386手册中说明,如果中断发生在用户态,此时将会由CPU直接进入内核态,在进入内核态的过程中,cpu完成硬件的堆栈和地址切换,此时会将用户态的堆栈地址esp和下一条指令地址esp压入内核态中断,找到对应中断描述符中指定的地址,跳转到该地址,并且切换到内核态堆栈。这里中断描述符指定的位置并不太难理解,中断描述符表由idtr寄存器指向,而中断向量数值CPU又是知道的,所以顺藤摸瓜,相当于一个数组寻址的过程。
问题是此时内核态堆栈的地址在哪里?所有的书中都会说内核态堆栈就是当前进程的task_struct结构的高地址开始的问题,问题是此时处理器如何知道该位置的地址,内核中又是如何实现的?
二、tr寄存器
除了比较熟悉的ldtr/gdtr、idtr之外,还有一个当前进程寄存器tr,在特权级从用户态切换到内核态时,CPU通过寄存器tr指向的任务描述符结构来找到其中保存的esp寄存器,也就是内核态的esp位置。
该变量在内核的初始化时赋值,之后在系统运行过程中该地址始终保持不变,每次切换进程时,只是修改该结构中esp指针位置,而tr寄存器内的值不会变化。
三、init_tss定义及初始化
linux-2.6.21\include\asm-i386\processor.h

extern struct tss_struct doublefault_tss;
DECLARE_PER_CPU(struct tss_struct, init_tss);

linux-2.6.21\arch\i386\kernel\init_task.c

/*
 * per-CPU TSS segments. Threads are completely 'soft' on Linux,
 * no more per-task TSS's.
 */
DEFINE_PER_CPU(struct tss_struct, init_tss) ____cacheline_internodealigned_in_smp = INIT_TSS;

linux-2.6.21\arch\i386\kernel\cpu\common.c
/* Common CPU init for both boot and secondary CPUs */
static void __cpuinit _cpu_init(int cpu, struct task_struct *curr)
{
    struct tss_struct * t = &per_cpu(init_tss, cpu);
……
    set_tss_desc(cpu,t);
    load_TR_desc(); 这两条指令完成tss在gdt表中的注册,并加载tr寄存器的值。
}
四、进程切换时变化
linux-2.6.21\include\asm-i386\system.h
/*
 * Saving eflags is important. It switches not only IOPL between tasks,
 * it also protects other tasks from NT leaking through sysenter etc.
 */
#define switch_to(prev,next,last) do {                    \
    unsigned long esi,edi;                        \
    asm volatile("pushfl\n\t"        /* Save flags */    \
             "pushl %%ebp\n\t"                    \
             "movl %%esp,%0\n\t"    /* save ESP */        \
             "movl %5,%%esp\n\t"    /* restore ESP */    \
             "movl $1f,%1\n\t"        /* save EIP */        \
             "pushl %6\n\t"        /* restore EIP */    \
             "jmp __switch_to\n"                \
             "1:\t"                        \
             "popl %%ebp\n\t"                    \
             "popfl"                        \
             :"=m" (prev->thread.esp),"=m" (prev->thread.eip),    \
              "=a" (last),"=S" (esi),"=D" (edi)            \
             :"m" (next->thread.esp),"m" (next->thread.eip),    \
              "2" (prev), "d" (next));                \
} while (0)

struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
{
    struct tss_struct *tss = &per_cpu(init_tss, cpu);
……
    /*
     * Reload esp0.
     */
    load_esp0(tss, next);
……
}

static inline void load_esp0(struct tss_struct *tss, struct thread_struct *thread)
{
    tss->esp0 = thread->esp0; 其中的esp0即0特权级(内核)中使用的堆栈指针的栈顶位置,而thread->esp0则在task_struct初始化时初始化为task_struct的最顶端位置,在进程复制过程中拷贝父进程结构,代码在copy_thread函数中
    /* This can only happen when SEP is enabled, no need to test "SEP"arately */
    if (unlikely(tss->ss1 != thread->sysenter_cs)) {
        tss->ss1 = thread->sysenter_cs;
        wrmsr(MSR_IA32_SYSENTER_CS, thread->sysenter_cs, 0);
    }
}
  评论这张
 
阅读(478)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017