主頁 > 作業系統 > Linux Kernel 0.12 啟動簡介,除錯記錄(Ubuntu1804, Bochs, gdb)

Linux Kernel 0.12 啟動簡介,除錯記錄(Ubuntu1804, Bochs, gdb)

2021-03-08 06:09:24 作業系統

PS:要轉載請注明出處,本人著作權所有,

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

如果和你的原則及想法相沖突,請諒解,勿噴,

前置說明

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

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

前言


??自從我近段時間開始溫習一些基礎知識以來,其中覺得以前學的很淺的就是OS原理,為啥這樣說呢?因為就是淺,知道一些瑣碎的知識,以前我自負的認為OS就是硬體的抽象,然后把這些硬體資源合理的分配給用戶使用就完了,因為我覺得合理的整合這些硬體資源是非常‘簡單’的,

??由于我本身對底層是非常著迷的,帶著覺得OS很簡單的想法,想著去看看LinuxKernel的原始碼,在以前,我對LinuxKernel的認知很膚淺,就知道一些驅動移植的事情,如果硬要說一件我在LinuxKernel中玩的很深的事情,那就是自己理解并實作了一個類似Anonymous Shared Memory的Linux驅動,詳見以下兩篇文章,

  • 《Android匿名共享記憶體(Anonymous Shared Memory) --- 瞎折騰記錄 (驅動程式篇)》 https://blog.csdn.net/u011728480/article/details/88420467
  • 《linux kernel 中行程間描述符的傳遞方法及原理》 https://blog.csdn.net/u011728480/article/details/88553602

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

??好的多說無益,直接看運行效果,

result_img

??說來也慚愧,利用斷斷續續的時間,我花了約2月,把LinuxKernel0.12在Ubuntu1804上編譯通過,并在1804上通過Bochs運行成功,而且要命的事情是我其實只加了一些列印除錯函式,和根據實際的除錯情況修改了一些代碼,卻花了那么久的時間,搞得我很不自信了QAQ,

??我修改好的原始碼已經開源,立即想要原始碼的請直接去文末兩個rep clone即可,

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

??本文適用于:

  • 會編譯和使用bochs的人,不會可以去網上找找,很多這方面的資料,
  • 對Intel AT&T 匯編有點了解的人,
  • 會GDB除錯的人,
  • 知道C語言常識的人,
  • 對LinuxKernel感興趣的人,




搭環境


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



Ubuntu18.04環境安裝

我們應該首先安裝make,gcc,gcc-multilib,bin86,

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

然后進入原始碼目錄,

  • cd my_src
  • make disk

更多的詳情資訊查看開源的rep,



編譯兩個bochs版本備用

??我們首先就得把Linux0.12的運行環境搭建起來,方便我們除錯,我們使用的是Bochs2.6 和 GDB遠程除錯,并編譯出兩個bochs版本,一個是帶本身除錯功能(命名為:bochs),一個是和gdb聯調(命名為:bochsdbg),bochs 主要是除錯在init/main()函式之前的內容以及查看更多的x86暫存器, bochsdbg主要是除錯進入init/main()函式之后到sh成功執行的事情,

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


運行我們編譯的內核

??通過本文介紹生成的檔案是Linux內核鏡像,稍微懂點行的人都知道還差一個RootFS,這個檔案系統我們在網上下載的例如: http://oldlinux.org/Linux.old/bochs/linux-0.12-080324.zip ,本文生成的Linux內核鏡像使用的是rootimage-0.12-hd這個檔案系統,

??我建議這里自己配置兩個.bxrc檔案,一個對應bochs,一個對應bochsdbg遠程除錯,這樣在遇到問題的時候我們可以很方便的除錯,





LinuxKernel啟動簡介


??本節簡述LinuxKernel的啟動流程,根據我近段時間的學習來看,這里包含了許多的歷史性的東西,大家不要去細究為啥是這樣,很多都是為了兼容,

??此外在整個學習期間,由于涉及到許多的x86 硬體體系知識,除了參考上文我說的檔案以外,還必須參考以下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內核完全注釋(PDF) v5.0 by 趙炯.pdf》 第4章,全篇精華,


boot/bootsect.S 階段

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

poweron_img

??BIOS主要是做自檢,并且在物理地址0x0開始初始化BIOS的中斷向量,同時通過BIOS訪問存盤設備的中斷,將可啟動設備的第一個扇區512位元組給搬運到絕對地址0x7c00(31k)處,然后跳轉到0x7c00繼續執行,這里被搬運的512位元組就是bootsect.S生成的指令,這一段沒啥營養,都是一些約定好的,到了CPU執行到絕對地址0x7c00的時候,才是真正的我們能控制的地方,其實這里也能夠看到,我們的bootsect.S生成的指令最大只能夠512位元組,超過了就會出問題,下圖為我們的0x7c00處的開始幾句指令和bootsect.S的幾句指令,同時也能夠看到BIOS初始化和自檢列印的一些內容:

7c00_img
??在上圖的圖中,我列印了0x7c00開始的一部分反匯編代碼,可以看到和下面的bootsect.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開始,就是我們自己的可以編程的領域了,也開始有了一些我自己特有的內容,主要是各種方法實作的print陳述句,這種除錯方法簡直不要太好,

??下面簡要說明一下bootsect.S的功能:

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

??在我的bootsect模塊,我定義了一個列印字串的函式,主要是通過使用BIOS INT 0x10的0x13號功能實作,主要還是為了除錯,注意,這里不能夠隨意添加代碼,因為生成的代碼超過512byte后,聯結器會報錯,只能夠少量的添加我們的除錯代碼,

??至此,我們就執行完了bootsect模塊,本模塊的主要內容還是加載setup和system到指定位置,bootsect執行的一些除錯日志如下圖(在0x90200下斷點):

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

boot/setup.S 階段

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

90200_img
??這里和bootsect一樣,我也弄了一個prtstr函式,這個prtstr和bootsect里面的是一樣的,原理也是一致的,

??剛剛我們提到,setup是從0x90200開始存放的,那么0x90000~0x901ff中的bootsect已經無用了,于是我們setup中,用這里的記憶體存放一些引數,下面簡要說明一下setup.S的功能:

  • 用BIOS INT 0x15功能號0x88取系統所含擴展記憶體大小并保存在記憶體0x90002~0x90003處,共兩個位元組,
  • 用BIOS INT 0x10功能號0x12讀取顯卡引數,0x9000A 顯存大小,0x9000B 顯卡型別(單色/彩色),0x9000C顯卡特性引數,
  • 用BIOS中斷讀取螢屏的行列存放到0x9000E 0x9000F
  • 用BIOS INT 0x10功能號0x03讀取當前游標位置存放到0x90000 0x90001
  • 用BIOS INT 0x10功能號0x0f讀取當前顯示頁,顯示模式,字符列數, 0x90004~0x90005 存放當前顯示頁, 0x90006 顯示模式, 0x90007 字符列數,
  • 讀取第一個硬碟引數表和第二個硬碟引數表,并放到0x90080 0x90090,每個表共16byte,注意,這里和之前的軟盤引數一樣,在BIOS自檢程序中,就被放到了中斷向量0x41 0x46 的位置,
  • 用BIOS INT 0x13功能號0x15讀取當前硬碟設備情況,如果硬碟2不存在,則把0x90090之后的16byte清零,

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

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

??這里需要說明幾個事情:

  • 我們在下移system模塊的時候,覆寫了BIOS中斷向量表,所以通過BIOS中斷列印字串是行不通的,
  • 在實模式中,cs:pc就是真實執行的地址,但是在保護模式中,cs是一個選擇符號,根據選擇符號值不同,分表在GDT或者LDT中查找對應的CS段描述符,其中最重要的就是base地址,當未開啟分頁的時候,這里的base+pc就是我們真實的執行地址,上面我們加載了LDT和GDT,這里的LDT是空,GDT有3項,第零項是空,第一項是代碼段描述符,第二項是資料段描述符,他們的基地址都是0x0,當cs=0x08,ds=0x10時,分別指向這里的第一項和第二項,

??剛剛說了,system下移導致BIOS中斷向量表被沖掉了,于是我們不能夠通過BIOS列印字串,于是這里我們使用的是直接操作顯存記憶體地址顯示字符,這個原理和LinuxKernel tty顯示原理差別不是很大,

??這里我們設計了print_str函式,通過直接操控顯存然后寫入字符進行顯示,這里還使用到了剛剛我們保存的當前游標位置(0x90000 0x90001),寫這個主要還是為了除錯,

??到此,我們已經開始去執行system的內容,其中head.s是入口,下圖是在0x0下斷點得到的setup模塊的一些列印日志,

setup_log_img
??這里我們可以看到,紅框還是BIOS中斷列印的,黃框是通過直接操縱顯存顯示的,注意,我這里設計的直接操作顯存的函式,是通過回圈在當前顯存頁顯示的,并不是我們常見的整頁上移的方式,

boot/head.s 階段

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

head_start_img

??這里我們需要說明的是,bootsect.S和setup.S用的是intel匯編,而從head.s開始,我們用的都是AT&T匯編,同理,這里我也弄了一個safe_mode_print_str_no_page,列印字串,為了除錯,還是用的直接操作顯存的方式,

??從這里開始,CPU開始作業于保護模式,下面簡要介紹一下作業流程:

  • 剛剛我們通過jmpi切換到0x0開始執行,這時cs=0x8,根據setup設定好的GDT,base為0x0,同理我們設定其他段暫存器,
  • 設定堆疊為stack_start,這個就是內核堆疊,此符號定義于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 };
  • 設定IDT,所有的中斷向量指向ignore_int,一個預定義的中斷服務程式,共256項,每項8byte,
  • 重新設定GDT,共256項,每項8byte,重新設定GDT的原因是setup的GDT可能會被沖掉,于是把GDT設定到合理的記憶體位置,這里設定好的GDT有4個,和setup中類似,第0,3個為0.第1,2項為cs和ds的段描述符,
  • 檢查A20是否開通,主要是通過判斷0x100000 和 0x0值是否相等,
  • 檢查數學協處理器是否存在,

??到這里,我們就開始準備正式進入到init/main.c中的main函式了,但是還差最后一個重要的事情,那就是啟用分頁機制,下面繼續介紹其作業流程:

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
  • 從上面的代碼我們可只,我們在啟用分頁前,把init/main.c中的main函式地址設定到了堆疊中,
  • 首先我們把從0x0開始的5頁記憶體清零,每頁4096位元組,其中第一頁為頁表目錄,第2-5頁為頁表,
  • 設定頁表目錄的前4項為第2-5頁頁表地址,注意頁表目錄為1024項,每項4位元組,
  • 倒序設定每一個頁表的每一項內容,第5頁最后一項為0xfff000,映射之后,2-5頁分別映射好了16MB記憶體的空間,
  • 操作cr0,開啟分頁
  • 通過ret指令,從堆疊中把main地址彈出去執行,

??到這里,我們正式進入到init/main.c中的main函式中,進入c語言相關代碼的地界,下面是進入main之前的一些日志輸出,

head_log_img


init/main.c 到進入shell

??這里我們進入了init/main.c中的main函式,可從下圖看到,從這里開始,也是我們大家都熟知的Linux內核部分,

init_main_img
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):);
}

??注意,這里我們仍然設計了一個函式為safe_mode_print_str_after_page,通過直接操作顯存進行顯示字串,知道tty_init之后,我們才能夠呼叫printk類似的函式進行列印,

??下面簡要介紹一下main函式主要做的事情:

  • 根據我們在setup中保存到記憶體中的記憶體引數初始化高速緩沖區和主存的位置,
  • 然后就是我們常見的初始化mm模塊,
  • 初始化中斷向量,
  • 初始化塊設備,
  • 初始化字串設備,
  • 初始化tty設備,
  • 初始化時間,
  • 初始化調度模塊,
  • 初始化緩沖區,
  • 初始化硬碟,
  • 初始化軟盤,
  • 開啟中斷,
  • 把當前任務切換到用戶態,

??當我們切換到用戶態之后,并且當前我們的行程是0號行程,我們內核的一些重要初始化基本設定完畢,然后就像我們常見的linux編程那樣,通過fork,創建我們的1號行程,然后我們繼續進行下面的事情:

  • task0在fork出task1之后,就回圈呼叫sys_pause, 這里主要還是執行schedule()開始執行行程調度,
  • task1成功創建后,呼叫setup,開始加載根檔案系統,然后task1 通過fork創建了task2,
  • task2通過execve開始運行/bin/sh,進入shell,后續就是一些其他的事情,

??到這里,我們已經把kernel跑起來了,在我除錯的程序中,主要還是mm模塊和schedule模塊有些問題,可能和編譯器版本有關系,反正我生成的代碼,總會報錯,哪怕到現在,我開源出來的我修改的內核,也非常的不穩定,經常崩潰,但是好在正常作業了,

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

main_log0_img
main_log1_img


tool/build.c

??此工具是生成LinuxKernel鏡像的手段,但是我們在Ubuntu上生成的內核,由于gcc版本變更的原因,需要做一些變更,主要還是把生成的elf格式system模塊通過objcopy 生成二進制記憶體鏡像,主要原因就是elf格式需要一個elf加載器進行各個段的重定位,但是由于我們是內核,所以沒有,詳情,請查看tool/build.c 及 Makefile,





開源


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

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





后記


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

??經過了這一波除錯,我對LinuxKernel有了更深的認知,我覺得很不錯,如果以后有必要,我還可以分別對這些模塊進行詳細的查看,在這里,我只是簡單的說明了init/main中的內容,其實,還有許多其他的內容是運行在背后的,比如system_call,sys_table等等內容,還有do_fork do_execve等等內容都是我在除錯程序中踩過的坑,

??這里還是要說明,深入除錯學習這個的原因還是想看看OS是怎么運行起來,雖然不能說已經100%的熟知,但是也可管中窺豹,

??注意,這個版本的內核和現代的2.0,4.0,5.0還缺了一些主要的知識,比如網路堆疊,VFS等,但是其他的一些內容,在現在的最新內核中,多多少少都能夠看到這個版本的一些影子,這也是學習這個內核的原因之一,




打賞、訂閱、收藏、丟香蕉、硬幣,請關注公眾號(攻城獅的搬磚之路)
qrc_img

PS: 請尊重原創,不喜勿噴,

PS: 要轉載請注明出處,本人著作權所有,

PS: 有問題請留言,看到后我會第一時間回復,

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/267269.html

標籤:Linux

上一篇:linux ubuntu下的WiFi配置器未找到問題(嘗試N多方法)

下一篇:以太網資料包架構

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • CA和證書

    1、在 CentOS7 中使用 gpg 創建 RSA 非對稱密鑰對 gpg --gen-key #Centos上生成公鑰/密鑰對(存放在家目錄.gnupg/) 2、將 CentOS7 匯出的公鑰,拷貝到 CentOS8 中,在 CentOS8 中使用 CentOS7 的公鑰加密一個檔案 gpg -a ......

    uj5u.com 2020-09-10 00:09:53 more
  • Kubernetes K8S之資源控制器Job和CronJob詳解

    Kubernetes的資源控制器Job和CronJob詳解與示例 ......

    uj5u.com 2020-09-10 00:10:45 more
  • VMware下安裝CentOS

    VMware下安裝CentOS 一、軟硬體準備 1 Centos鏡像準備 1.1 CentOS鏡像下載地址 下載地址 1.2 CentOS鏡像下載程序 點擊下載地址進入如下圖的網站,選擇需要下載的版本,這里選擇的是Centos8,點擊如圖所示。 決定選擇Centos8后,選擇想要的鏡像源進行下載,此 ......

    uj5u.com 2020-09-10 00:12:10 more
  • 如何使用Grep命令查找多個字串

    如何使用Grep 命令查找多個字串 大家好,我是良許! 今天向大家介紹一個非常有用的技巧,那就是使用 grep 命令查找多個字串。 簡單介紹一下,grep 命令可以理解為是一個功能強大的命令列工具,可以用它在一個或多個輸入檔案中搜索與正則運算式相匹配的文本,然后再將每個匹配的文本用標準輸出的格式 ......

    uj5u.com 2020-09-10 00:12:28 more
  • git配置http代理

    git配置http代理 經常遇到克隆 github 慢的問題,這里記錄一下幾種配置 git 代理的方法,解決 clone github 過慢。 目錄 git配置代理 git單獨配置github代理 git配置全域代理 配置終端環境變數 git配置代理 主要使用 git config 命令 git單獨 ......

    uj5u.com 2020-09-10 00:12:33 more
  • Linux npm install 裝包時提示Error EACCES permission denied解

    npm install 裝包時提示Error EACCES permission denied解決辦法 ......

    uj5u.com 2020-09-10 00:12:53 more
  • Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包

    Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包。 18 (flaskApi) [root@67 flaskDemo]# yum -y install nginx 19 已加載插件:fastestmirror, langpacks 20 Loading ......

    uj5u.com 2020-09-10 00:13:13 more
  • Linux查看服務器暴力破解ssh IP

    在公網的服務器上經常遇到別人爆破你服務器的22埠,用來挖礦或者干其他嘿嘿嘿的事情~ 這種情況下正確的做法是: 修改默認ssh的22埠 使用設定密鑰登錄或者白名單ip登錄 建議服務器密碼為復雜密碼 創建普通用戶登錄服務器(root權限過大) 建立堡壘機,實作統一管理服務器 統計爆破IP [root ......

    uj5u.com 2020-09-10 00:13:17 more
  • CentOS 7系統常見快捷鍵操作方式

    Linux系統中一些常見的快捷方式,可有效提高操作效率,在某些時刻也能避免操作失誤帶來的問題。 ......

    uj5u.com 2020-09-10 00:13:31 more
  • CentOS 7作業系統目錄結構介紹

    作業系統存在著大量的資料檔案資訊,相應檔案資訊會存在于系統相應目錄中,為了更好的管理資料資訊,會將系統進行一些目錄規劃,不同目錄存放不同的資源。 ......

    uj5u.com 2020-09-10 00:13:35 more
最新发布
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:43:21 more
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:42:36 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:26:53 more
  • 設定Windows主機的瀏覽器為wls2的默認瀏覽器

    這里以Chrome為例。 1. 準備作業 wsl是可以使用Windows主機上安裝的exe程式,出于安全考慮,默認情況下改功能是無法使用。要使用的話,終端需要以管理員權限啟動。 我這里以Windows Terminal為例,介紹如何默認使用管理員權限打開終端,具體操作如下圖所示: 2. 操作 wsl ......

    uj5u.com 2023-04-19 09:25:49 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:19:04 more
  • Linux學習筆記

    IP地址和主機名 IP地址 ifconfig可以用來查詢本機的IP地址,如果不能使用,可以通過install net-tools安裝。 Centos系統下ens33表示主網卡;inet后表示IP地址;lo表示本地回環網卡; 127.0.0.1表示代指本機;0.0.0.0可以用于代指本機,同時在放行設 ......

    uj5u.com 2023-04-18 06:52:01 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:50 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:01 more
  • 你是不是暴露了?

    作者:袁首京 原創文章,轉載時請保留此宣告,并給出原文連接。 如果您是計算機相關從業人員,那么應該經歷不止一次網路安全專項檢查了,你肯定是收到過資訊系統技術檢測報告,要求你加強風險監測,確保你提供的系統服務堅實可靠了。 沒檢測到問題還好,檢測到問題的話,有些處理起來還是挺麻煩的,尤其是線上正在運行的 ......

    uj5u.com 2023-04-05 16:52:56 more
  • 細節拉滿,80 張圖帶你一步一步推演 slab 記憶體池的設計與實作

    1. 前文回顧 在之前的幾篇記憶體管理系列文章中,筆者帶大家從宏觀角度完整地梳理了一遍 Linux 記憶體分配的整個鏈路,本文的主題依然是記憶體分配,這一次我們會從微觀的角度來探秘一下 Linux 內核中用于零散小記憶體塊分配的記憶體池 —— slab 分配器。 在本小節中,筆者還是按照以往的風格先帶大家簡單 ......

    uj5u.com 2023-04-05 16:44:11 more