switch_to(n)函数

来源:互联网 发布:微信接口开发教程 java 编辑:程序博客网 时间:2024/05/17 02:31


这篇文章我们来分析switch_to()函数, 分析的代码取自linux kernel 0.11 sched.h文件, 我们只为说明原理, 所以尽量简单. 其中代码注释参考了赵炯老师<Linux内核完全注释>一书, 言归正传,下面来看switch_to()函数.

switch_to(n)将切换当前任务到任务n.首先检测任务n是不是当前任务,是则直接退出.

输入: %0 – 指向_tmp                                        1% - 指向_tmp.b处, 用于存放新TSS的选择符

          dx – 新任务n的TSS段选择符 ecx – 新任务n的任务结构指针task[n].

_tmp用于jump指令的操作数, 其中a值是32位偏移地址, 而b的低2字节是新TSS段的选择符. 细心的读者看到a并没有被赋值,可以思考一下这是为什么

#define switch_to(n) {\struct {long a,b;} __tmp; \__asm__("cmpl %%ecx,_current\n\t" \"je 1f\n\t" \"movw %%dx,%1\n\t" \"xchgl %%ecx,_current\n\t" \"ljmp %0\n\t" \"cmpl %%ecx,_last_task_used_math\n\t" \"jne 1f\n\t" \"clts\n" \"1:" \::"m" (*&__tmp.a),"m" (*&__tmp.b), \"d" (_TSS(n)),"c" ((long) task[n])); \}

3. 检查任务n是当前任务吗?

4. 是则什么都不做,退出

5. 将新任务TSS的16位选择子存入_tmp.b中.

6. 当前任务变为task[n]; ecx = 被切换出的任务.

7. 长跳转到*&_tmp, 造成任务切换. 我们可以看之前的一篇文章<JMP指令>, 其中代码34行, 指明了任务切换时要调用的函数TaskSwitch(), 下面我们将分析这个函数具体要做什么.

8. 原任务使用过协处理器吗

9. 没有,则跳转退出.

10. 原任务使用过协处理器, 清cr0中的任务切换标志TS

下面介绍<JMP指令>一文中伪代码函数TaskSwitch()做了哪些事情


TaskSwitch(SelectorType Selector, SegAttributes Attributes, int Base, int Limit, int Linkage){SelectorType OldTSS;if(Limit < 103)SegmentException($TS, Selector);AccessTSSState(1); //WriteTR.Base = Base;TR.Limit = Limit;TR.Attributes = Attributes;OldTSS = TR.Selector; TR.Selector = Selector;AccessTSSState(0); //readif(Linkage == 1){AccessLinear(TR.Base, 2, 0, 1, &OldTSS); EFLAGS.NT = 1;SetTSSBusy(Selector, 1);}else if(Linkage == -1)SetTSSBusy(OldTSS, 0);else if(Linkage == 0){SetTSSBusy(OldTSS, 0);SetTSSBusy(Selector, 1);}CR0.TS = 1;CPL = CS.Selector.RPL;LDTR.Attributes.Present = 0;CS.Attributes.Present = 0;SS.Attributes.Present = 0;CS.Attributes.Present = 0;DS.Attributes.Present = 0;ES.Attributes.Present = 0;FS.Attributes.Present = 0;GS.Attributes.Present = 0;if(LDTR.Selector.TI == 1)SegmentException($TS, LDTR.Selector);if((LDTR.Selector & 0FFFCh) == 0)LDTR.Attributes.P = 0;else{ReadDescriptor(LDTR.Selector, &Attributes, &Base, &Limit, &GSelector, &GOffset);if((Attributes.DType == 1)|| (Attributes.Type != 2)|| (Attributes.Present == 0))SegmentException($TS, LDTR.Selector);SetAccessed(LDTR.Selector);LDTR.Attributes = Attributes;LDTR.Base = Base;LDTR.Limit = Limit;}JumpGate(CS.Selector, EIP, $TS);SRegLoad(SS, SS.Selector, $TS);SRegLoad(DS, SS.Selector, $TS);SRegLoad(ES, SS.Selector, $TS);SRegLoad(FS, SS.Selector, $TS);SRegLoad(GS, SS.Selector, $TS);}

4. 任务状态段至少有104个字节

6. 当前机器的状态(EIP, EFLAGS,EAX,ECX, EDX, ESP, EBP…….)保存到老任务的TSS中

7-11 TR及投影寄存器指向新任务的TSS, 原来任务的selector保存到OldTSS中

12. 把新任务TSS中的内容装入到硬件寄存器(EIP,EFLAGS, EAX,ECX, EDX, ESP, EBP…….)中.

13-25 根据链家字段的值做不同的处理

26-27 设置任务切换位及当前特权级

28-35 各描述符投影寄存器存在位预置为0

36-37 LDTR的选择子必须指定全局描述符表,否则产生异常

38-39 局部描述符表可以为空, 如为空, 将描述符表存在位设置为0

40. 局部描述符表非空

41-51 读出描述符的属性, 基地址和段界限. 检查出错,产生段异常. 之后装入局部描述符表投影寄存器.

52. 装入CS投影寄存器

53-57 装入SS, DS, ES, FS, GS投影寄存器

下面给出JumpGate的伪代码, 经过<JMP一文>,相信读者可以自行分析.

JumpGate(SelectorType Selector, int Offset, int GP){    SegAttributes Attributes;    SelectorType GSelector;    int Base, Limit, GOffset;    if((Selector&0FFFCh) == 0)        SegmentException(GP, 0);    ReadDescriptor(Selector,&Attributes,&Base,&Limit,&GSelector,&GOffset);    if(Attributes.DType == 0)        SegmentException(GP, Selector);    Selector.RPL = 0;      CSDescriptorLoad(Selector,Attributes,Base,Limit,GP);    if(Offset > CS.Limit)        SegmentException(GP, 0);    CS.Selector = Selector;    CS.Selector.RPL = CPL;    EIP = Offset;}


原创粉丝点击