操作系统——内核级线程实现

写在前面:

建议看原课件梳理!!!

一、内核级线程具体实现 

例(在这里printf("A")和printf("B")变为 exec(cmd1)和exec(cmd2)):

1. 正在执行int 0x80

SS:SP、EFLAGS和 ret = 100都是通过 int0x80 硬件自动压栈

2.中断处理:system_call 

3.sys_fork:CreateThread

 4.copy_process:在执行子程序前的准备工作

  • 申请内存空间;
  • 创建TCB(TSS:TASK_STRUCT);
  • 创建内核栈和用户栈
  • 关联栈和TCB

  • fork和父进程共用用户栈
  • fork创建自己的内核栈

TASK_STRUCT(TSS结构):

  • 通过sys_fork函数,执行子程序exec(cmd1)的准备工作已经完成,此时父进程未堵塞,执行:

                                        push %eax

                                        cmpl $0,counter(%eax)

                                        ret_from_sys_call

        未跳转执行子程序,ret_from_sys_call是恢复父进程执行现场,并通过iret回到用户栈,即int 0x80后面的执行程序!

  • 返回到父进程 100:mov %eax,res(即ret=100)处,此时res!=0,执行父进程;若res=0,即%eax被置为0时,执行子程序;
  • 继续创建子程序exec(cmd2)执行前的准备工作(TSS和内核栈),放入就绪队列

至此,exec(cmd1)和exec(cmd2)的TSS被初始化完成(即sys_fork执行完毕)

然后继续向下执行:

                       push %eax 

                       mov1 _current,%eax

                       cmpl $0,state(%eax)

                       jne reschedule

                       cmpl $0,counter(%eax)

                       jne reschedule

                       ret_from_sys_call

假设此时父进程发生堵塞(wait()函数),则要执行子进程,即执行 jne reschedule

5.进入子进程,父进程等待

  • 将 $ret_from_sys_call压栈,方便后续从内核栈回到用户栈,恢复父进程执行现场
  • 跳到schedule函数
    • next = i //涉及到调度算法,即从就绪队列选择一个程序执行(exec(cmd1)和exec(cmd2)
    • switch_to(next) //线程切换,即父进程、exec(cmd1)和exec(cmd2)互相切换

 6.switch_to://线程切换

7.执行子进程

  • 首先通过system_call调用 sys_execve函数
  • 其次将%eax赋值为EIP+esp,即 %eax = esp+0x1C = 28,即原先返回父进程的地址(int0x80 后面的执行程序) 
  • 最后 eip[0] = ex.a_entry,即将原先返回父进程的地址变为执行子程序的入口;eip[3] = p,即为栈顶SP
  • 此时,ret返回的地址从原来的父进程变为子程序的入口,再次返回到用户栈时,就变为执行子程序

二、内核级线程实现大致梳理

  • 首先通过int 0x80进行中断进入内核,建立用户栈和内核栈的联系,并进行相应的中断处理(sys_call)
  • 中断处理,引发切换(保护进程现场,判断进程状态从而决定是否切换进程)
  • 调用schedule函数选择要切换到的线程并调用switch_to函数
  • 通过switch_to函数完成相应TCB切换,即相应线程切换
  • 然后通过中断出口iret,完成内核栈到用户栈的二级转换

参考文章:

L12 内核级线程实现_哔哩哔哩_bilibili

L13 操作系统之“树”_哔哩哔哩_bilibili

猜你喜欢

转载自blog.csdn.net/weixin_45864705/article/details/127428715