java开发系统内核:进程切换

更详细的讲解和代码调试演示过程,请参看视频
Linux kernel Hacker, 从零构建自己的内核

上一节,我们初步介绍了进程相关的具体概念,特别是讲解了进程切换相关的数据结构,也就是TSS,也实现了进程的自我切换,本节,我们看看如何从当前的进程切换到新进程,然后再切换回来,也就是:

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

我们先看看进程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.

从运行过程上看,当进程A运行时,有一个光标会在文本框中不断的闪烁:


这里写图片描述

一旦跳转到task_b_main, 桌面会打印出相关字符串,然后光标会停止住,等5庙后,进程从task_b_main,切换回进程A,进程A恢复执行,于是在卡死5秒后,在跳转会进程A前,task_b_main会打印出一条语句"switch back",当这条语句出现在桌面上时,控制器转回到进程A, 于是光标会重新开始闪烁。

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


这里写图片描述

更加详细的讲解和调试演示请参看视频。

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:


这里写图片描述
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容