Linux0.11内核栈切换的一些问题分析

在网上流传的方法如下

以及如下

 

以上两个代码段,当进行第一次fork时,看似没有问题,但是如果在Linux0.11上的根文件系统里编译Linux0.11源码时,可能会出现一些程序错误,我们深入分析会发现,在后续的任务调度中,当程序执行到switch_to时,理论上来说是需要保护所有的寄存器现场,但如上的代码有好几个寄存器在任务切换时没有保存,经过分析修改代码如下:

复制代码
.align 4
switch_to_by_stack:
    pushl %ebp                      # 入栈保存ebp
    movl %esp,%ebp                  # 将当前的栈指针存放在ebp中,C语言函数掉用规范
    pushl %edx                      # 入栈保存edx
    pushl %ecx                      # 入栈保存ecx
    pushl %ebx                      # 入栈保存ebx
    pushl %eax                      # 入栈保存eax
    pushl %edi                      # 入栈保存edi
    pushl %esi                      # 入栈保存esi
    pushfl                          # 入栈保存eflags
    push %gs                        # 入栈保存gs
    push %fs                        # 入栈保存fs
    push %es                        # 入栈保存es
    push %ds                        # 入栈保存ds
    #
    # 以上代码就是常说的保存现场
    #
    movl 8(%ebp),%ebx               # *(ebp + 8)存放的是pnext, ebp = [EIP, CS, pnext, ldt, cr]
    cmpl %ebx,current               # 判断要切换的任务和当前任务是不是一样
    je 1f                           # 如果一样跳转到1处
    # switch_to PCB
    cli
    movl %ebx,%eax                  # pnext赋值给ebx
    xchgl %eax,current              # 交换current和eax,current目前是pnext了
    # rewrite TSS pointer
    movl tss,%ecx                   # 当前tss段地址
    addl $4096,%ebx                 # ebx是pnext(task struct)的顶端,也就是栈顶
    movl %ebx,4(%ecx)               # 4表示esp0的偏移,设置tss的内核态指针为task的顶端
    # switch_to system core stack
    movl %esp,stack_top(%eax)       # 当前esp存放到old task->stack_stop
    movl 8(%ebp),%ebx               # ebx为pnext
    movl stack_top(%ebx),%esp       # 使用pnext恢复栈
    # switch_to LDT
    movl 12(%ebp), %ecx             # 获取局部描述符
    lldt %cx                        # 加载局部描述符
    movl $0x17,%ecx                 # 设置fs为0x17
    mov %cx,%fs
    # get pnext->page dir base
    movl 16(%ebp), %ecx             # 获取CR3
    movl %ecx,%cr3                  # 设置CR3
    sti
    # nonsense
    cmpl %eax,last_task_used_math 
    jne 1f
    clts
1:   
    #
    # 为什么不保存SS,SP,CS,IP
    # 切换肯定发生内核态,SS不发生变化,而SP在保存在进程的stack_top中
    # CS, IP 通过函数调用call指令已经保存
    # 因此不用保存
    # 为什么要保存eflags,因此eflags保存了一些中断状态,溢出参数等
    # 如下代码就是常说的恢复现场
    #
    pop %ds
    pop %es
    pop %fs
    pop %gs
    popfl
    popl %esi
    popl %edi
    popl %eax
    popl %ebx
    popl %ecx
    popl %edx
    popl %ebp
    ret

.align 4
first_return_from_kernel: 
    iret
复制代码

以上代码设置CR3的代码可不用关注,因为我是将Linux0.11修改为0-3GB为用户空间,3GB-4GB为内核空间的地址模型,在原始的Linux0.11上切换时不用切换页表

当然也要修改fork中的代码

复制代码
    stack_top = (long *)(PAGE_SIZE + (long)p);
    *(--stack_top) = ss & 0xffff;
    *(--stack_top) = esp;
    *(--stack_top) = eflags;
    *(--stack_top) = cs & 0xffff;
    *(--stack_top) = eip;
    *(--stack_top) = (long)first_return_from_kernel;
    *(--stack_top) = ebp;
    *(--stack_top) = edx;
    *(--stack_top) = ecx;
    *(--stack_top) = ebx;
    *(--stack_top) = 0;     /*为什么进程返回0的原因*/
    *(--stack_top) = edi;
    *(--stack_top) = esi;
    *(--stack_top) = eflags;
    *(--stack_top) = gs & 0xffff;
    *(--stack_top) = fs & 0xffff; 
    *(--stack_top) = es & 0xffff; 
    *(--stack_top) = ds & 0xffff; 
    p->stack_top = (long)stack_top;
复制代码

经过如上修改,代码运行OK,在跟文件系统里面编译Linux0.11也没有问题

 

posted on   sudochen  阅读(114)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示