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也没有问题
分类:
Linux0.11
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律