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
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...