閱讀分析Linux0.11 /boot/head.s

目錄

  • 初始化IDT、IDTR和GDT、GDTR
  • 檢查協處理器并設置CR0寄存器
  • 初始化頁表和CR3寄存器,開啟分頁

初始化IDT、IDTR和GDT、GDTR

startup_32:movl $0x10,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fsmov %ax,%gslss _stack_start,%espcall setup_idtcall setup_gdtmovl $0x10,%eax		# reload all the segment registersmov %ax,%ds		# after changing gdt. CS was alreadymov %ax,%es		# reloaded in 'setup_gdt'mov %ax,%fsmov %ax,%gslss _stack_start,%espxorl %eax,%eax
1:	incl %eax		# check that A20 really IS enabledmovl %eax,0x000000	# loop forever if it isn'tcmpl %eax,0x100000je 1b

個人閱讀上面代碼的知識點:

  • lss _stack_start,%esp 首先lss是遠指針加載指令,不了解的查deepseek就懂了。“_stack_start”這個標號找了很久都找不到在哪兒定義的,搜索整個源碼都沒有。最后還是求助deepseek終于搞懂了(deepseek真自學神器)。要把“_stack_start”中下劃線去掉,直接搜索“stack_start”,才知道這個標號是定義在kernel/sched.c中的結構體變量名。之所以要加下劃線,是因為早期的編譯器編譯時會在C中變量名前面加,現在不加了 。由于對kernel代碼不懂,所以沒過多研究,知道是在初始化棧就行了。
  • call setup_idt 調用子程序初始化IDT表,通過指令lidt將IDT表的長度和地址加載到IDTR寄存器。 還處在內核初始化階段,所以只是簡單的將IDT表中的描述符初始化為同一個,都指向ignore_int這個中斷處理程序。ignore_int子程序就是本文件中,功能就是打印一段字符。
    • lidt指令加載的IDT表的地址是線性地址,初始化階段,此時還沒開啟分頁模式,線性地址等于物理地址。開啟分頁后,IDTR中的線性地址要經過MMU查頁表轉化成IDT表在內存中的物理地址。
    • 需要對IDT表描述符了解,IDT表描述符總共8字節,小端法存入內存,先存低4字節,再高4字節,先低2字節,再高2字節。之所以提小端法是因為我一開始沒注意,把描述符的選擇子判斷錯了。
  • call setup_gdt 調用子程序初始化GDT表,通過指令lgdt將GDT表的長度和地址加載到GDTR寄存器。 GDT表自己構造。
    • lgdt指令加載的GDT表的地址是線性地址,只不過初始化階段,此時還沒開啟分頁模式,線性地址等于物理地址。開啟分頁后,GDTR中的線性地址要經過MMU查頁表轉化成GDT表在內存中的物理地址。
    • 構造GDT表時,GDT表中的描述符大小為8字節,第一個描述符為全0,后面描述符的基地址base=0X00000000,limit=最大值(0X000FFF),把內存管理就設置成了平坦模式, 現代操作系統支持這種模式,這樣一個進程的 代碼段、數據段、棧段就共享同一個線性地址空間了,方便虛擬內存管理
  • 保護模式下,指令執行過程中,查詢IDT表、GDT表的工作是有硬件來實現的,不是軟件模擬的,所以對IDT、GDT中描述符的結構也是硬件規定的。我們在構造這兩個表時要按照硬件的規定來。
  • movl %eax,0x000000;cmpl %eax,0x100000 這段是負責檢查A20地址線是否打開,如果沒打開那么0X100000地址就等于0X000000地址,cmpl比較的結果就是相等,那就卡在這個執行死循環。
  • je 1b 這個條件跳轉指令開始看了我也很蒙,還是靠deepseek,這是以前的寫法“b”表示滿足條件跳轉到后面的標號,“f”表示滿足條件跳轉到前面的標號。b和f指示跳轉的方向的。現在好像不用這種用法了



檢查協處理器并設置CR0寄存器

movl %cr0,%eax		# check math chipandl $0x80000011,%eax	# Save PG,PE,ET
/* "orl $0x10020,%eax" here for 486 might be good */orl $2,%eax		# set MPmovl %eax,%cr0call check_x87jmp after_page_tables/** We depend on ET to be correct. This checks for 287/387.*/
check_x87:fninitfstsw %axcmpb $0,%alje 1f			/* no coprocessor: have to set bits */movl %cr0,%eaxxorl $6,%eax		/* reset MP, set EM */movl %eax,%cr0ret

個人閱讀上面代碼的知識點:

  • 了解CR0寄存器,CR0寄存器中,PE位開啟保護模式、PG位開啟分頁機制、WP位寫保護位。還有其它涉及協處理器的位,我一知半解就不寫了。上面這段代碼主要就是檢查協處理器,然后進行設置。 指令call check_x87就是調用子程序檢查是否存在287/387協處理器。
  • fninit fstsw這兩個是協處理器指令,自己查deepseek能看懂,不贅述了。check_x87這個子程序功能就是檢查協處理器的。



初始化頁表和CR3寄存器,開啟分頁

after_page_tables:pushl $0		# These are the parameters to main :-)pushl $0pushl $0pushl $L6		# return address for main, if it decides to.pushl $mainjmp setup_paging
L6:jmp L6			# main should never return here, but# just in case, we know what happens.
-----------------------------------------------------------------------------------------
setup_paging:movl $1024*5,%ecx		/* 5 pages - pg_dir+4 page tables */xorl %eax,%eaxxorl %edi,%edi			/* pg_dir is at 0x000 */cld;rep;stoslmovl $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,%edimovl $0xfff007,%eax		/*  16Mb - 4096 + 7 (r/w user,p) */std
1:	stosl			/* fill pages backwards - more efficient :-) */subl $0x1000,%eaxjge 1bxorl %eax,%eax		/* pg_dir is at 0x0000 */movl %eax,%cr3		/* cr3 - page directory start */movl %cr0,%eaxorl $0x80000000,%eaxmovl %eax,%cr0		/* set paging (PG) bit */ret			/* this also flushes prefetch-queue */

個人閱讀上面代碼的知識點:

  • jmp setup_paging 跳轉到初始化頁表的子程序。該指令前面的push指令是在壓棧,pushl $main 目的是從setup_paging子程序返回時,ret指令能返回到main程序。 我還沒看過main.c的代碼,不贅述了。功能就是結束head.s的初始化任務。開啟main.c
    • 強化我自己的一個知識點,call和jmp都是跳轉指令,區別就是call跳轉前要將返回地址壓棧,jmp無需壓棧直接跳轉無法返回。開始我還疑問為什么不用call指令,然后返回到main.c,仔細一想call指令是將下一條指令的地址壓棧,只能返回到本源文件call的下一條指令。而我們需要的是結束head.s,返回到另一個源文件main.c。所以通過pushl $main壓入返回地址,不用call調用,而用jmp直接跳轉到setup_paging初始化頁表子程序
  • setup_paging子程序初始化頁表、CR3寄存器,置CR0寄存器的PG為1開啟分頁。了解頁表和頁表項,即使有些匯編指令不熟,查一下就是看懂這段了。
    • stosl指令,我忽略了它執行時會自動增加EDI。stos、movs、cmps、scas都是字符串操作指令,可以順便都了解一下。偉大的deepseek很好用的。
    • CR3寄存器是用來存放頂級頁表的物理地址的。MMU將線性地址轉換位物理地址時需要通過CR3寄存器找到頁表進行映射。CR3寄存器的改變,會引起TLB表的改變。CR3改變代表頁表的切換,TLB相當于部分頁表項的緩存,自然也要切換



.org 0x1000
pg0:.org 0x2000
pg1:.org 0x3000
pg2:.org 0x4000
pg3:.org 0x5000
剛看見這段代碼時,感覺整齊有規律,就是不知道作用是什么。即使知道了.org這個偽指令的功能,也不知道寫這段代碼的意義。直到最后看到初始化頁表的子程序setup_paging。補充點因為這段代碼學到的零碎知識點。
(1).org(Origin)是匯編語言中的一條偽指令(Directive),用于指定程序或數據在內存中的起始地址。 在需要直接控制內存布局的場景(如裸機開發、嵌入式開發)中,.org 是一個簡單有效的工具,但在高層編程中通常由鏈接器管理地址分配
(2) 這段代碼其實就是在劃分頁表的頁。它們之間的地址差是0X1000(4KB),從0地址開始到0X5000共20KB,分成5個頁,每頁4KB,第一個頁是頁目錄表,其余4個頁都是普通頁表。setup_paging子程序的開始將5個頁20KB用0填充,接著構造4個普通物理頁的頁表項填充到0地址的頁目錄表,最后構造一個普通物理頁的頁表項填充4個普通物理頁。
(3)你還可以觀察到初始化IDT、IDTR和GDT、GDTR的代碼,還有檢查協處理器并設置CR0寄存器的代碼都寫在這段代碼之前,因為那些初始化、檢查的代碼執行完就沒用了,它們所在的內存空間可以被頁表覆蓋; 結束head.s跳轉到main.c的代碼、初始化頁表的子程序setup_paging、IDT表GDT表的位置,包括中斷處理程序ignore_int的代碼位置都在“.org 0X5000”之后,就是防止誤操作,初始化頁表時覆蓋比較重要的代碼、表空間
(4) 內存中IDT表、GDT表,各有1個,所有應用程序和操作系統共用;頁表有多個,每個應程序都有自己的頁表,任務切換時,通過修改CR3寄存器切換頁表,同時TLB表也要刷新。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/76111.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76111.shtml
英文地址,請注明出處:http://en.pswp.cn/web/76111.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

33、單元測試實戰練習題

以下是三個練習題的具體實現方案,包含完整代碼示例和詳細說明: 練習題1:TDD實現博客評論功能 步驟1:編寫失敗測試 # tests/test_blog.py import unittest from blog import BlogPost, Comment, InvalidCommentErrorclass TestBl…

16-算法打卡-哈希表-兩個數組的交集-leetcode(349)-第十六天

1 題目地址 349. 兩個數組的交集 - 力扣(LeetCode)349. 兩個數組的交集 - 給定兩個數組 nums1 和 nums2 ,返回 它們的 交集 。輸出結果中的每個元素一定是 唯一 的。我們可以 不考慮輸出結果的順序 。 示例 1:輸入:nu…

SciPy庫詳解

SciPy 是一個用于數學、科學和工程計算的 Python 庫,它建立在 NumPy 之上,提供了許多高效的算法和工具,用于解決各種科學計算問題。 CONTENT 1. 數值積分功能代碼 2. 優化問題求解功能代碼3. 線性代數運算功能代碼 4. 信號處理功能代碼 5. 插…

杰弗里·辛頓:深度學習教父

名人說:路漫漫其修遠兮,吾將上下而求索。—— 屈原《離騷》 創作者:Code_流蘇(CSDN)(一個喜歡古詩詞和編程的Coder😊) 杰弗里辛頓:當堅持遇見突破,AI迎來新紀元 一、人物簡介 杰弗…

BladeX單點登錄與若依框架集成實現

1. 概述 本文檔詳細介紹了將BladeX認證系統與若依(RuoYi)框架集成的完整實現過程。集成采用OAuth2.0授權碼流程,使用戶能夠通過BladeX賬號直接登錄若依系統,實現無縫單點登錄體驗。 2. 系統架構 2.1 總體架構 #mermaid-svg-YxdmBwBtzGqZHMme {font-fa…

初識Redis · set和zset

目錄 前言: set 基本命令 交集并集差集 內部編碼和應用場景 zset 基本命令 交集并集差集 內部編碼和應用場景 應用場景(AI生成) 排行榜系統 應用背景 設計思路 熱榜系統 應用背景 設計思路 熱度計算方式 總結對比表 前言&a…

playwright 教程高級篇:掌握網頁自動化與驗證碼處理等關鍵技術詳解

Playwright 教程高級篇:掌握網頁自動化與驗證碼處理等關鍵技術詳解 本教程將帶您一步步學習如何使用 Playwright——一個強大的瀏覽器自動化工具,來完成網頁任務,例如提交鏈接并處理旋轉驗證碼。我們將按照典型的自動化流程順序,從啟動瀏覽器到關閉瀏覽器,詳細講解每個步驟…

數據結構(完)

樹 二叉樹 構建二叉樹 int value;Node left;Node right;public Node(int val) {valueval;} 節點的添加 Node rootnull;public void insert(int num) {Node nodenew Node(num);if(rootnull) {rootnode;return;}Node index root;while(true) {//插入的節點值小if(index.value&g…

FastAPI與SQLAlchemy數據庫集成與CRUD操作

title: FastAPI與SQLAlchemy數據庫集成與CRUD操作 date: 2025/04/16 09:50:57 updated: 2025/04/16 09:50:57 author: cmdragon excerpt: FastAPI與SQLAlchemy集成基礎包括環境準備、數據庫連接配置和模型定義。CRUD操作通過數據訪問層封裝和路由層實現,確保線程安全和事務…

一個基于Django的寫字樓管理系統實現方案

一個基于Django的寫字樓管理系統實現方案 用戶現在需要我用Django來編寫一個寫字樓管理系統的Web版本,要求包括增刪改查寫字樓的HTML頁面,視頻管理功能,本地化部署,以及人員權限管理,包含完整的代碼結構和功能實現&am…

mongodb在window10中創建副本集的方法,以及node.js連接副本集的方法

創建Mongodb的副本集最好是新建一個文件夾,如D:/data,不要在mongodb安裝文件夾里面創建副本集,雖然這樣也可以,但是容易造成誤操作或路徑混亂;在新建文件夾里與現有 MongoDB 數據隔離,避免誤操作影響原有數…

Maven 多倉庫與鏡像配置全攻略:從原理到企業級實踐

Maven 多倉庫與鏡像配置全攻略:從原理到企業級實踐 一、核心概念:Repository 與 Mirror 的本質差異 在 Maven 依賴管理體系中,repository與mirror是構建可靠依賴解析鏈的兩大核心組件,其核心區別如下: 1. Repositor…

STM32 四足機器人常見問題匯總

文章不介紹具體參數,有需求可去網上搜索。 特別聲明:不論年齡,不看學歷。既然你對這個領域的東西感興趣,就應該不斷培養自己提出問題、思考問題、探索答案的能力。 提出問題:提出問題時,應說明是哪款產品&a…

MySQL 中 `${}` 和 `#{}` 占位符詳解及面試高頻考點

文章目錄 一、概述二、#{} 和 ${} 的核心區別1. 底層機制代碼示例 2. 核心區別總結 三、為什么表名只能用 ${}?1. 預編譯機制的限制2. 動態表名的實現 四、安全性注意事項1. ${} 的風險場景2. 安全實踐 五、面試高頻考點1. 基礎原理類問題**問題 1**:**問…

C語言編譯預處理2

#include <XXXX.h>和#include <XXXX.c> #include "XXXX.h" 是 C 語言中一條預處理指令 #include <XXXX.h>&#xff1a;這種形式用于包含系統標準庫的頭文件。預處理器會在系統默認的頭文件搜索路徑中查找XXXX.h 文件。例如在 Linux 系統中&#…

Elasticvue-輕量級Elasticsearch可視化管理工具

Elasticvue一個免費且開源的 Elasticsearch 在線可視化客戶端&#xff0c;用于管理 Elasticsearch 集群中的數據&#xff0c;完全支持 Elasticsearch 版本 8.x 和 7.x. 功能特色&#xff1a; 集群概覽索引和別名管理分片管理搜索和編輯文檔REST 查詢快照和存儲庫管理支持國際…

Git提交規范及最佳實踐

Git 提交規范通常是為了提高代碼提交的可讀性、可維護性和自動化效率&#xff08;如生成 ChangeLog&#xff09;。以下是常見的 Conventional Commits 規范&#xff0c;結合社區最佳實踐總結而成&#xff1a; 1. 提交格式 每次提交的 commit message 應包含三部分&#xff1a;…

Ubuntu中snap

通過Snap可以安裝眾多的軟件包。需要注意的是&#xff0c;snap是一種全新的軟件包管理方式&#xff0c;它類似一個容器擁有一個應用程序所有的文件和庫&#xff0c;各個應用程序之間完全獨立。所以使用snap包的好處就是它解決了應用程序之間的依賴問題&#xff0c;使應用程序之…

android studio 運行java main報錯

運行某個帶main函數的java文件報錯 Could not create task :app:Test.main(). > SourceSet with name main not found. 解決辦法&#xff1a;在工程的.idea/gradle.xml 文件下添加&#xff1a; <option name"delegatedBuild" value"false" /&g…

openssh離線一鍵升級腳本分享(含安裝包)

查看當前的版本 [rootmyoracle ~]#ssh -V相關安裝包下載地址 openssh下載地址&#xff1a;http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssl下載地址&#xff1a;https://www.openssl.org/source/zlib下載地址&#xff1a;http://www.zlib.net/今天演示從7.4升級…