久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Linux Kernel 0.12 啟動(dòng)簡(jiǎn)介,調(diào)試記錄(Ubuntu1804, Bochs, gdb)

 頭號(hào)碼甲 2022-05-09 發(fā)布于北京

PS:要轉(zhuǎn)載請(qǐng)注明出處,,本人版權(quán)所有,。

PS: 這個(gè)只是基于《我自己》的理解,

如果和你的原則及想法相沖突,,請(qǐng)諒解,,勿噴。

前置說明

??本文作為本人csdn blog的主站的備份,。(BlogID=102)

環(huán)境說明
  • Ubuntu 18.04
  • gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
  • Bochs 2.6
  • As86 version: 0.16.17

前言


??自從我近段時(shí)間開始溫習(xí)一些基礎(chǔ)知識(shí)以來,,其中覺得以前學(xué)的很淺的就是OS原理。為啥這樣說呢,?因?yàn)榫褪菧\,,知道一些瑣碎的知識(shí)。以前我自負(fù)的認(rèn)為OS就是硬件的抽象,,然后把這些硬件資源合理的分配給用戶使用就完了,,因?yàn)槲矣X得合理的整合這些硬件資源是非常'簡(jiǎn)單’的。

??由于我本身對(duì)底層是非常著迷的,。帶著覺得OS很簡(jiǎn)單的想法,,想著去看看LinuxKernel的源碼。在以前,,我對(duì)LinuxKernel的認(rèn)知很膚淺,,就知道一些驅(qū)動(dòng)移植的事情。如果硬要說一件我在LinuxKernel中玩的很深的事情,,那就是自己理解并實(shí)現(xiàn)了一個(gè)類似Anonymous Shared Memory的Linux驅(qū)動(dòng),詳見以下兩篇文章,。

??帶著這樣的想法其實(shí)已經(jīng)很久了,,由于現(xiàn)在的LinuxKernel太大了,對(duì)新手不友好,。我就想著去找一個(gè)老一點(diǎn)的版本內(nèi)核看看,。結(jié)果去網(wǎng)上一找,,就發(fā)現(xiàn)了前人已經(jīng)做了許多許多了,比如這個(gè)之前就有了解的《linux 0.11內(nèi)核完全注釋》,,還比如其他許許多多前人種的'樹’,,看到了許多,最終我決定跟著國(guó)內(nèi)現(xiàn)在比較好和新的資料從'遠(yuǎn)古’開始學(xué)習(xí)它,。它就是《Linux內(nèi)核完全注釋(PDF) v5.0 by 趙炯.pdf》,。它是基于LinuxKernel0.12 講述的,它是我在ubuntu1804上編譯通過LinuxKernel0.12的主要參考和學(xué)習(xí)資料,,同時(shí)也是我在Bochs上運(yùn)行成功的主要參考和學(xué)習(xí)資料,。

??好的多說無益,直接看運(yùn)行效果,。

??說來也慚愧,,利用斷斷續(xù)續(xù)的時(shí)間,我花了約2月,,把LinuxKernel0.12在Ubuntu1804上編譯通過,,并在1804上通過Bochs運(yùn)行成功。而且要命的事情是我其實(shí)只加了一些打印調(diào)試函數(shù),,和根據(jù)實(shí)際的調(diào)試情況修改了一些代碼,,卻花了那么久的時(shí)間,搞得我很不自信了QAQ,。

??我修改好的源碼已經(jīng)開源,,立即想要源碼的請(qǐng)直接去文末兩個(gè)rep clone即可。

??本文主要還是簡(jiǎn)單介紹LinuxKernel從上電到進(jìn)入sh的中間的簡(jiǎn)要流程,。這些流程網(wǎng)上已經(jīng)有很多了,,可能我會(huì)挑選一些我覺得比較重要的來說。

??本文適用于:

  • 會(huì)編譯和使用bochs的人,。不會(huì)可以去網(wǎng)上找找,,很多這方面的資料。
  • 對(duì)Intel AT&T 匯編有點(diǎn)了解的人,。
  • 會(huì)GDB調(diào)試的人,。
  • 知道C語言常識(shí)的人。
  • 對(duì)LinuxKernel感興趣的人,。




搭環(huán)境


??工欲善其事必先利其器,。本文主要是在Ubuntu1804上編譯生成LinuxKernel,然后用Bochs運(yùn)行我們的內(nèi)核,。



Ubuntu18.04環(huán)境安裝

我們應(yīng)該首先安裝make,gcc,gcc-multilib,bin86,。

  • sudo apt install build-essential cmake make gcc-multilib g++-multilib module-assistant bin86

然后進(jìn)入源碼目錄。

  • cd my_src
  • make disk

更多的詳情信息查看開源的rep,。



編譯兩個(gè)bochs版本備用

??我們首先就得把Linux0.12的運(yùn)行環(huán)境搭建起來,,方便我們調(diào)試,。我們使用的是Bochs2.6 和 GDB遠(yuǎn)程調(diào)試。并編譯出兩個(gè)bochs版本,,一個(gè)是帶本身調(diào)試功能(命名為:bochs),,一個(gè)是和gdb聯(lián)調(diào)(命名為:bochsdbg)。bochs 主要是調(diào)試在init/main()函數(shù)之前的內(nèi)容以及查看更多的x86寄存器,。 bochsdbg主要是調(diào)試進(jìn)入init/main()函數(shù)之后到sh成功執(zhí)行的事情,。

  • 通過 ./configure --enable-debugger 生成bochs。
  • 通過 ./configure --enable-gdb-stub 生成bochsdbg,。


運(yùn)行我們編譯的內(nèi)核

??通過本文介紹生成的文件是Linux內(nèi)核鏡像,,稍微懂點(diǎn)行的人都知道還差一個(gè)RootFS。這個(gè)文件系統(tǒng)我們?cè)诰W(wǎng)上下載的例如: http:///Linux.old/bochs/linux-0.12-080324.zip ,。本文生成的Linux內(nèi)核鏡像使用的是rootimage-0.12-hd這個(gè)文件系統(tǒng),。

??我建議這里自己配置兩個(gè).bxrc文件,一個(gè)對(duì)應(yīng)bochs,,一個(gè)對(duì)應(yīng)bochsdbg遠(yuǎn)程調(diào)試,。這樣在遇到問題的時(shí)候我們可以很方便的調(diào)試。





LinuxKernel啟動(dòng)簡(jiǎn)介


??本節(jié)簡(jiǎn)述LinuxKernel的啟動(dòng)流程,。根據(jù)我近段時(shí)間的學(xué)習(xí)來看,,這里包含了許多的歷史性的東西,大家不要去細(xì)究為啥是這樣,,很多都是為了兼容,。

??此外在整個(gè)學(xué)習(xí)期間,由于涉及到許多的x86 硬件體系知識(shí),,除了參考上文我說的文檔以外,,還必須參考以下Intel官方文檔:

  • Intel? 64 and IA-32 architectures software developer's manual combined volumes 2A, 2B, 2C, and 2D:Instruction set reference, A-Z
  • Intel? 64 and IA-32 architectures software developer's manual combined volumes 3A,
    3B, 3C, and 3D: System programming guide
  • 《Linux內(nèi)核完全注釋(PDF) v5.0 by 趙炯.pdf》 第4章,全篇精華,。


boot/bootsect.S 階段

??當(dāng)我們的計(jì)算機(jī)上電以后,,IntelCPU進(jìn)入實(shí)模式,并且PC指向了0xfff0整個(gè)地址,,如下圖,。什么意思呢?就是開機(jī)的時(shí)候執(zhí)行的第一句指令放在0xffff0這個(gè)地方,,通常這里有一個(gè)很重要的東西叫做BIOS,。我們可以看到下圖,cs=0xf000,base=0xffff0000,在實(shí)模式下面,,cs:pc 就是真實(shí)的指向地址0xffff0,。到了這里不知道大家發(fā)現(xiàn)沒有,這里還差一個(gè)東西,那就是bios本來是放在rom里面的,,怎么被指向了內(nèi)存地址0xffff0的地方呢?是誰在之前自動(dòng)搬運(yùn)的嗎,?經(jīng)過查詢后發(fā)現(xiàn),,大部分人說開機(jī)的時(shí)候,對(duì)特殊地址的訪問會(huì)被仲裁器件指向BIOS-ROM器件,。仲裁器還可以把地址翻譯并指向我們熟悉的MEM和IO,。所以這里我理解對(duì)0xffff0的訪問就是對(duì)BIOS-ROM器件的直接訪問和執(zhí)行。

??BIOS主要是做自檢,,并且在物理地址0x0開始初始化BIOS的中斷向量,,同時(shí)通過BIOS訪問存儲(chǔ)設(shè)備的中斷,將可啟動(dòng)設(shè)備的第一個(gè)扇區(qū)512字節(jié)給搬運(yùn)到絕對(duì)地址0x7c00(31k)處,。然后跳轉(zhuǎn)到0x7c00繼續(xù)執(zhí)行,,這里被搬運(yùn)的512字節(jié)就是bootsect.S生成的指令。這一段沒啥營(yíng)養(yǎng),,都是一些約定好的,,到了CPU執(zhí)行到絕對(duì)地址0x7c00的時(shí)候,才是真正的我們能控制的地方,。其實(shí)這里也能夠看到,,我們的bootsect.S生成的指令最大只能夠512字節(jié),超過了就會(huì)出問題,。下圖為我們的0x7c00處的開始幾句指令和bootsect.S的幾句指令,同時(shí)也能夠看到BIOS初始化和自檢打印的一些內(nèi)容:

??在上圖的圖中,,我打印了0x7c00開始的一部分反匯編代碼??梢钥吹胶拖旅娴腷ootsect.S的代碼是一致的,。
entry start
start:
! start at 0x07c0:0
! add by sky
	mov ax,#BOOTSEG
	mov es,ax
	mov	bp,#msg2	  ! sky-notes: src-str is es:bp
	mov	si,#15        ! sky-notes: src-str-len is cx
	call pirnt_str
! add by sky

	mov	ax,#BOOTSEG
	mov	ds,ax
	mov	ax,#INITSEG
	mov	es,ax
	mov	cx,#256
	sub	si,si
	sub	di,di
	rep
	movw
	jmpi	go,INITSEG

??從0x7c00開始,就是我們自己的可以編程的領(lǐng)域了,,也開始有了一些我自己特有的內(nèi)容,。主要是各種方法實(shí)現(xiàn)的print語句。這種調(diào)試方法簡(jiǎn)直不要太好,。

??下面簡(jiǎn)要說明一下bootsect.S的功能:

  • 首先用rep movw把自己從0x7c00搬運(yùn)到0x90000,,并跳轉(zhuǎn)cs=0x9000, pc=go 標(biāo)號(hào)的地址。繼續(xù)執(zhí)行剩下的內(nèi)容,。
  • 通過讀取0x1E號(hào)中斷向量位置的軟驅(qū)參數(shù)(由BIOS初始化時(shí)候通過BIOS中斷讀取的)到內(nèi)存,,然后修改其中的最大扇區(qū)數(shù),并重新寫回到0x1E中斷向量位置絕對(duì)地址0x78去,。最后重置軟驅(qū),,使其加載最新的參數(shù)。
  • 使用BIOS INT 0x13的2號(hào)功能,將第一個(gè)軟盤第2,,3,,4,5扇區(qū)讀取到0x90200開始的位置,。這里讀取的就是setup.S的指令內(nèi)容,,最大共2k(4*512)。0x90000-0x90200存放的是bootsect.S,, 0x90200-0x90A00 為setup.S,。
  • 使用BIOS INT 0x13的8號(hào)功能,讀取磁盤參數(shù):每磁道扇區(qū)數(shù),。并保存到變量sectors中,。
  • 使用BIOS INT 0x13的2號(hào)功能,使用剛剛的參數(shù),,讀取system模塊到0x10000,,我們的bootsect.S放在0x90000,所以我們system模塊最大只能夠占用0x10000~0x8ffff。這里的system模塊就是除了bootsect和setup模塊之外的所有內(nèi)核代碼,。
  • 判斷bootsect模塊第508,,509字節(jié)是否為0,來判斷我們是否指定根文件系統(tǒng)的設(shè)備號(hào),。我們的內(nèi)核定義為0x0301,,代表第一個(gè)磁盤第一個(gè)分區(qū)為我們的根文件系統(tǒng)。
  • 然后通過jmpi 0:9020跳轉(zhuǎn)到cs=0x9020,,pc=0的地方去執(zhí)行setup.S的代碼,。

??在我的bootsect模塊,我定義了一個(gè)打印字符串的函數(shù),,主要是通過使用BIOS INT 0x10的0x13號(hào)功能實(shí)現(xiàn),。主要還是為了調(diào)試,注意,,這里不能夠隨意添加代碼,,因?yàn)樯傻拇a超過512byte后,鏈接器會(huì)報(bào)錯(cuò),。只能夠少量的添加我們的調(diào)試代碼,。

??至此,我們就執(zhí)行完了bootsect模塊,。本模塊的主要內(nèi)容還是加載setup和system到指定位置,。bootsect執(zhí)行的一些調(diào)試日志如下圖(在0x90200下斷點(diǎn)):

注意:圖中話框的部分就是我們上文貼出的call pirnt_str打印的。

boot/setup.S 階段

??首先我們還是來看一下0x90200的位置是否是setup.S,,換句話來說是否加載好了setup模塊,。

??這里和bootsect一樣,,我也弄了一個(gè)prtstr函數(shù),這個(gè)prtstr和bootsect里面的是一樣的,,原理也是一致的,。

??剛剛我們提到,setup是從0x90200開始存放的,。那么0x90000~0x901ff中的bootsect已經(jīng)無用了,,于是我們setup中,用這里的內(nèi)存存放一些參數(shù),。下面簡(jiǎn)要說明一下setup.S的功能:

  • 用BIOS INT 0x15功能號(hào)0x88取系統(tǒng)所含擴(kuò)展內(nèi)存大小并保存在內(nèi)存0x90002~0x90003處。共兩個(gè)字節(jié),。
  • 用BIOS INT 0x10功能號(hào)0x12讀取顯卡參數(shù),,0x9000A 顯存大小,0x9000B 顯卡類型(單色/彩色),0x9000C顯卡特性參數(shù),。
  • 用BIOS中斷讀取屏幕的行列存放到0x9000E 0x9000F
  • 用BIOS INT 0x10功能號(hào)0x03讀取當(dāng)前光標(biāo)位置存放到0x90000 0x90001
  • 用BIOS INT 0x10功能號(hào)0x0f讀取當(dāng)前顯示頁(yè),,顯示模式,字符列數(shù),。 0x90004~0x90005 存放當(dāng)前顯示頁(yè),。 0x90006 顯示模式, 0x90007 字符列數(shù),。
  • 讀取第一個(gè)硬盤參數(shù)表和第二個(gè)硬盤參數(shù)表,,并放到0x90080 0x90090。每個(gè)表共16byte,。注意,,這里和之前的軟盤參數(shù)一樣,在BIOS自檢過程中,,就被放到了中斷向量0x41 0x46 的位置,。
  • 用BIOS INT 0x13功能號(hào)0x15讀取當(dāng)前硬盤設(shè)備情況,如果硬盤2不存在,,則把0x90090之后的16byte清零,。

??下面我們將使CPU從實(shí)模式變更為保護(hù)模式,下面繼續(xù)說明一下setup.S的功能:

  • 禁用中斷,。
  • 然后我們把system模塊0x10000~0x8ffff整體下移到0x0開始的位置,。就是把最大0x80000(512k)的system模塊向下移動(dòng)0x10000(64k)。
  • 首先加載LDT和GDT,。
  • 開啟A20地址線,,支持1M以上的內(nèi)存。
  • 初始化兩個(gè)8259A中斷控制器,。
  • 通過lmsw 設(shè)置cr0最低位位1,,進(jìn)入保護(hù)模式。
  • 通過jmpi 0:0x8跳轉(zhuǎn)到絕對(duì)地址0x0開始執(zhí)行system的代碼。system是從boot/head.s開始的,。

??這里需要說明幾個(gè)事情:

  • 我們?cè)谙乱苨ystem模塊的時(shí)候,,覆蓋了BIOS中斷向量表。所以通過BIOS中斷打印字符串是行不通的,。
  • 在實(shí)模式中,,cs:pc就是真實(shí)執(zhí)行的地址。但是在保護(hù)模式中,,cs是一個(gè)選擇符號(hào),,根據(jù)選擇符號(hào)值不同,分表在GDT或者LDT中查找對(duì)應(yīng)的CS段描述符,,其中最重要的就是base地址,,當(dāng)未開啟分頁(yè)的時(shí)候,這里的base+pc就是我們真實(shí)的執(zhí)行地址,。上面我們加載了LDT和GDT,。這里的LDT是空,GDT有3項(xiàng),,第零項(xiàng)是空,,第一項(xiàng)是代碼段描述符,第二項(xiàng)是數(shù)據(jù)段描述符,,他們的基地址都是0x0,。當(dāng)cs=0x08,ds=0x10時(shí),分別指向這里的第一項(xiàng)和第二項(xiàng),。

??剛剛說了,,system下移導(dǎo)致BIOS中斷向量表被沖掉了,于是我們不能夠通過BIOS打印字符串,,于是這里我們使用的是直接操作顯存內(nèi)存地址顯示字符,,這個(gè)原理和LinuxKernel tty顯示原理差別不是很大。

??這里我們?cè)O(shè)計(jì)了print_str函數(shù),,通過直接操控顯存然后寫入字符進(jìn)行顯示,,這里還使用到了剛剛我們保存的當(dāng)前光標(biāo)位置(0x90000 0x90001)。寫這個(gè)主要還是為了調(diào)試,。

??到此,,我們已經(jīng)開始去執(zhí)行system的內(nèi)容,其中head.s是入口,。下圖是在0x0下斷點(diǎn)得到的setup模塊的一些打印日志,。

??這里我們可以看到,紅框還是BIOS中斷打印的,,黃框是通過直接操縱顯存顯示的,。注意,,我這里設(shè)計(jì)的直接操作顯存的函數(shù),是通過循環(huán)在當(dāng)前顯存頁(yè)顯示的,,并不是我們常見的整頁(yè)上移的方式,。

boot/head.s 階段

??首先我們還是來看一下0x0的位置是否是head.s,換句話來說是否加載好了system模塊,。并且,,從這里開始,我們就是進(jìn)入了真正的LinuxKernel的世界,,前面都是做一些環(huán)境初始化,,都是一些固定的內(nèi)容。

head_start_img

??這里我們需要說明的是,,bootsect.S和setup.S用的是intel匯編,,而從head.s開始,我們用的都是AT&T匯編,。同理,這里我也弄了一個(gè)safe_mode_print_str_no_page,,打印字符串,,為了調(diào)試,還是用的直接操作顯存的方式,。

??從這里開始,,CPU開始工作于保護(hù)模式,下面簡(jiǎn)要介紹一下工作流程:

  • 剛剛我們通過jmpi切換到0x0開始執(zhí)行,,這時(shí)cs=0x8,根據(jù)setup設(shè)置好的GDT,,base為0x0,同理我們?cè)O(shè)置其他段寄存器,。
  • 設(shè)置堆棧為stack_start,,這個(gè)就是內(nèi)核堆棧。此符號(hào)定義于kernel/sched.c中,,如下文,。
long user_stack [ PAGE_SIZE>>2 ] ;

struct {
	long * a;
	short b;
	// } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
	} stack_start = { & user_stack , 0x10 };
  • 設(shè)置IDT,所有的中斷向量指向ignore_int,,一個(gè)預(yù)定義的中斷服務(wù)程序,。共256項(xiàng),每項(xiàng)8byte,。
  • 重新設(shè)置GDT,。共256項(xiàng),每項(xiàng)8byte,。重新設(shè)置GDT的原因是setup的GDT可能會(huì)被沖掉,,于是把GDT設(shè)置到合理的內(nèi)存位置,。這里設(shè)置好的GDT有4個(gè)。和setup中類似,。第0,,3個(gè)為0.第1,2項(xiàng)為cs和ds的段描述符,。
  • 檢查A20是否開通,,主要是通過判斷0x100000 和 0x0值是否相等。
  • 檢查數(shù)學(xué)協(xié)處理器是否存在,。

??到這里,,我們就開始準(zhǔn)備正式進(jìn)入到init/main.c中的main函數(shù)了,但是還差最后一個(gè)重要的事情,,那就是啟用分頁(yè)機(jī)制,,下面繼續(xù)介紹其工作流程:

after_page_tables:
	# sky print
	push %ebp
	lea msg5, %ebp
	call safe_mode_print_str_no_page
	pop %ebp	
	#
	pushl $0		# These are the parameters to main :-)
	pushl $0
	pushl $0
	pushl $L6		# return address for main, if it decides to.
	pushl $main
	jmp setup_paging
  • 從上面的代碼我們可只,我們?cè)趩⒂梅猪?yè)前,,把init/main.c中的main函數(shù)地址設(shè)置到了堆棧中,。
  • 首先我們把從0x0開始的5頁(yè)內(nèi)存清零。每頁(yè)4096字節(jié),。其中第一頁(yè)為頁(yè)表目錄,,第2-5頁(yè)為頁(yè)表。
  • 設(shè)置頁(yè)表目錄的前4項(xiàng)為第2-5頁(yè)頁(yè)表地址,。注意頁(yè)表目錄為1024項(xiàng),,每項(xiàng)4字節(jié)。
  • 倒序設(shè)置每一個(gè)頁(yè)表的每一項(xiàng)內(nèi)容,,第5頁(yè)最后一項(xiàng)為0xfff000,。映射之后,2-5頁(yè)分別映射好了16MB內(nèi)存的空間,。
  • 操作cr0,,開啟分頁(yè)
  • 通過ret指令,從堆棧中把main地址彈出去執(zhí)行,。

??到這里,,我們正式進(jìn)入到init/main.c中的main函數(shù)中,進(jìn)入c語言相關(guān)代碼的地界,。下面是進(jìn)入main之前的一些日志輸出,。



init/main.c 到進(jìn)入shell

??這里我們進(jìn)入了init/main.c中的main函數(shù),可從下圖看到,。從這里開始,,也是我們大家都熟知的Linux內(nèi)核部分。

void main(void)		/* This really IS void, no error here. */
{			/* The startup routine assumes (well, ...) this */
/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
	char _my_msg_buf[100];
	sprintf(_my_msg_buf, "kernel main() start, root_dev=%x, swap_dev=%x ... ...\0", ORIG_ROOT_DEV, ORIG_SWAP_DEV);
	__asm__ (
	"push %%ebp\n\t"
	"mov %0, %%ebp\n\t"
	"call safe_mode_print_str_after_page\n\t" 
	"pop %%ebp\n\t"
	:
	:"p"((char *)&_my_msg_buf)
	:);



 	ROOT_DEV = ORIG_ROOT_DEV;
 	SWAP_DEV = ORIG_SWAP_DEV;

	sprintf(term, "TERM=con%dx%d", CON_COLS, CON_ROWS);

	envp[1] = term;	
	envp_rc[1] = term;
 	drive_info = DRIVE_INFO;

	memory_end = (1<<20) + (EXT_MEM_K<<10);
	memory_end &= 0xfffff000;//align 4k



	if (memory_end > 16*1024*1024)//if memory_end > 16MB, set it to be 16 MB
		memory_end = 16*1024*1024;

	if (memory_end > 12*1024*1024) 
		buffer_memory_end = 4*1024*1024;
	else if (memory_end > 6*1024*1024)
		buffer_memory_end = 2*1024*1024;
	else
		buffer_memory_end = 1*1024*1024;

	main_memory_start = buffer_memory_end;

	sprintf(_my_msg_buf, "Mem size is %x, buf-mem size is %x, main-mem start %x ... ...\0", memory_end, main_memory_start, buffer_memory_end);
	__asm__ (
	"push %%ebp\n\t"
	"mov %0, %%ebp\n\t"
	"call safe_mode_print_str_after_page\n\t" 
	"pop %%ebp\n\t"
	:
	:"p"((char *)&_my_msg_buf)
	:);


#ifdef RAMDISK
	sprintf(_my_msg_buf, "ramdisk init ... ...\0");
	__asm__ (
	"push %%ebp\n\t"
	"mov %0, %%ebp\n\t"
	"call safe_mode_print_str_after_page\n\t" 
	"pop %%ebp\n\t"
	:
	:"p"((char *)&_my_msg_buf)
	:);
	main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endif

	sprintf(_my_msg_buf, "memory init ... ...\0");
	__asm__ (
	"push %%ebp\n\t"
	"mov %0, %%ebp\n\t"
	"call safe_mode_print_str_after_page\n\t" 
	"pop %%ebp\n\t"
	:
	:"p"((char *)&_my_msg_buf)
	:);
	mem_init(main_memory_start,memory_end);

	sprintf(_my_msg_buf, "trap init ... ...\0");
	__asm__ (
	"push %%ebp\n\t"
	"mov %0, %%ebp\n\t"
	"call safe_mode_print_str_after_page\n\t" 
	"pop %%ebp\n\t"
	:
	:"p"((char *)&_my_msg_buf)
	:);
	trap_init();

	sprintf(_my_msg_buf, "blk init ... ...\0");
	__asm__ (
	"push %%ebp\n\t"
	"mov %0, %%ebp\n\t"
	"call safe_mode_print_str_after_page\n\t" 
	"pop %%ebp\n\t"
	:
	:"p"((char *)&_my_msg_buf)
	:);
	blk_dev_init();

	sprintf(_my_msg_buf, "chr init ... ...\0");
	__asm__ (
	"push %%ebp\n\t"
	"mov %0, %%ebp\n\t"
	"call safe_mode_print_str_after_page\n\t" 
	"pop %%ebp\n\t"
	:
	:"p"((char *)&_my_msg_buf)
	:);
	chr_dev_init();

	sprintf(_my_msg_buf, "tty init ... ...\0");
	__asm__ (
	"push %%ebp\n\t"
	"mov %0, %%ebp\n\t"
	"call safe_mode_print_str_after_page\n\t" 
	"pop %%ebp\n\t"
	:
	:"p"((char *)&_my_msg_buf)
	:);
	tty_init();

	printk("time init ... ...\n\r");
	time_init();

	printk("sched init ... ...\n\r");
	sched_init();
	/*
	After sched_init()

	gdt[0] = NULL
	gdt[1] = kernel cs
	gdt[2] = kernel ds
	gdt[3] = NULL
	
	gdt[4] = task0.tss
	gdt[5] = task0.ldt

	tr=task0.tss
	ldtr=task0.ldt
	*/

	printk("buffer init ... ...\n\r");
	buffer_init(buffer_memory_end);

	printk("hd init ... ...\n\r");
	hd_init();

	printk("floppy init ... ...\n\r");
	floppy_init();

	printk("enable interrupts ... ...\n\r");
	sti();

	printk("go to user mode ... ...\n\r");
	/*
	movl %%esp,%%eax
	pushl $0x17
	pushl %%eax
	pushfl
	pushl $0x0f
	pushl $1f
	iret
	1:
	movl $0x17,%%eax
	mov %%ax,%%ds
	mov %%ax,%%es
	mov %%ax,%%fs
	mov %%ax,%%gs

	iret instruction will do follow op:
	popl eip
	popl cs
	popl eflag
	popl esp
	popl ss
	*/
	move_to_user_mode();

	printf("user_mode: fork() task0 ... ...");
	if (!fork()) {		/* we count on this going ok */

		printf("user_mode: task1 call init ... ...");
		init();
	}
/*
 *   NOTE!!   For any other task 'pause()' would mean we have to get a
 * signal to awaken, but task0 is the sole exception (see 'schedule()')
 * as task 0 gets activated at every idle moment (when no other tasks
 * can run). For task0 'pause()' just means we go check if some other
 * task can run, and if not we return here.
 */
	printf("user_mode: task0 call sys_pause() in while ... ...");
	for(;;)
		__asm__("int $0x80"::"a" (__NR_pause):);
}

??注意,,這里我們?nèi)匀辉O(shè)計(jì)了一個(gè)函數(shù)為safe_mode_print_str_after_page,,通過直接操作顯存進(jìn)行顯示字符串,,知道tty_init之后,我們才能夠調(diào)用printk類似的函數(shù)進(jìn)行打印,。

??下面簡(jiǎn)要介紹一下main函數(shù)主要做的事情:

  • 根據(jù)我們?cè)趕etup中保存到內(nèi)存中的內(nèi)存參數(shù)初始化高速緩沖區(qū)和主存的位置,。
  • 然后就是我們常見的初始化mm模塊。
  • 初始化中斷向量,。
  • 初始化塊設(shè)備,。
  • 初始化字符串設(shè)備。
  • 初始化tty設(shè)備,。
  • 初始化時(shí)間,。
  • 初始化調(diào)度模塊。
  • 初始化緩沖區(qū),。
  • 初始化硬盤,。
  • 初始化軟盤。
  • 開啟中斷,。
  • 把當(dāng)前任務(wù)切換到用戶態(tài),。

??當(dāng)我們切換到用戶態(tài)之后,并且當(dāng)前我們的進(jìn)程是0號(hào)進(jìn)程,,我們內(nèi)核的一些重要初始化基本設(shè)置完畢,。然后就像我們常見的linux編程那樣,通過fork,,創(chuàng)建我們的1號(hào)進(jìn)程。然后我們繼續(xù)進(jìn)行下面的事情:

  • task0在fork出task1之后,,就循環(huán)調(diào)用sys_pause, 這里主要還是執(zhí)行schedule()開始執(zhí)行進(jìn)程調(diào)度,。
  • task1成功創(chuàng)建后,調(diào)用setup,,開始加載根文件系統(tǒng),。然后task1 通過fork創(chuàng)建了task2。
  • task2通過execve開始運(yùn)行/bin/sh,,進(jìn)入shell,。后續(xù)就是一些其他的事情。

??到這里,,我們已經(jīng)把kernel跑起來了,。在我調(diào)試的過程中,主要還是mm模塊和schedule模塊有些問題,,可能和編譯器版本有關(guān)系,,反正我生成的代碼,總會(huì)報(bào)錯(cuò),。哪怕到現(xiàn)在,,我開源出來的我修改的內(nèi)核,,也非常的不穩(wěn)定,經(jīng)常崩潰,。但是好在正常工作了,。

??下面給出兩種不同打印的日志:



tool/build.c

??此工具是生成LinuxKernel鏡像的手段。但是我們?cè)赨buntu上生成的內(nèi)核,,由于gcc版本變更的原因,,需要做一些變更。主要還是把生成的elf格式system模塊通過objcopy 生成二進(jìn)制內(nèi)存鏡像,。主要原因就是elf格式需要一個(gè)elf加載器進(jìn)行各個(gè)段的重定位,,但是由于我們是內(nèi)核,所以沒有,。詳情,,請(qǐng)查看tool/build.c 及 Makefile。





開源


??https://github.com/flyinskyin2013/LinuxKernel-src0.12

??https:///sky-X/LinuxKernel-src0.12 (鏡像)





后記


??為啥想要在ubuntu1804環(huán)境下弄這個(gè)東西呢,?一方面是想學(xué)習(xí)一下,,通過踩坑的方式加深自己的理解。另一方面還是太懶了,,我只想在我的ubuntu1804上編譯內(nèi)核,,不想安裝其他虛擬機(jī)了,我的電腦太卡了(畢竟8年的電腦了QAQ),。

??經(jīng)過了這一波調(diào)試,,我對(duì)LinuxKernel有了更深的認(rèn)知,我覺得很不錯(cuò),,如果以后有必要,,我還可以分別對(duì)這些模塊進(jìn)行詳細(xì)的查看,在這里,,我只是簡(jiǎn)單的說明了init/main中的內(nèi)容,,其實(shí),還有許多其他的內(nèi)容是運(yùn)行在背后的,。比如system_call,sys_table等等內(nèi)容,。還有do_fork do_execve等等內(nèi)容都是我在調(diào)試過程中踩過的坑。

??這里還是要說明,,深入調(diào)試學(xué)習(xí)這個(gè)的原因還是想看看OS是怎么運(yùn)行起來,,雖然不能說已經(jīng)100%的熟知,但是也可管中窺豹,。

??注意,,這個(gè)版本的內(nèi)核和現(xiàn)代的2.0,4.0,,5.0還缺了一些主要的知識(shí),,比如網(wǎng)絡(luò)棧,,VFS等。但是其他的一些內(nèi)容,,在現(xiàn)在的最新內(nèi)核中,,多多少少都能夠看到這個(gè)版本的一些影子。這也是學(xué)習(xí)這個(gè)內(nèi)核的原因之一,。




打賞,、訂閱、收藏,、丟香蕉,、硬幣,請(qǐng)關(guān)注公眾號(hào)(攻城獅的搬磚之路)

PS: 請(qǐng)尊重原創(chuàng),,不喜勿噴,。

PS: 要轉(zhuǎn)載請(qǐng)注明出處,本人版權(quán)所有,。

PS: 有問題請(qǐng)留言,,看到后我會(huì)第一時(shí)間回復(fù)。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多