[汇总]POK(X86处理器下)分析文档
POK分析文档(X86处理器环境)
(1) 每个线程都有一个内核栈空间,用于保存进行线程现场切换时的
环境寄存器;还有一个用户栈空间,从分区内存中分配空间,保
存用户程序执行中进行
函
关于工期滞后的函关于工程严重滞后的函关于工程进度滞后的回复函关于征求同志党风廉政意见的函关于征求廉洁自律情况的复函
数调用时需要的环境寄存器。
(2) 栈空间保存现场时(即,入栈)从高地址向低地址生长;而恢复
现场时(即,出栈),顺序正好相反,从低地址向高地址生长。
(3) 函数调用时的参数也是通过当前的栈空间传递的。
(4) 在X86下,发生中断时,CPU自动保存eflag、cs和eip三个寄存
器到当前的栈空间中(注:当发生error时,会自动保存error类
型到栈空间中),而执行iret时,则按相反顺序将上述三个寄存器
从堆栈弹出到对应的寄存器中。 (5) X86下,因为不支持中断嵌套,所以,中断都是在线程执行中发
生的,而且发生中断时,CPU会自动从TSS中获取ESP0,作为
当前的栈空间指针,而在每个线程第一次执行调用
pok_dispatch_space()时,已经将线程的内核栈空间(对应的结构
体为space_context_t)赋予了此ESP0。 (6) 线程第一次被执行时,通过执行现场切换函数:
pok_context_switch(),将当前CPU的esp赋值为新线程的sp,即
在调用pok_space_context_create()时创建的线程现场栈空间(对应
的结构体为:space_context_t)。所以,新线程的现场出栈后,CPU
执行的第一条指令为函数:pok_dispatch_space(),且其使用的参
数即为调用pok_space_context_create()时,赋给sp(即,
space_context_t)的值,此函数生成描述符后,还会执行一个出栈
操作,但这次使用的是interrupt_frame栈,且出栈前先初始化此
栈中的数值,使得出栈后,令CPU从线程对应的函数体的第一条语句开始执行。详细情况参见函数pok_dispatch_space()。这样做的目的是:1)切换到线程的用户栈空间;2)令每个线程的内核栈空间都包含两部分:interrupt_frame+context_t,从而在线程运行中被中断时能有两部分栈空间保存信息,其中的interrupt_frame
中的
内容
财务内部控制制度的内容财务内部控制制度的内容人员招聘与配置的内容项目成本控制的内容消防安全演练内容
被用于再次被调度执行时,执行中断处理程序中call update_tss后面的出栈代码时使用(当然,进入中断时也会被使用)。总之,实现出栈入栈的对称和谐。
(7) 与现场切换相关的执行过程描述如下:1)线程在执行过程中,发生中断,中断类型可能是定时器中断、系统调用中断或者其它异常中断。2)CPU自动从tss描述符中取出当前可用的栈空间指针(注:此指针在pok_dispatch_space()中调用tss_set_esp0()函数时已经被初始化为当前线程的内核栈空间)保存eflag、cs和eip三个寄存器,之后执行名为具体name(name视宏中的定义而定)的中断处理程序,保存后续的栈空间,具体内容见“图1-中断发生时线程内核栈空间的变化图右侧”。3)中断处理程序调用具体的name_handler()函数(name视宏中的定义而定),在大部分情况下,通过此函数会进入调度内核,并在最后可能进行线程的现场切换。4)如果发生线程的现场切换,则首先保存当前线程的现场,具体内容见“图1-中断发生时线程内核栈空间的变化图右侧”中栈空间下半部分的变化。而对于换入的新线程,其栈空间弹出过程正好与此过程相反。见“图1-中断发生时线程内核栈空间的变化图左侧”,包含了从函数“pok_context_switch”中退出时,以及紧接着从中断处理程序退出时栈空间的变化。需要注意的是:若发生现场切换,被换出的当前线程仍然在执行中断处理程序中,执行pok_context_switch时保存的现场也是还没有执行完的中断处理程序现场,只有下次再被执行时,才从
pok_context_switch()->pok_sched()->call update_tss->执行中断处理程序的下半部分,一路退出。
(8) 关键结构体分析
1)每个结构体中前面的元素存储时在低地址,后面的元素在高地址;
typedef struct
{
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t __esp;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
uint32_t eip;
uint32_t cs;
uint32_t eflags;
} context_t; 是线程切换时需要保存的现场,是线程完整内核栈空间的一部分,另一部分
是interrupt_frame。因为入栈时是从高地址向低地址生长,所以首先保存eflags,之后是cs,
然后是eip,以此类推。出栈时顺序正好反过来。 typedef struct
{
context_t ctx; 指向上一个结构体,即,保存线程现场所用栈空间
uint32_t fake_ret;
unsigned int partition_id; 分区ID号
uint32_t user_pc; 线程执行体执行的当前点
uint32_t user_sp; 线程的用户栈空间,其作用前面已描述
uint32_t kernel_sp; 线程的系统栈空间,其作用前面已描述
uint32_t arg1; 线程执行体函数参数1
uint32_t arg2; 线程执行体函数参数2 } space_context_t;
typedef struct
{
uint32_t es;
uint32_t ds;
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t __esp;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
/* These are pushed by interrupt */
uint32_t error; /* Error code or padding */
uint32_t eip;
uint32_t cs;
uint32_t eflags;
/* Only pushed with privilege switch */
/* (Check cs content to have original CPL) */
uint32_t esp;
uint32_t ss;
} interrupt_frame;用于中断时保存现场,线程第一次执行时恢复现场,在context_t之上,是整个线程内核栈空间的一部分,另一部分是context_t。
请仔细阅读分析下图1-中断发生时线程内核栈空间的变化图,其中也包含了发生现场切换时的变化过程。若没有发生现场切换,则不包含下半部分的变化。
eflags产生中断时CPU自中断处理程序的下半部分,cs动压栈的执行iret时出栈的寄存器组寄存器组eip
中断处理程序的下半部分,执行erroraddl $4, %esp时跳过的4个字节
eax
ecx
edx“popa”interrupt_frame结构体定压入堆栈义的内容ebx中断处理程序中调用name_handler中断处理程序的下半的寄存器之前保存到线程内核栈空间中的部分,执行popa 时出组__esp内容栈的寄存器组
ebp地地
址址esi
由由此“esp”用作函数:ediname##_handler(interrupt_frame* 低高frame)中的参数“frame”,此参数除ds中断处理程序的下半部分,执行pop 到到定时器中断处理程序不用外,其它中后续指令%es 和 pop %ds 时跳过的8个字节压入堆栈断,包括系统调用都会用到es高低的寄存器中断处理程序的下半部分,执行,,上半部分espaddl $4, %esp 时跳过的4个字节也也在执行pok_context_switch()函数前,执行其它函数时,此处的栈new_sp(即程序中的 52(%esp))下半部分编译器会自动生成弹出是是空间还会不断地对称增长和缩减,但到执行pok_context_switch()此参数的代码old_sp(即程序中的 48(%esp))出入函数时,压入堆栈的是这两个参数,压栈代码由编译器自动生成
栈栈调用函数pok_context_switch的返回地址执行ret时将此值恢复到eip中恢保eflags在函数复存pok_context_switch在函数pok_context_switch()cs时时中执行iret时出栈中通过指令压入的的寄存器组顺顺ret(出栈时eip的值)序序eax注意:据此,未来此线程再次被调度执行时,现场切换完成,此线程现场被换ecx入后执行的第一条指令就是ret,此指令context_t可令esp值加4,从而跳过保存保存old_sp结构体定edx和new_sp的8个字节。义的内容在函数“popa”ebxpok_context_switch压入堆栈中执行popa时出栈的寄存器__esp的寄存器组组
ebp
esi
edi