java开发系统内核:让内核从严重错误中恢复

更详细的讲解和代码调试演示过程,请参看视频
用java开发C语言编译器

更详细的讲解和代码调试演示过程,请参看视频
如何进入google,算法面试技能全面提升指南

如果你对机器学习感兴趣,请参看一下链接:
机器学习:神经网络导论

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

微软早期的DOS系统,存在一个严重的问题是,如果应用程序运行出现问题,它会导致整个系统完全奔溃掉,我们当前的系统内核也存在这一的问题,例如打开api_call.asm,其内容如下:

[SECTION .s32]
BITS 32
call main
retf

api_putchar:
  mov edx, 1
  mov al, [esp + 4]
  int 02Dh
  ret

%include "app.asm"

call main 时CPU控制权会提交给应用程序,执行应用程序的代码,应用程序执行完毕后,返回到call main语句的下一条指令继续执行,call main 的下一条语句是retf, 它的作用是从堆栈上得到内核代码的全局描述符,把该描述符在描述符表中的下标赋值给寄存器cs, 同时把内核要执行语句的地址从堆栈上取得,并赋值给寄存器ip, 这样CPU的控制器会重新交还给内核,假设我们对上面代码做一个修改如下:

[SECTION .s32]
BITS 32
call main
pop  eax ;故意让返回地址出错造成异常保护中断
retf

....

我们在执行retf语句时,把存储在堆栈上的内核要执行的语句地址弹出,这样要返回的内核地址就会遭遇破坏,当执行retf语句后,ip指针会执行内存的任意一个无法确定的地方,于是CPU在接下来的执行中就会遇到错误,上面代码修改后运行起来,情况如下:


这里写图片描述

由于ip指针指向了无效地址,致使CPU执行了无效指令进而导致系统的整体奔溃,接下来我们看看如何处理让系统从这种严重错误中恢复回来,这样就可以防止应用程序执行恶意代码而对系统造成伤害。

当CPU在执行指令出现错误时,例如遇到了无效指令,那么CPU会出发0Dh号中断,让该中断来处理当前所出现的异常局面,如果该中断无法处理,那么CPU就会停止运行,于是就出现了上面的情况。为了让系统能从错误中恢复,我们需要实现0Dh号中断,在中断中,我们直接结束掉当前正在运行的应用程序,直接把CPU的控制全交还给内核。

我们现在kernel.asm中注册0Dh号中断,代码修改如下:

LABEL_IDT:
%rep  13
    Gate  SelectorCode32, SpuriousHandler,0, DA_386IGate
%endrep

.0Dh:
    Gate SelectorCode32, exceptionHandler,0, DA_386IGate

%rep  18
    Gate  SelectorCode32, SpuriousHandler,0, DA_386IGate
%endrep

上面代码在中断描述符表中注册了一个0Dh号中断,当中断发送时,CPU会调用exceptionHandler函数。我们看看该函数的实现,同样也是在kernel.asm中:

_exceptionHandler:
exceptionHandler equ _exceptionHandler - $$
    cli
    ;把内存段切换到内核
    mov  ax, SelectorVram
    mov  ds, ax
    mov  es, ax 
    mov  ax, SelectorStack ;切换到内核堆栈段
    mov  ss, ax
    
    mov  ecx, [0xfe4];获取内核堆栈指针
    add  ecx, -8
    mov  [ecx+4], ss ;保存中断时的堆栈段
    mov  [ecx], esp  ;保存中断时堆栈指针

    mov  esp, ecx ;切换内核指针

    call intHandlerForException
    
.kill:  ;通过把CPU交给内核的方式直接杀掉应用程序
     mov  esp, [0xfe4]
     sti
     popad
     ret

中断运行时,先把内核专有内存段的描述符赋值给寄存器ds,es,这样代码运行时可以直接读写内核的数据,同时把内核的堆栈描述符赋值给寄存器ss,这样代码运行时使用的堆栈就是内核的专有堆栈。然后把错误发生时的应用程序使用的堆栈段描述符和堆栈指针存入内核堆栈,接着调用函数intHandlerForException进行错误处理,该函数实现在内核的C语言部分,也就是在write_vga_desktop.c中,我们看看此函数的实现:

void intHandlerForException(int *esp) {
    g_Console.cur_x = 8;
    cons_putstr("INT 0D ");
    g_Console.cur_x = 8;
    g_Console.cur_y += 16;
    cons_putstr("General Protected Exception");         
    g_Console.cur_y += 16;
    g_Console.cur_x = 8;
    return 1;
}

它的做法很简单,只是在控制台上打印出两行字符串,分别是"INT OD",和"General Protected Exception",然后就返回了。回到中断实现部分,也就是.kill对应的代码,在内核把控制权交给应用程序时,会把它当时的堆栈指针存储到内存[0xfe4]处,内核时通过start_app把控制权交给应用程序的,我们再看看start_app的代码:

start_app:  ;void start_app(int eip, int cs,int esp, int ds)
    cli
    pushad
    ...
    mov  [0xfe4], esp
    ...

大家看,start_app在执行时,通过指令把当时所有通用寄存器的数据存储到堆栈上,同时把esp指针的值,也就是当时的内核堆栈指针存储到内存0xfe4这个地方,因此.kill 的指令 mov esp [0xfe4]实际上就是对上面指令mov [0xfe4], esp 的逆操作,同时popad也是对上面指令pushad的逆操作。我们再看看start_app是如何被调用的,在write_vga_desktop.c中:

void cmd_hlt() {
    ....
    start_app(0, 11*8,64*1024, 12*8);

    char *pApp = (char*)(q + 0x100);
    showString(shtctl, sht_back, 0, 179, COL8_FFFFFF, pApp);

    memman_free_4k(memman, buffer.pBuffer, buffer.length);
    memman_free_4k(memman, q, 64 * 1024);
}

start_app是在cmd_hlt中被调用的,我们知道C语言在调用子函数时,会把调用子函数后要执行的下一条指令的地址压入堆栈,因此当上面代码在调用start_app函数时,下一条指令,也就是char *pApp=(char*)这条语句的地址会被压入到堆栈上。

当.kill处的代码执行完语句popad后,此时堆栈上存储的恰好就是语句char *pApp=(char*)的地址,于是当我们执行指令ret的时候,该语句的地址会赋值给ip寄存器,这样CPU就会直接从char *pApp=(char*)这条语句开始执行,这就类似与应用程序正常执行完毕后,CPU控制权正常返还给内核的情况是一样的。

这样,当应用程序执行出现严重错误时,CPU触发0Dh号异常处理中断,在该中断中,代码会把CPU控制权直接交还给内核,着就相当于把出现异常的应用程序毁尸灭迹,内核就好像什么事都没发生过一样,继续按照老样子运行,上面代码修改后,执行错误的应用程序后,情况如下:

这里写图片描述

由此可见,当错误发生时0Dh号中断确实被调用了,两行字符串被显示到控制台,然后内核可以正常继续运行而不会像前面的例子一样,整个系统会奔溃掉。

更详实的讲解和代码演示,请参看视频。

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


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

推荐阅读更多精彩内容