x01.os.9: 进程切换,x01.os.9进程切换


进入内核后,当然不能无所事事。先创建三个进程,分别打印 A,B,C。虽然只是简单的打印,但却是一切扩展的基础,不可等闲视之。

进程切换,涉及一系列的寄存器需要保护,于是,就有了 ProcessStack 结构,代码如下:

typedef struct {
    u32        gs;
    u32        fs;
    u32        es;
    u32        ds;
    u32        edi;
    u32        esi;
    u32        ebp;
    u32        KernelEsp;
    u32        ebx;
    u32        edx;
    u32        ecx;
    u32        eax;
    u32        RetAddr;
    u32        eip;
    u32        cs;
    u32        eflags;
    u32        esp;
    u32        ss;
} ProcessStack;

稍加注意,会发现有个 KernelEsp。它的作用,是防止进程切换时,栈指针乱指。

进程切换,当然离不开中断。有意思的是,涉及中断调用的任务状态栈 TSS 同 ProcessStack 竟有异曲同工之妙。

进程切换,还有一个关键,就是不能再用简单的 ret 来返回了。kernel.s 中的 save 代码如下:

save:
    pushad
    push        ds
    push        es
    push        fs
    push        gs

    mov        dx, ss
    mov        ds, dx
    mov        es, dx

    mov        esi, esp
    inc            dword [g_IntReenter]
    cmp        dword [g_IntReenter], 0
    jne            .1
    mov        esp, StackTop
    push        Restart
    jmp            [esi + P_RetAddr - P_StackBase]
.1:
    push        reenter
    jmp            [esi + P_RetAddr - P_StackBase]

Restart:
    mov        esp, [g_pProcReady]
    lldt            [esp + P_LdtSel]
    lea            esi, [esp + P_StackTop]
    mov        dword [g_Tss + TSS_ESP0], esi
reenter:
    dec            dword [g_IntReenter]

    pop        gs
    pop        fs
    pop        es
    pop        ds
    popad
    add            esp, 4
    iretd

其中的 jmp  [esi + P_RetAddr - P_StackBase] ,就是跳到事先保存的返回地址。而这一返回地址,由 main.c 中的 KernelMain 设置。即 for 循环里的 pProc->Regs.esp = (u32)pTaskStack; 这种多兵种作战,需细心体会,方能领会之。

此关键点如能领会,进程切换就不是难事。所谓进程,不过在 ProcessStack 的基础上,添加一些进程 Id,name,优先级而已。

进入工程目录,make 后,再 bochs,即可看到如下界面:

          

其中,优先级的设置为 15:5:3, 同显示的基本一致。完整代码,可到 x01.Lab.Download 中下载。虚拟机及开发工具,可参看 x01.os.7

有个问题需说明一下。就是修改后重新 make 时,会出现 /mnt/temp 忙,可运行命令 sudo umount /mnt/temp 卸载之。


何时进行进程切换,怎进行进程切换?

张婷婷,你没有悬赏分,不给你回答。作业已经做好了,哈哈哈哈
不过还是给点提示吧: 不过要把我的答案评为最佳答案,知道不?

从表面看,进程切换的功能是很简单的。在某一时刻,一个正在运行的进程被中断,操作系统指定另一个进程为运行态,并把控制权交给这个进程。但是这会引发若干问题。首先,什么事件触发进程的切换?另一个问题是必须认识到模式切换与进程切换之间的区别。最后,为实现进程切换,操作系统必须对它控制的各种数据结构做些什么?

何时切换进程

进程切换可以在操作系统从当前正在运行的进程中获得控制权的任何时刻发生

首先考虑系统中断。实际上,大多数操作系统区分两种类型的系统中断。一种称为中断,另一种称为陷阱。前者与当前正在运行的进程无关的某种类型的外部事件相关,如完成一次I/O 操作;后者与当前正在运行的进程所产生的错误或异常条件相关,如非法的文件访问。对于普通中断,控制首先转移给中断处理器,它做一些基本的辅助工作,然后转到与已经发生的特定类型的中断相关的操作系统例程。参见以下例子:

时钟中断:操作系统确定当前正在运行的进程的执行时间是否已经超过了最大允许时间段(时间片,即进程在被中断前可以执行的最大时间段),如果超过了,进程必须切换到就绪态,调入另一个进程。

I/O 中断:操作系统确定是否发生了I/O 活动。如果I/O 活动是一个或多个进程正在等待的事件,操作系统就把所有相应的阻塞态进程转换到就绪态(阻塞/挂起态进程转换到就绪/挂起态),操作系统必须决定是继续执行当前处于运行态的进程,还是让具有高优先级的就绪态进程抢占这个进程。

内存失效:处理器访问一个虚拟内存地址,且此地址单元不在内存中时,操作系统必须从外存中把包含这个引用的内存块(页或段)调入内存中。在发出调入内存块的I/O 请求之后,操作系统可能会执行一个进程切换,以恢复另一个进程的执行,发生内存失效的进程被置为阻塞态,当想要的块调入内存中时,该进程被置为就绪态。

对于陷阱,操作系统确定错误或异常条件是否是致命的。如果是,当前正在运行的进程被转换到退出态,并发生进程切换;如果不是,操作系统的动作取决于错误的种类和操作系统的设计,其行为可以是试图恢复或通知用户,操作系统可能会进行一次进程切换或者继续执行当前正在运行的进程。

最后,操作系统可能被来自正在执行的程序的系统调用激活。例如,一个用户进程正在运行,并且正在执行一条请求I/O 操作的指令,如打开文件,这个调用导致转移到作为操作系统代码一部分的一个例程上执行。通常,使用系统调用会导致把用户进程置为阻塞态。
 

模式切换与进程切换有什不同

进程切换是,一个正在运行的进程被中断,操作系统指定另一个进程为运行态,并把控制权交给这个进程。进程切换可以在操作系统从当前正在运行的进程中获得控制权的任何时刻发生,由于进程之间不同状态的切换,需要重新分配各种资源,操作系统需要做更多的工作。

模式切换是,用户态和内涵态之间的切换。因为他们的资源是共享的,所以效率高并且不改变正在运行的进程的状态。
 

相关内容