MacOS环境-手写操作系统-48-让内核从错误中恢复

简介: MacOS环境-手写操作系统-48-让内核从错误中恢复

让内核从错误中恢复

1.简介

微软早期的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的控制全交还给内核


2.代码

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控制权直接交还给内核


就相当于把出现异常的应用程序毁尸灭迹


内核就好像什么事都没发生过一样 继续按照老样子运行


3.运行

目录
相关文章
|
23天前
|
存储 调度
探索操作系统的心脏:内核与用户空间的交互
在数字世界的每一次呼吸中,操作系统扮演着至关重要的角色。本文将深入探讨操作系统的核心组件——内核与用户空间之间的神秘舞蹈。通过直观的比喻和生动的代码片段,我们将一窥这场幕后的交响曲,了解它们是如何协同工作以支持我们的计算需求的。从简单的文件读写到复杂的网络通信,每一个操作背后都隐藏着内核与用户空间之间精妙的互动。准备好跟随我们的脚步,一起揭开操作系统的神秘面纱。
30 3
|
12天前
|
安全 Linux 开发者
探索操作系统的心脏:内核与用户空间的交互
在数字世界的每一次点击和命令背后,隐藏着一个复杂而精妙的操作系统世界。本文将带你走进这个世界的核心,揭示内核与用户空间的神秘交互。通过深入浅出的解释和直观的代码示例,我们将一起理解操作系统如何协调硬件资源,管理进程和内存,以及提供文件系统服务。无论你是编程新手还是资深开发者,这篇文章都将为你打开一扇通往操作系统深层原理的大门。让我们一起开始这段旅程,探索那些支撑我们日常数字生活的技术基石吧!
23 6
|
10天前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
11天前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
16天前
|
存储 Linux 开发者
探索操作系统的内核——从理论到实践
操作系统是计算机科学的核心,它像一位默默无闻的指挥官,协调着硬件和软件之间的复杂关系。本文将深入操作系统的心脏——内核,通过直观的解释和丰富的代码示例,揭示其神秘面纱。我们将一起学习进程管理、内存分配、文件系统等关键概念,并通过实际代码,体验内核编程的魅力。无论你是初学者还是有经验的开发者,这篇文章都将带给你新的视角和知识。
|
15天前
|
机器学习/深度学习 人工智能 物联网
操作系统的心脏——深入理解内核机制
在本文中,我们揭开操作系统内核的神秘面纱,探索其作为计算机系统核心的重要性。通过详细分析内核的基本功能、类型以及它如何管理硬件资源和软件进程,我们将了解内核是如何成为现代计算不可或缺的基础。此外,我们还会探讨内核设计的挑战和未来趋势,为读者提供一个全面的内核知识框架。
|
16天前
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。
|
20天前
|
存储 调度 开发者
探索操作系统的心脏:内核与用户空间的交互之旅
在数字世界的无限广阔中,操作系统扮演着枢纽的角色,连接硬件与软件,支撑起整个计算生态。本篇文章将带领读者深入操作系统的核心——内核,揭示其与用户空间的神秘交互。我们将透过生动的例子和易于理解的比喻,深入浅出地探讨这一复杂主题,旨在为非专业读者揭开操作系统的神秘面纱,同时为有一定基础的读者提供更深层次的认识。从进程管理到内存分配,从文件系统到设备驱动,每一个环节都是精确而优雅的舞蹈,它们共同编织出稳定而高效的计算体验。让我们开始这场奇妙之旅,一探操作系统背后的科学与艺术。
22 5
|
17天前
|
缓存 并行计算 Linux
深入解析Linux操作系统的内核优化策略
本文旨在探讨Linux操作系统内核的优化策略,包括内核参数调整、内存管理、CPU调度以及文件系统性能提升等方面。通过对这些关键领域的分析,我们可以理解如何有效地提高Linux系统的性能和稳定性,从而为用户提供更加流畅和高效的计算体验。
27 2
|
17天前
|
缓存 网络协议 Linux
深入探索Linux操作系统的内核优化策略####
本文旨在探讨Linux操作系统内核的优化方法,通过分析当前主流的几种内核优化技术,结合具体案例,阐述如何有效提升系统性能与稳定性。文章首先概述了Linux内核的基本结构,随后详细解析了内核优化的必要性及常用手段,包括编译优化、内核参数调整、内存管理优化等,最后通过实例展示了这些优化技巧在实际场景中的应用效果,为读者提供了一套实用的Linux内核优化指南。 ####
43 1