linux0.11中head.s分析
/* *注意!32位啟動程式碼是從絕對地址0x0000 0000開始的,這裡同樣也是頁目錄 *將要存在的地方,因此啟動程式碼會被頁目錄覆蓋掉 */ .text .globl _idt,_gdt,_pg_dir,_tmp_floppy_area _pg_dir: !頁目錄將會放在這裡 startup_32: /* *注意!!!這裡已處於3位執行模式,因此這裡的0x10並不是把地址0x10放入到各個段暫存器中,它現在**其實是全域性段描述符表中的偏移值,或者更正確的說是一個描述符項的選擇符。這裡0x10含義是請求 *特權級0,選擇全域性描述符表,選擇表中的第二項。正好指向表中的資料段描述符項。 *下面程式碼含義是ds,es,fs,gs中的選擇符為setup中構造的資料段。並將堆疊放置在stack_start指向*的user_stack陣列區.然後使用本程式後面定義的新中斷描述符表和全域性段描述符表.新全域性段描述**符表中初始內容和setup中基本一樣,僅段限長修改成了16M,stack_start定義在kernel/sched.s中**,是指向user_stack陣列末端的一個長指標。 */ movl $0x10,%eax !對GNU彙編來說,每個直接數要以"$"開始要不就表示地址 mov %ax,%ds ! mov %ax,%es ! mov %ax,%fs ! mov %ax,%gs ! lss _stack_start,%esp !表示_stack_start->ss:esp,設定系統堆疊 call setup_idt !呼叫設定中斷描述符表子程式 call setup_gdt !呼叫設定全域性描述符表子程式 movl $0x10,%eax !因為修改了gdt,所以重新裝載所有的段暫存器 mov %ax,%ds ! mov %ax,%es ! mov %ax,%fs ! mov %ax,%gs ! lss _stack_start,%esp ! xorl %eax,%eax ! ! 1: ! incl %eax !檢測A20地址線是否開啟.採用的方法是向0x000000處寫入任意一 movl %eax,0x000000 !個數值,然後看記憶體0x100000處是否也是這個數值.如果一直相同 cmpl %eax,0x100000 !的話,就一直比較下去,即宕機.表示沒有選通A20,結果核心不能 je 1b !使用1M以上的記憶體 !檢查數學協處理器是否存在.方法是修改控制暫存器CR0,在假設存 !在處理器的情況下執行一個協處理器指令,出錯就不存在,需要 !設定CR0中的協處理器模擬位EM,並復位協處理器存在標誌MP movl %cr0,%eax ! andl $0x80000011,%eax !save PG,PE,ET orl $2,%eax !set MP movl %eax,%cr0 ! call check_x86 ! jmp after_page_tables ! ! check_x86: ! fninit ! fstsw %ax ! cmpb $0,%al ! je 1f ! movl %cr0,%eax ! xorl $6,%eax ! movl %eax,%cr0 ! ret ! .align 2 !儲存邊界對齊,2表示調整到地址最後2位為零,即4位元組對齊 1: ! .byte 0xDB,0xE4 ! ret ! !中斷描述符表idt有256項,並都指向ignore_int中斷門,然後載入中斷描述符表暫存器,真正實用 !的中斷門以後再安裝.認為其他地方都正常時在開啟中斷.該子程式會被也表覆蓋掉 !中斷描述符表中斷是8位元組構成,格式與全域性表不同,被稱為們描述符(Gate Descriptor).0-1,6-7字 !節是偏移量,2-3是選擇符,4-5位元組是標誌 set_idt: ! lea ignore_int,%edx !ignore_int有效地址->edx暫存器 movl $0x00080000,%eax !選擇符放入eax的高16位,selector=0x0008=cs movw %dx,%ax !偏移值低16位放入eax的低16位中.eax含有們描述符低4位元組的值 movw $0x8E00,%dx !edx含有門描述符高4位元組的值 ! lea _idt,%edi !_idt是中斷描述符表的地址 mov $256,%ecx ! rp_sidt: ! movl %eax,(%edi) !將啞中斷門描述符存入表中 movl %edx,4(%edi) ! addl $8,%edi !edi指向表中下一項 dec %ecx ! jne rp_sidt ! lidt idt_descr !載入中斷描述符表暫存器值 ret ! ! setup_gdt: ! lgdt gdt_descr !載入全域性描述符表暫存器 ret ! !核心的記憶體也表直接放在頁目錄之後,使用了4個表來定址16M實體記憶體.每個也表長4KB位元組,每個頁!!表需要4自己,因此一個頁表可以存放1024個表項,如果一個頁表項定址4KB,則一個頁表可以定址4M, !頁表項格式:項前0-11位存放一些標誌,例如是否存在記憶體中(P位0),讀寫許可(R/W位1),普通使用者還 !是超級使用者(U/S位2),是否修改過(是否髒了D位6).表項位12-31是頁框地址,用於指出一頁記憶體物理 !起始地址 .org 0x1000 !偏移0x1000處開始時第一個頁表(偏移0是頁表目錄) pg0 ! ! .org 0x2000 ! pg1 ! ! .org 0x3000 ! pg2 ! ! .org 0x4000 ! pg3 ! ! .org 0x5000 !定義下面的記憶體資料從偏移0x5000開始 !當DMA不能訪問緩衝塊時,_tmp_floppy_area記憶體塊就可供軟盤驅動使用,其地址需要對齊調整,這樣!!就不會跨越64K邊界 ! _tmp_floppy_area: ! .fill 1024,1,0 ! !這幾個入棧操作(pushl)用於為呼叫/init/main.c程式和返回作準備,前面3個入棧0應該分別是envp, !argv,argc值,但main沒有用到.pushl $L6是模擬呼叫main程式時首先將返回地址入棧的操作,所以 !main真退出時,就會返回到這裡L6繼續執行下去.入棧完成後就進行分頁處理,分頁處理完成後執行 !ret指令,此時就會將main程式地址彈出堆疊,並執行main程式去了 after_page_tables: ! pushl $0 ! pushl $0 ! pushl $0 ! pushl $L6 ! pushl $_main ! jmp setup_paging ! L6: ! jmp L6 ! int_msg: .asciz "Unknown interrupt\n\r" .align 2 ignore_int: pushl %eax ! pushl %ecx ! pushl %edx ! push %ds ! push %es ! push %fs !這裡ds es fs gs雖然是16位暫存器,但入棧後以32位儲存 movl $0x10,%eax !置段選擇符(使ds,es,fs指向gdt表中的資料段) mov %ax,%ds ! mov %ax,%es ! mov %ax,%fs ! pushl $int_msg !把呼叫printk函式的引數指標入棧 call _printk ! popl %eax ! pop %fs ! pop %es ! pop %ds ! popl %edx ! popl %ecx ! popl %eax ! iret ! !這個程式通過設定控制暫存器cr0的標誌PG位31來啟動對分頁的分頁處理功能,並設定各個頁表項 !的內容,以恆等於前16M的實體記憶體,分頁器假定不會產生非法的地址對映.注意,儘管所有的實體地址 !都應該由這個子程式進行恆等對映,但只有核心頁面管理函式能直接使用>1M的地址,所有""一般" !函式僅使用低於1M的地址空間,或者是使用區域性資料空間,地址空間將被對映到其他一些地方去, !mm(記憶體管理程式)會管理這些事. !在記憶體實體地址0x0處開始存放1頁頁目錄表和4頁頁表.頁目錄表是系統所有程序公用,而這裡的4頁 !表示核心專用的.對於新程序,系統會再主記憶體區為其申請頁面存放頁表. ! .align 2 ! setup_paging: ! movl $1024*5,%ecx !首先對5頁記憶體清零 xorl %eax,%eax ! xorl %edi,%edi ! cld ! rep ! stosl ! !下面4句是設定目錄表中的項,因為核心共有4個頁表所以只需要設定4項,頁目錄項的結構與頁表中 !項的結構一樣,4位元組為1項,$pg0+7表示:0x0000 1007 是頁目錄中的第一項 !則第一個頁表所在的地址0x0000 1007 & 0xffff f000=0x1000 !第一個頁表屬性標誌 0x0000 1007 & 0x0000 0fff=0x07 表示該頁存在,使用者可讀寫 movl $pg0+7,_pg_dir ! movl $pg1+7,_pg_dir ! movl $pg2+7,_pg_dir ! movl $pg3+7,_pg_dir ! !下面6行填寫4個頁表中所有項的內容,共有4(頁表)*1024(項/頁表)=4096項(0-0xfff) !也即能對映實體記憶體4096*4kb=16M !每項的內容是:當前項所對映的實體記憶體地址+該頁的標誌(這裡都是7) !使用的方法是從最後一個頁表的最後一項開始按倒退順序填寫,一個頁表的最後一項在頁表中的位置 !是1023*4=4092.因此最後一頁的最後一項位置是$pg3+4092 ! movl $pg3+4092,%edi !edi->指向最後一頁最後一項 movl $0xfff007,%eax ! std !方向位,edi遞減 1: stosl ! subl $0x1000,%eax !每填好一項,實體地址值減0x1000 jge 1b !如果小於0則說明全添寫好了 !設定頁目錄基地址暫存器cr3的值,指向頁目錄表 xorl %eax,%eax !頁目錄表在0x0000 movl %eax,%cr3 !cr3=0 movl %cr0,%eax !啟動分頁標誌 orl $0x80000000,%eax! movl %eax,%cr0 ! ret !去執行main .align 2 ! .word 0 ! idt_descr: !下面是lidt指令6位元組運算元: .word 256*8-1 !長度 .long _idt !基址 .align 2 ! .word 0 ! gdt_descr: !下面是lgdt指令的6位元組運算元 .word 256*8-1 !長度 .long _gdt !基址 .align 3 ! _idt: .fill 256,8,0 !256項,每項8位元組,填0 !全域性表.前4項是空項(不用),程式碼段描述符,資料段描述符,系統段描述符.其中系統段描述符linux沒 !有用上.後面預留了252項空間,使用者放置所建立人物的區域性描述符LDT和對應的任務狀態段TSS描述符 !0-null 1-cs 2-ds 3-sys 4-TSS0 5-LDT0 6-TSS1 7-LDT1 8-TSS2 etc... _gdt: .quad 0x0000 0000 0000 0000 ! .quad 0x00c0 9a00 0000 0fff ! .quad 0x00c0 9200 0000 0fff ! .quad 0x0000 0000 0000 0000 ! .fill 252,8,0 !
head.s執行完成後,記憶體的分配示意圖如下:
相關推薦
linux0.11中head.s分析
/* *注意!32位啟動程式碼是從絕對地址0x0000 0000開始的,這裡同樣也是頁目錄 *將要存在的地方,因此啟動程式碼會被頁目錄覆蓋掉 */ .text .globl _idt,_gdt,_pg_dir,_tmp_floppy_area _pg_dir: !頁
Linux0.11---head.s分析
head.s位於system模組的頭部,故其命名為head。 system模組位於磁碟上setup模組之後(4個扇區),即從磁碟上第6個扇區開始的位置 從此核心完全開始執行在保護模式下運行了。 head.s的彙編程式與前面的語法格式不同,它採用的是AT&T的組合語言格
嵌入式 arm平臺kernel啟動第一階段彙編head.s分析
arm_linux核心生成過程: 1. 依據arch/arm/kernel/vmlinux.lds 生成linux核心原始碼根目錄下的vmlinux,這個vmlinux屬於未壓縮,帶除錯資訊、符號表的最初的核心,大小約23MB; 命令:arm-linux-gnu-ld
linux0.11 execve系統呼叫分析
在Linux平臺下,我們一般都是在命令列下鍵入"./hello"來執行一個當前目錄下的hello應用程式("./"指定當前目錄)。雖然看似很簡單,但這麼小小的一個操作其實涉及到了很多的知識。比如:shell是如何將hello調入記憶體的?hello在執行前shell
linux2.4 啟動程式碼head.S分析
32位啟動程式碼,暫時不考慮SMP的情況。關鍵程式碼分析 頁目錄表的起始地址在0x101000,由於目前仍然處於真實模式,地址都是 實體地址 開始啟動核心 startup_32: 清方向標誌位 cld 用核心資料段的地址來初始化ds,es,fs,gs暫存器 巨
閱讀Linux0.11——head.s
最近經過反饋,博主得知了自己在輸出自己的知識方面做的非常不好。經過認真的反思,好吧主要是思考別人是怎麼做的,以及結合我的特點,得出如下結論: 1、認為每一個聽眾或者讀者都是零基礎,要直白的講出想要講給別人的知識。雖然這句話聽起來像是廢話,但是挺不容易的。舉個栗
第一次作業:基於Linux0.11操作系統的進程模型分析
機制 中斷處理程序 soft page tab nr_open sched move 關閉 1.前言 本文基於Linux0.11操作系統的源代碼,分析其進程模型。 Linux0.11下載地址:https://zhidao.baidu.com/share/20396e1704
LoadRunner 11 中Analysis分析
其實在 ane 可能 大於 操作系統 摘要 類型 觀察 9.png 原文:http://www.cnblogs.com/Chilam007/p/6445165.html analysis簡介 分析器就是對測試結果數據進行分析的組件,它
linux-2.6.22.6內核啟動分析之head.S引導段代碼
common begin ber 字節數 static smd 我們 ovs pro 學習目標: 了解arch/arm/kernel/head.S作為內核啟動的第一個文件所實現的功能! 前面通過對內核Makefile的分析,可以知道arch/arm/kernel/head
C++ 11中的一些典型概念與分析
相比傳統的C++98與C++03, C++11中新提出了很多新的概念,本文根據C++佈道師Scott Meyers 在Youtube上的培訓視訊展開,介紹C++11中的一些典型概念,並進行分析。 1. 左值(lvalue)與右值(rvalue) C++11之前已
除錯大叔V2.0.2(2018.11.18)|http/s介面除錯、資料分析程式設計師輔助開發神器
2018.11.18 - 除錯大叔 V2.0.2*支援http/https協議的get/post除錯與反饋;*可儲存請求協議的記錄;*內建一批動態引數,可應用於Referer、URL、引數;支援自定義常量引數;支援自定義UA資訊(解決模擬不同網站或手機請求); *可自由管理cookie;*支援請求的代理與模擬
C++11中once_flag,call_once實現分析
本文的分析基於llvm的libc++,而不是gun的libstdc++,因為libstdc++的程式碼裡太多巨集了,看起來蛋疼。 在多執行緒程式設計中,有一個常見的情景是某個任務只需要執行一次。在C++11中提供了很方便的輔助類once_flag,call_once。 宣告 首先來看一下
linux0.11程序睡眠sleep_on函式和喚醒wake_up函式分析
核心中的這兩個函式主要用於訪問資源時的同步操作。高速緩衝區的訪問就是其中的一個例子:如果兩個程序都要訪問同一個緩衝塊,那麼其中的一個程序就必然睡眠等待,直到該緩衝塊被釋放才可訪問。趙炯博士所著的linux0.11核心完全註釋一書中也是對該問題進行詳細的討論,但是我在閱讀這部
總結linux0.11核心中的主,次裝置號
老會忘,記下來方便後面查閱 主裝置 型別 說明 請求操作函式 0 無 無 NULL 1 塊/字元 ram,記憶體裝置(虛擬盤等) do_rd_request 2 塊 fd,軟碟機裝置 do_fd_request 3 塊 hd,硬碟裝置 do_hd_request 4 字元
linux0.11字元裝置的讀寫過程分析
首先要知道linux系統/dev目錄下的各種裝置檔案(檔案屬性c打頭)並不佔用空間,你可以發現他們的大小為0位元組,他們的區別在於檔案的i節點的成員i_zone[0]的值不同,該值標識不同的裝置號。比如tty0檔案的裝置號為0x0400,tty1裝置號為0x0401,hd0
linux0.00 "head.s"程式詳解
// head.s包含32位保護模式初始化設定程式碼、時鐘中斷程式碼、系統呼叫中斷程式碼和兩個任務的程式碼。 // 在初始化完成之後程式移動到任務0開始執行,並在時鐘中斷控制下進行任務0和1之間的切換操作。 LATCH = 119
linux 0.11 核心學習 -- head.s
# # 這段程式碼被連線到system模組的最前面,這也是它為什麼稱之為head.s的原因。 # 從這裡開始核心完全執行在保護模式下。head.s採用的是at&t格式的 # 彙編。注意的是程式碼中的賦值方向是從左到右。 # # 這段程式實際上是出於記憶體的絕對地址0開始處。首先是載入各個
Linux0.11核心讀書筆記/boot/bootsect.s
果凍QQ:457283! 本程式完成的主要功能! 1.bootsect.s從0x7c00處開始執行! 2.將自己複製到0x90000處! 3.將setup.s程式從磁碟第2扇區讀取到0x90200處! 4.將system讀取到0x10000處! 5.獲取根檔案系統裝置號! 6
nasm重寫linux-0.11 head.s (博古以通今)
;檔名:followking/boot/head.s;本檔案改寫linux-0.11/boot/head.s,目的是為了體驗整個系統構建的過程。;我是看著趙炯《Linux核心0.11完全註釋》編寫的。不過,我是編寫程式碼,有疑問再看。;我用的nasm的語法格式。我想寫一個作業
C++11中智能指針的原理、使用、實現
his animal something include expire another .cn 表現 oid 目錄 理解智能指針的原理 智能指針的使用 智能指針的設計和實現 1.智能指針的作用 C++程序設計中使用堆內存是非