更详细的讲解和代码调试演示过程,请参看视频
用java开发C语言编译器java
更详细的讲解和代码调试演示过程,请参看视频
如何进入google,算法面试技能全面提高指南面试
若是你对机器学习感兴趣,请参看一下连接:
机器学习:神经网络导论算法
更详细的讲解和代码调试演示过程,请参看视频
Linux kernel Hacker, 从零构建本身的内核api
微软早期的DOS系统,存在一个严重的问题是,若是应用程序运行出现问题,它会致使整个系统彻底奔溃掉,咱们当前的系统内核也存在这一的问题,例如打开api_call.asm,其内容以下:网络
[SECTION .s32]
BITS 32
call main
retfapp
api_putchar:
mov edx, 1
mov al, [www.vboyule66.cn 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执行了无效指令进而致使系统的总体奔溃,接下来咱们看看如何处理让系统从这种严重错误中恢复回来,这样就能够防止应用程序执行恶意代码而对系统形成伤害。google
当CPU在执行指令出现错误时,例如遇到了无效指令,那么CPU会出发0Dh号中断,让该中断来处理当前所出现的异常局面,若是该中断没法处理,那么CPU就会中止运行,因而就出现了上面的状况。为了让系统能从错误中恢复,咱们须要实现0Dh号中断,在中断中,咱们直接结束掉当前正在运行的应用程序,直接把CPU的控制全交还给内核。
咱们如今kernel.asm中注册0Dh号中断,代码修改以下:
LABEL_IDT:
%rep 13
Gate SelectorCode32, SpuriousHandler,0, DA_386IGate
%endrep
.0Dh:
Gate SelectorCode32, www.yszxylpt.com exceptionHandler,0, DA_386IGate
%rep 18
Gate SelectorCode32, www.boayulevip.cn 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, www.vboyl130.cn 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号中断确实被调用了,两行字符串被显示到控制台,而后内核能够正常继续运行而不会像前面的例子同样,整个系统会奔溃掉。
更详实的讲解和代码演示,请参看视频。