linux-0.11分析:boot文件 head.s 第三篇隨筆

来源:https://www.cnblogs.com/shuisanya/archive/2022/08/01/16541884.html
-Advertisement-
Play Games

head.s 參考 [github這個博主的][ https://github.com/sunym1993/flash-linux0.11-talk ] 改變棧頂位置 _pg_dir: startup_32: movl $0x10,%eax mov %ax,%ds mov %ax,%es mov % ...


head.s

參考 [github這個博主的][ https://github.com/sunym1993/flash-linux0.11-talk ]

  1. 改變棧頂位置

    _pg_dir:
    startup_32:
    	movl $0x10,%eax
    	mov %ax,%ds
    	mov %ax,%es
    	mov %ax,%fs
    	mov %ax,%gs
    	lss _stack_start,%esp
    

    先是分別ds,es,fs,gs的值都置成了0x10

    然後這段·lss _stack_start,esp相當於把ss:sep這個棧頂指針指向 _stack_start這個位置,而這個位置在head.s中找不到,它在sched.c

    long user_stack [ PAGE_SIZE>>2 ] ;
    
    struct {
    	long * a;
    	short b;
    	} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
    

    在linux-0.11文件kernel文件下的sched.c,在67~72行左右;

  2. 從新設置idt和gdt;

    call setup_idt
    call setup_gdt
    movl $0x10,%eax		; 重新載入所有段寄存器
    mov %ax,%ds		; 更改gdt後。CS已經
    mov %ax,%es		; 在“setup\u gdt”中重新載入
    mov %ax,%fs
    mov %ax,%gs
    lss _stack_start,%esp
    

    前兩行分別在設置 idt 和 gdt的值

    後面有重新設置了ds,es,fs,gs因為在設置idt和gdt中改變了這些值,而這裡在進行call操作時,並沒有壓棧,所以需要從新設置,還有從新設置棧頂

  3. 先看看dit的設置

    setup_idt:
    	lea ignore_int,%edx
    	movl $0x00080000,%eax
    	movw %dx,%ax		/* selector = 0x0008 = cs */
    	movw $0x8E00,%dx	/* interrupt gate - dpl=0, present */
    
    	lea _idt,%edi
    	mov $256,%ecx
    rp_sidt:
    	movl %eax,(%edi)
    	movl %edx,4(%edi)
    	addl $8,%edi
    	dec %ecx
    	jne rp_sidt
    	lidt idt_descr
    	ret
    

    設置了 **256 **個中斷描述符, 讓每一個中斷描述符中的中斷程式常式都指向一個 ignore_int 的函數地址

    看看 ignore_int

    ignore_int:
    	pushl %eax
    	pushl %ecx
    	pushl %edx
    	push %ds
    	push %es
    	push %fs
    	movl $0x10,%eax
    	mov %ax,%ds
    	mov %ax,%es
    	mov %ax,%fs
    	pushl $int_msg
    	call _printk
    	popl %eax
    	pop %fs
    	pop %es
    	pop %ds
    	popl %edx
    	popl %ecx
    	popl %eax
    	iret
    

    也就call了_printk這個地址這是一個列印的操作.....

  4. 再看看gdt的設置

    setup_gdt:
    	lgdt gdt_descr
    	ret
    gdt_descr:
    	.word 256*8-1		# so does gdt (not that that's any
    	.long _gdt		# magic number, but it works for me :^)
    	.align 3
    
    _gdt:	
    	.quad 0x0000000000000000	/* NULL descriptor */
    	.quad 0x00c09a0000000fff	/* 16Mb */
    	.quad 0x00c0920000000fff	/* 16Mb */
    	.quad 0x0000000000000000	/* TEMPORARY - don't use */
    	.fill 252,8,0
    

    跟之前設置的gdt一毛一樣,只是在記憶體中的位置變了

    然後看看記憶體的改變的樣子

  5. 然後看看進入main.c的那段代碼

    after_page_tables:
    	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
    L6:
    	jmp L6	
    

    把_main的地址入棧,當進行完jmp setup_paging這段操作後面彈棧就進入main.c函數了

  6. 接下里就看看setup_paging這個函數吧

    setup_paging:
    	movl $1024*5,%ecx		/* 5 pages - pg_dir+4 page tables */
    	xorl %eax,%eax
    	xorl %edi,%edi			/* pg_dir is at 0x000 */
    	cld;rep;stosl
    	movl $pg0+7,_pg_dir		/* set present bit/user r/w */
    	movl $pg1+7,_pg_dir+4		/*  --------- " " --------- */
    	movl $pg2+7,_pg_dir+8		/*  --------- " " --------- */
    	movl $pg3+7,_pg_dir+12		/*  --------- " " --------- */
    	movl $pg3+4092,%edi
    	movl $0xfff007,%eax		/*  16Mb - 4096 + 7 (r/w user,p) */
    	std
    1:	stosl			/* fill pages backwards - more efficient :-) */
    	subl $0x1000,%eax
    	jge 1b
    	xorl %eax,%eax		/* pg_dir is at 0x0000 */
    	movl %eax,%cr3		/* cr3 - page directory start */
    	movl %cr0,%eax
    	orl $0x80000000,%eax
    	movl %eax,%cr0		/* set paging (PG) bit */
    	ret	
    

    這是在設置頁目錄和頁表;

    分頁機制也是相當於改變了物理定址的方式,或者說給物理定址多加了一步

    先看看前面的分段機制:

    段選擇子:高12位 => 存儲的是段描述符的索引;段選擇子例如:ds,cs,es等等

    通過段描述符的索引找到段描述符:在得到 段基址 + 偏移地址 = 物理地址

    那麼現在這個分頁機制頁數類似的,只不過在是在分段機制的後面

    先來說一說官方的詞語:邏輯地址,線性地址,虛擬地址,物理地址

    邏輯地址:也就是我們程式員寫程式時給出的地址

    線性地址:吧邏輯地址經過分段機制後的出來的就是線性地址 32位

    虛擬地址:這個地址其實就是如果你開啟了分頁機制,那麼剛剛得到的線性地址就是虛擬地址,只是改了一個名字而已

    物理地址:如果不經過分頁機制,那麼剛剛得到的線性地址就是物理地址;如果經過分頁機制,那麼剛剛的線性地址(虛擬地址)經過分頁機制得到的就是物理地址;

    下麵通過一張圖來清晰的看看:

    現在來看看分頁機制如何定址的:

    先把線性地址(32位)分段:高10位;中間10位;最後12位

    1. 用高10位去頁目錄表尋找頁目錄項
    2. 然後用頁目錄項拼接上中間10位得到的值,去頁表中尋找頁表項
    3. 最後用頁表項加上最後12位的偏移地址得到就是物理地址

    而這一切的操作,都由電腦的一個硬體叫 MMU,中文名字叫記憶體管理單元,有時也叫 PMMU,分頁記憶體管理單元。由這個部件來負責將虛擬地址轉換為物理地址。

    那怎麼才算開啟了分頁機制了,就要用到這個cr0寄存器的PG位了

    由於linux0.11 用的是20位物理地址,所以大小為16MB

    1 個頁目錄表最多包含 1024 個頁目錄項(也就是 1024 個頁表),1 個頁表最多包含 1024 個頁表項(也就是 1024 個頁),1 頁為 4KB(因為有 12 位偏移地址),因此,16M 的地址空間可以用 1 個頁目錄表 + 4 個頁表搞定。

    4(頁表數)* 1024(頁表項數) * 4KB(一頁大小)= 16MB

    .org 0x1000
    pg0:
    .org 0x2000
    pg1:
    .org 0x3000
    pg2:
    .org 0x4000
    pg3:
    .org 0x5000
    

    開始的位置就存儲在cr3寄存器

    首先頁目錄存在 : 0x0000 ~ 0x1000

    而四個頁表項存在:0x1000 ~ 0x2000;0x2000 ~ 0x3000;0x3000 ~ 0x4000;0x4000 ~ 0x5000

    註意:這裡覆蓋了0x0000~0x5000的System的代碼因為這些代碼已經運行了,也沒有保存什麼有用的東西,所有可以覆蓋了

    那麼現在再來看看記憶體的佈局,就要進入main.c函數了

    1. 再次看看最後進入main.c的那段代碼吧

      after_page_tables:
      	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
      	
      setup_paging:
      	..... 此處省略一大段
      	ret	
      
      • 可以看到在jmp進入setup_paging之前先把 0,0,0, L6 和main.c的地址壓棧

      • 最後在執行完 setup_paging 之後又ret 彈棧頂元素就是main.c的地址

      • 至此就徹底進入了main.c的代碼中,在上面也不會ret結束,在上面就等待著其他用於程式調用操作系統,但是也並非這麼簡單

      最後來回憶一哈,boot這個文件下bootsect.s、setup.s、head.s都做了些什麼!!!(補充哈,這是在linux-0.11操作系統下喲);最後記憶體的樣子在上面了


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 什麼是Filter實現許可權攔截,比如說我們登陸一個網站,登陸成功後可以訪問其中的內容,退出登陸後就不能再對內容進行訪問,這就用到了我們的Filter實現許可權攔截。 那麼具體是怎麼實現的呢? 原理很簡單,我們可以給已登錄用戶session存放一個用於標記登陸的數據,只需要在過濾器里看能否獲取數據來進行 ...
  • 監聽器種類數不勝數,監聽器最常被應用在GUI中,不過網站中也可以設置監聽器,這裡我們利用sessionListener實現一個網站線上人數統計的監聽來瞭解一下監聽器的使用。 首先建立一個類來實現監聽器OnlineListener.class 1 package com.jms.listener; 2 ...
  • 過濾器是用來過濾瀏覽器中的數據,例如web伺服器受到一些垃圾請求,後臺可以不處理這些請求或者報錯,還可以用來解決亂碼問題等。 過濾器是在伺服器啟動的時候初始化,在伺服器關閉的時候銷毀。 下麵我們用實例來演示: 首先建立一個過濾器類CharacterEncodingFilter.class 1 pac ...
  • 一、什麼是JavaBean? JavaBean 是特殊的 Java 類,使用 Java 語言書寫,並且遵守 JavaBean API 規範。 JavaBean具有以下的特征: 具有無參構造函數; 屬性私有化; 私有化的屬性通過get和set方法提供訪問。 二、JavaBean的作用 一是為了提高代碼 ...
  • 今天我們先來講一下狀態壓縮dp(也稱狀壓dp)。狀壓dp,顧名思義,就是把狀態壓縮起來。比如對於8*8 的棋盤,每個位置可以放一個棋子,對於在第i行第2個位置和第6個位置放了棋子,我們可能需要8維或9維數組表示。因此我們就有了把一行狀態壓縮成一個數字的做法。一般我們會轉化為二進位,如果每個位置可以有 ...
  • 又是寫ORM的博客,但是寫其它的類庫,我不會啊,只有這個我能寫一寫,希望大家輕噴 最近修改內容 以前只支持.NET Framework,現在修改為支持.NET Framework、.NET Standard、.NET Core多目標平臺 以前工程依賴了具體的資料庫操作類庫,不方便實現支持多目標平臺, ...
  • 公司業務歷史悠久且複雜,資料庫的表更是多而繁雜,每次基於老業務做功能開發都需要去翻以前的表和業務代碼。需要理解舊的表的用途以及包含的欄位的含義,表少還好說,但是表一多這就很浪費時間,而且留下來的文檔都是殘缺不全,每次查一些表的含義都要捯飭很久。在網上搜索關於資料庫文檔管理工具搜到最多的就是Screw... ...
  • 目 錄 1. 概述... 2 2. 設備運維業務... 3 3. “低代碼”表單開發工具... 6 1. 概述 iNeuOS工業互聯網操作系統增加了設備運維業務大屏統計功能和所謂的“低代碼”表單開發工具。 設備運維業務大屏統計功能主要統計當前系統設備數量、預警設備數量、通訊正常、通訊干擾、通訊中斷及 ...
一周排行
    -Advertisement-
    Play Games
  • 隨著Aspire發佈preview5的發佈,Microsoft.Extensions.ServiceDiscovery隨之更新, 服務註冊發現這個屬於老掉牙的話題解決什麼問題就不贅述了,這裡主要講講Microsoft.Extensions.ServiceDiscovery(preview5)以及如何 ...
  • 概述:通過使用`SemaphoreSlim`,可以簡單而有效地限制非同步HTTP請求的併發量,確保在任何給定時間內不超過20個網頁同時下載。`ParallelOptions`不適用於非同步操作,但可考慮使用`Parallel.ForEach`,儘管在非同步場景中謹慎使用。 對於併發非同步 I/O 操作的數量 ...
  • 1.Linux上安裝Docken 伺服器系統版本以及內核版本:cat /etc/redhat-release 查看伺服器內核版本:uname -r 安裝依賴包:yum install -y yum-utils device-mapper-persistent-data lvm2 設置阿裡雲鏡像源:y ...
  • 概述:WPF界面綁定和渲染大量數據可能導致性能問題。通過啟用UI虛擬化、非同步載入和數據分頁,可以有效提高界面響應性能。以下是簡單示例演示這些優化方法。 在WPF中,當你嘗試綁定和渲染大量的數據項時,性能問題可能出現。以下是一些可能導致性能慢的原因以及優化方法: UI 虛擬化: WPF提供了虛擬化技術 ...
  • 引言 上一章節介紹了 TDD 的三大法則,今天我們講一下在單元測試中模擬對象的使用。 Fake Fake - Fake 是一個通用術語,可用於描述 stub或 mock 對象。 它是 stub 還是 mock 取決於使用它的上下文。 也就是說,Fake 可以是 stub 或 mock Mock - ...
  • 為.net6在CentOS7上面做準備,先在vmware虛擬機安裝CentOS 7.9 新建CentOS764位的系統 因為CentOS8不更新了,所以安裝7;簡單就一筆帶過了 選擇下載好的操作系統的iso文件,下載地址https://mirrors.aliyun.com/centos/7.9.20 ...
  • 經過前面幾篇的學習,我們瞭解到指令的大概分類,如:參數載入指令,該載入指令以 Ld 開頭,將參數載入到棧中,以便於後續執行操作命令。參數存儲指令,其指令以 St 開頭,將棧中的數據,存儲到指定的變數中,以方便後續使用。創建實例指令,其指令以 New 開頭,用於在運行時動態生成並初始化對象。方法調用指... ...
  • LiteDB 是一個輕量級的嵌入式 NoSQL 資料庫,其設計理念與 MongoDB 類似,但它是完全使用 C# 開發的,因此與 C# 應用程式的集成非常順暢。與 SQLite 相比,LiteDB 提供了 NoSQL(即鍵值對)的數據存儲方式,並且是一個開源且免費的項目。它適用於桌面、移動以及 We ...
  • 1 開源解析和拆分文檔 第三方的工具去對文件解析拆分,去將我們的文件內容給提取出來,並將我們的文檔內容去拆分成一個小的chunk。常見的PDF word mark down, JSON、HTML。都可以有很好的一些模塊去把這些文件去進行一個東西去提取。 優勢 支持豐富的文檔類型 每種文檔多樣化選擇 ...
  • OOM是什麼?英文全稱為 OutOfMemoryError(記憶體溢出錯誤)。當程式發生OOM時,如何去定位導致異常的代碼還是挺麻煩的。 要檢查OOM發生的原因,首先需要瞭解各種OOM情況下會報的異常信息。這樣能縮小排查範圍,再結合異常堆棧、heapDump文件、JVM分析工具和業務代碼來判斷具體是哪 ...