更詳細的講解和代碼調試演示過程,請參看視頻
用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號中斷確實被調用了,兩行字符串被顯示到控制檯,而後內核能夠正常繼續運行而不會像前面的例子同樣,整個系統會奔潰掉。
更詳實的講解和代碼演示,請參看視頻。