MacOS环境-手写操作系统-30-进程之间互相切换

本文涉及的产品
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
公网NAT网关,每月750个小时 15CU
简介: MacOS环境-手写操作系统-30-进程之间互相切换

进程之间互相切换

1.简介

上一节 我们初步介绍了进程相关的具体概念


特别是讲解了进程切换相关的数据结构 也就是TSS


也实现了进程的自我切换


本节 我们看看如何从当前的进程切换到新进程 然后再切换回来


进程A -切换->进程B-切换->进程A.


2.代码

先看看进程B的实现 一个进程主要包含一个主函数 我们把进程B的主函数实现如下

void task_b_main(void) {
   showString(shtctl, sht_back, 0, 144, COL8_FFFFFF, "enter task b");

    struct FIFO8 timerinfo_b;
    char timerbuf_b[8];
    struct TIMER *timer_b = 0;

    int i = 0;

    fifo8_init(&timerinfo_b, 8, timerbuf_b);
    timer_b = timer_alloc();
    timer_init(timer_b, &timerinfo_b, 123);

    timer_settime(timer_b, 500);


    for(;;) {

       io_cli();
        if (fifo8_status(&timerinfo_b) == 0) {
            io_sti();
        } else {
           i = fifo8_get(&timerinfo_b);
           io_sti();
           if (i == 123) {
               showString(shtctl, sht_back, 0, 160, COL8_FFFFFF, "switch back");
               taskswitch7();
           }

        }

    }

}

进程B函数的逻辑是这样的


当进入到进程B后 通过它的主函数现在桌面上打印出一个字符串”enter task b”


当这个字符串出现在桌面时 表示进程完成了切换


然后它初始化一个时钟 这个时钟超时是五秒 五秒过后 它调用函数taskswitch7重新切回到进程A


进程A就是主入口函数CMain 既然要切换进程B


那显然 我们需要一个描述进程B的TSS结构并进行相应的初始化


代码如下

int addr_code32 = get_code32_addr();
 tss_b.eip =  (task_b_main - addr_code32);
    tss_b.eflags = 0x00000202; 
    tss_b.eax = 0;
    tss_b.ecx = 0;
    tss_b.edx = 0;
    tss_b.ebx = 0;
    tss_b.esp = 1024;//tss_a.esp;
    tss_b.ebp = 0;
    tss_b.esi = 0;
    tss_b.edi = 0;
    tss_b.es = tss_a.es;
    tss_b.cs = tss_a.cs;//6 * 8;
    tss_b.ss = tss_a.ss;
    tss_b.ds = tss_a.ds;
    tss_b.fs = tss_a.fs;
    tss_b.gs = tss_a.gs;

上面的代码需要详细解释下


首先我们把tss_b.eflags设置成0x202


这个值可以当做一个写死的值


然后 我们把进程B的段寄存器设置成跟A一样


我们看看进程A的各个段寄存器分别指向哪个全局描述符 tss_a.cs 的值是8


对应全局描述符表的下标就是1(数值要除以8,上一节讲解过)


下标为1的描述符是这样的


LABEL_DESC_CODE32:  Descriptor        0,      0fffffh,       DA_CR | DA_32 | DA_LIMIT_4K


这个描述符指向一段内存 这段内存的性质是可执行代码段


这段内存的起始地址在内核的汇编部分进行了初始化 如下


xor   eax, eax
     mov   ax,  cs
     shl   eax, 4
     add   eax, LABEL_SEG_CODE32
     mov   word [LABEL_DESC_CODE32 + 2], ax
     shr   eax, 16
     mov   byte [LABEL_DESC_CODE32 + 4], al
     mov   byte [LABEL_DESC_CODE32 + 7], ah


上面的代码把描述符指向的内存地址的起始位置设置为LABEL_SEG_CODE32


tss_a.ds 的值为24 除以8后为3 也就是对应描述符在全局描述符表中的下标是3 这个描述符内容如下


LABEL_DESC_VRAM:    Descriptor        0,         0fffffh,            DA_DRWA | DA_LIMIT_

4K


这个描述符指向的内存起始地址是0 长度为0fffffh


这段内存的性质是可读写数据段 也就是从0到0fffffh 这段长度的内存是可读写的数据


tss_a.ss 的值是32 除以8后得4 因此对应的是下标为4的描述符 该描述符的内容如下


LABEL_DESC_STACK:   Descriptor        0,             LenOfStackSection,        DA_DRWA | DA_32


它描述的是一段32位可读写的内存 长度为LenOfStackSection


它对应的这段内存是我们在内核的汇编部分分配的内存 具体如下


[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512  db 0
TopOfStack1  equ  $ - LABEL_STACK
times 512 db 0
TopOfStack2 equ $ - LABEL_STACK
LenOfStackSection equ $ - LABEL_STACK


上面分配了两个512字节 总共1024字节的内存


LABEL_STACK将会设置成下标为4的描述符所对应内存的起始地址


第一个512字节 作为进程A的堆栈


第二个512字节 将作为进程B的堆栈 上面tss_b的初始化代码中有这么一句:

tss_b.esp = 1024;


它的作用就是让进程把把堆栈指针指向第二个512字节的末尾处 大家要记得


堆栈是有高地址向低地址生长的 所以设置堆栈指针时 要把它指向内存的末尾


在内核的汇编部分 有代码将下标为4的描述符对应的内容起始地址设置为了LABEL_STACK


代码如下


xor   eax, eax
     mov   ax,  cs
     shl   eax, 4
     add   eax, LABEL_STACK
     mov   word [LABEL_DESC_STACK + 2], ax
     shr   eax, 16
     mov   byte [LABEL_DESC_STACK + 4], al
     mov   byte [LABEL_DESC_STACK + 7], ah


最重要的三个段寄存器 cs, ds, ss,设置好


其余寄存器 设置成跟进程A一样即可


接下来最重要的设置是eip指针 这个指针将指向要执行代码的首地址


我们要执行的函数是task_b_main


因此eip应该指向这个函数 但注意 我们不能直接把这个函数的地址直接赋值给eip


eip指向的是相对于代码段起始地址的偏移 当前代码段的其实地址是LABEL_SEG_CODE32


因此我们需要把task_b_main的地址减去LABEL_SEG_CODE32


所得的结果就是相对偏移了


这也是eip初始化的逻辑


tss_b.eip = (task_b_main - addr_code32);


get_code32_addr是内核的汇编部分实现的行数


目的就是返回LABEL_SEG_CODE32对应的地址


实现如下


get_code32_addr:
        mov  eax, LABEL_SEG_CODE32
        ret


上一节 我们已经看到


我们通过代码 将一个描述符指向结构tss_b了


代码如下


set_segmdesc(gdt + 9, 103, (int) &tss_b, AR_TSS32);


指向tss_b结构的描述符下标是9 初始化好tss_b后


只要通过一个jmp语句 跳转到下标为9的描述符


那么就能将当前指向进程切换成运行task_b_main的进程了 这个跳转语句实现如下


taskswitch9:
        jmp 9*8:0
        ret


进程A运行的是CMain函数 它会创建一个5秒的计时器 一旦超时 则调用上面的函数实现任务切换


for(;;) {
.....
else if (fifo8_status(&timerinfo) != 0) {
           io_sti();
           int i = fifo8_get(&timerinfo);
           if (i == 10) {
               showString(shtctl, sht_back, 0, 176, COL8_FFFFFF, "switch to task b");
                //switch task 
               taskswitch9();
           }
.....
}


在跳转前 我们会在桌面上打印出一句switch to task b表示即将进行任务切换


task_b_main的实现我们已经看过了 进入task_b_main后


它会在桌面打印一条语句 表示跳转成功


然后启动一个5秒的计时器 五秒过后 通过taskswitch7重新跳转回进程A


一旦跳转到task_b_main 桌面会打印出相关字符串


然后光标会停止住 等5秒后


进程从task_b_main 切换回进程A,进程A恢复执行


于是在卡死5秒后 在跳转会进程A前


task_b_main会打印出一条语句”switch back”


当这条语句出现在桌面上时 控制器转回到进程A 于是光标会重新开始闪烁


这样的话 我们就实现了进程从A切换到B再从B切换回A的整个流程


3.编译运行

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
基于阿里云,构建一个企业web应用上云经典架构,让IT从业者体验企业级架构的实战训练。
高可用应用架构
欢迎来到“高可用应用架构”课程,本课程是“弹性计算Clouder系列认证“中的阶段四课程。本课程重点向您阐述了云服务器ECS的高可用部署方案,包含了弹性公网IP和负载均衡的概念及操作,通过本课程的学习您将了解在平时工作中,如何利用负载均衡和多台云服务器组建高可用应用架构,并通过弹性公网IP的方式对外提供稳定的互联网接入,使得您的网站更加稳定的同时可以接受更多人访问,掌握在阿里云上构建企业级大流量网站场景的方法。 学习完本课程后,您将能够: 理解高可用架构的含义并掌握基本实现方法 理解弹性公网IP的概念、功能以及应用场景 理解负载均衡的概念、功能以及应用场景 掌握网站高并发时如何处理的基本思路 完成多台Web服务器的负载均衡,从而实现高可用、高并发流量架构
目录
打赏
0
0
0
0
108
分享
相关文章
Arista cEOS 4.30.10M - 针对云原生环境设计的容器化网络操作系统
Arista cEOS 4.30.10M - 针对云原生环境设计的容器化网络操作系统
75 0
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
226 1
在macOS系统上配置Flutter环境的步骤
在macOS系统上配置Flutter环境的步骤
390 62
阿里云操作系统控制台:高效解决性能瓶颈与抖动之进程热点追踪
遇到“进程性能瓶颈导致业务异常”等多项业务痛点时,提供高效解决方案,并展示案例。
【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
212 10
【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
2025年1月9日更新Windows操作系统个人使用-禁用掉一下一些不必要的服务-关闭占用资源的进程-禁用服务提升系统运行速度-让电脑不再卡顿-优雅草央千澈-长期更新
2025年1月9日更新Windows操作系统个人使用-禁用掉一下一些不必要的服务-关闭占用资源的进程-禁用服务提升系统运行速度-让电脑不再卡顿-优雅草央千澈-长期更新
475 2
2025年1月9日更新Windows操作系统个人使用-禁用掉一下一些不必要的服务-关闭占用资源的进程-禁用服务提升系统运行速度-让电脑不再卡顿-优雅草央千澈-长期更新
深入理解操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅,从进程管理的基本概念出发,逐步探索到内存管理的高级技巧。我们将通过实际代码示例,揭示操作系统如何高效地调度和优化资源,确保系统稳定运行。无论你是初学者还是有一定基础的开发者,这篇文章都将为你打开一扇了解操作系统深层工作原理的大门。
107 4
探索操作系统核心:从进程管理到内存分配
本文将深入探讨操作系统的两大核心功能——进程管理和内存分配。通过直观的代码示例,我们将了解如何在操作系统中实现这些基本功能,以及它们如何影响系统性能和稳定性。文章旨在为读者提供一个清晰的操作系统内部工作机制视角,同时强调理解和掌握这些概念对于任何软件开发人员的重要性。
深入理解操作系统:进程与线程的管理
在数字世界的复杂编织中,操作系统如同一位精明的指挥家,协调着每一个音符的奏响。本篇文章将带领读者穿越操作系统的幕后,探索进程与线程管理的奥秘。从进程的诞生到线程的舞蹈,我们将一起见证这场微观世界的华丽变奏。通过深入浅出的解释和生动的比喻,本文旨在揭示操作系统如何高效地处理多任务,确保系统的稳定性和效率。让我们一起跟随代码的步伐,走进操作系统的内心世界。
112 2

热门文章

最新文章

推荐镜像

更多
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问