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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...