【操作系統】1.操作系統啟動

来源:https://www.cnblogs.com/stuxuan/archive/2023/08/23/17649710.html
-Advertisement-
Play Games

1.打開電源 (1)x86 PC開機時CPU處於實模式,實模式的定址方式是CS:IP (CS左移4位+IP) (2)開機時段寄存器CS=0xFFFF,偏移量IP=0x0000,段寄存器左移4位加上偏移量是實際地址,也就是定址地址為0xFFFF0 (ROM BIOS映射區) (3)檢查RAM,鍵盤,顯 ...


1.打開電源

(1)x86 PC開機時CPU處於實模式,實模式的定址方式是CS:IP (CS左移4位+IP)

(2)開機時段寄存器CS=0xFFFF,偏移量IP=0x0000,段寄存器左移4位加上偏移量是實際地址,也就是定址地址為0xFFFF0 (ROM BIOS映射區)

(3)檢查RAM,鍵盤,顯示器,磁碟

(4)將0磁軌0扇區512個位元組讀入0x7c00處(操作系統的引導扇區)

(5)設置cs=0x7c0,ip=0x0000

 2.引導扇區代碼bootsect.s

實驗bootsect.s代碼

(1)將bootsect從0x7c00處移動到0x90000處

(2)將setup讀入到0x90200處

(3)將system讀入0x91000處

(4)將PC移動到0x90200處,bootsect結束

1. 移動bootsect.s。讀取0x7c00處的記憶體,首先設置源地址和目標地址,並且將0x7c00記憶體處的256個字(512位元組)移動到了0x90000

start:
  mov ax, #BOOTSEG mov ds, ax ! 將 ds 段寄存器設置為0x07C0(16位彙編,後面的放入前面)
  mov ax, #INITSEG mov es, ax ! 將 es 段寄存器設置為0x9000
  sub si, si           ! 源地址   ds:si 0x7c00
  sub di, di           ! 目標地址 es:di 0x90000
  mov cx, #256          ! 設置移動計數值256字(512位元組)
  rep movw             ! 重覆移動直到cx為0,將bootsect移動到了0x90000處
  jmpi go, INITSEG            ! 跳轉到0x9000:go處,將設置es、ss、sp都為0x9000(相當於是初始化)

2. 0x13讀磁碟中斷,讀取setup代碼。在磁碟第2個扇區開始,讀取4個扇區的內容,並將這4個扇區的內容寫入到0x90200記憶體處。因為bootsect占用512位元組,所以需要寫在0x90000的512個位元組之後,16位表示就是在0x90200處寫入。

go: mov ax, cs                ! 0x9000
    ! 將ds、es、ss都置成移動後代碼所在段處0x9000、棧頂地址設置為0x9fff00,來遠離代碼位置
    mov ds, ax    mov es, ax    mov ss, ax    mov sp, #0xFF00 
load_setup:        
// 載入setup模塊, 從磁碟第2個扇區開始讀4個扇區, 寫入0x90200處
mov dx, #0x0000 ! 數據寄存器 mov cx, #0x0002 ! 計數寄存器 ch:cx高8位, 0x00(柱面號0); cl:低8位, 0x02(開始扇區為2) mov bx, #0x0200 ! 基址寄存器 寫到es:bx 0x90200處 mov ax, #0x0200+SETUPLEN ! 累加器 ah: ax高8位, 0x20(讀磁碟); al:ax低8位, 0x04(讀取4個扇區) int 0x13 ! BIOS讀磁碟扇區中斷 jnc ok_load_setup ! 跳轉指令, CF=0則跳轉。如果沒有讀錯,則跳轉到載入setup執行;如果讀錯,則複位驅動器重新讀。 mov dx, #0x0000 ! 對驅動器0進行操作 mov ax, # 0x0000 ! 複位 int 0x13 ! 執行0x13中斷(BIOS讀磁碟扇區的中斷) j load_setup ! 重讀

 3. 0x10顯示字元中斷,顯示字元在顯示器上。ok_load_setup,顯示歡迎頁面,調用read_it讀入system,最後將控制權交接給setup模塊,也就是將PC移動到0x90200處,開始執行setup。bootsect正式結束。

ok_load_setup:    ! 載入setup,顯示開機文字,讀取system
    ! 讀取setup,顯示開機文字
    mov dl, #0x00    mov ax, #0x0800        ! ah=8,獲得磁碟驅動器的參數
    int 0x13         mov    ch, #0x00   mov sectors, cx
    mov ah, #0x03    xor bh, bh         int 0x00 ! 讀游標
    mov cx, #24      mov bx, #0x0007    ! 7是顯示屬性
    mov bp, #msg1    mov ax, #1301      int 0x10 !顯示字元
    ! 讀取system模塊到0x10000處
    mov ax, #SYSSEG        ! SYSSEG=0X1000
    mov es, ax
    call read_it          ! 讀入system
    jmpi 0, SETUPSEG      ! 轉入0x9020:0X0000執行setup.s

 3.引導扇區代碼setup.s

實驗setup.s代碼

讀取硬體參數,將操作系統移動到0地址處,進入保護模式,初始化一個很簡單的gdt表,跳轉到system模塊

1. setup主要任務是讀取硬體參數,初始化一個數據結構來管理這些硬體設備,並移動操作系統到記憶體0地址處。其中0x15中斷是讀取擴展記憶體大小(Intel原先記憶體只有1m,我們把1m以外的記憶體稱為擴展記憶體)放在ax寄存器里,之後放到記憶體0x90002處。接下來do_move將0x90000處開始的數據移動到記憶體絕對地址0x00000處。

! 取游標位置,包括其他硬體參數,放到0x90000處
start : mov ax, #INITSEG    mov ds, ax    mov ah, #0x03
        xor bh, bh          int 0x10      mov [0], dx     ! 游標位置設置為0x90000
        mov ah, #0x88       int 0x15      mov [2], ax ...    ! 讀取擴展記憶體的大小(1m以外的記憶體稱為擴展記憶體), 放在0x90002處
        cli    ! 不允許中斷
        mov ax, #0x0000, cld        ! 設置讀取目標位置和遞增讀取方式
! 將system模塊從0x10000動到0x00000處        
do_mov: mov es, ax          add ax, #0x1000
        cmp ax, #0x9000     jz end_mov              ! 若ax=0x9000,表示移動完成,則跳出
        mov ds, ax          sub di, di    sub si, si        ! 設置源地址0x90000和目標地址0x00000
        mov cx, #0x8000   ! 設置移動數據的長度
        rep movsw         ! 將system模塊移動到0地址
        jmp do_move

2. 進入保護模式。cs左移4位+ip最大有20位地址,也就是最大記憶體空間只有1M。setup最後一步要從16位機切換到32定址方式。

  實模式:CS:IP,CS << 4 + IP,共20位定址空間

  保護模式:CS實際上是查表GDT(Global Descriptor Table),地址為CS查表 + IP

mov ax,#0x0001  ! 將0x0001放入ax寄存器
mov cr0,ax    ! 將ax放入cr0寄存器,這一步非常重要
                ! cr0寄存器最後一位是0的時候是CS:IP實模式
               ! cr0寄存器最後一位是1的時候是保護模式,需要走另外一條解釋指令的電路
jmpi 0,8     ! cs:8, ip:0; 這裡實際上跳轉的地址就是0地址

 4.head.s

head.s是system中的第一個模塊,負責重新載入各個數據段寄存器,重新設置中斷描述符表,重新設置全局描述符表,設置分頁處理機制(一個頁目錄表和4個頁表),打開20號地址線訪問4G記憶體,並且將main函數壓棧,head執行完後彈出main,轉到main函數。

startup_32:
    movl $0x10, %eax  ! 現在開始是32位彙編,前面的放入後面
    mov %ax, %ds
    mov %ax, %es
    mov %as, %fs
    mov %as, %gs        ! 指向gdt的0x10數據項
  lss _stact_start, %esp  ! 設置系統棧
  call setup_idt       ! 設置中斷描述符表子程式
  call setup_gdt       ! 設置全局描述符表子程式
  mov $0x10, %eax      ! 重載所有的段寄存器
...

 5.main函數

main函數中有大量初始化函數,例如初始化硬碟,初始化記憶體,將每4K記憶體作為一個頁等等

 6.系統調用

普通函數調用是通過call跳轉對應函數的地址繼續執行

系統調用是調用系統庫中為該系統調用編寫的一個介面函數,叫 API(Application Programming Interface)。API 並不能完成系統調用的真正功能,它要做的是去調用真正的系統調用,過程是:

  • 把系統調用的編號存入 EAX
  • 把函數參數存入其它通用寄存器
  • 觸發 0x80 號中斷(int 0x80)

這裡研究 close() 中的API,以巨集 _syscall1(int, close, int, fd) 為例子,它在 include/unistd.h 中定義了展開的形式

這就是 API 的定義。它先將巨集 __NR_close 存入 EAX,將參數 fd 存入 EBX,然後進行 0x80 中斷調用。調用返回後,從 EAX 取出返回值,存入 __res,再通過對 __res 的判斷決定傳給 API 的調用者什麼樣的返回值。

其中 __NR_close 在 include/unistd.h 中定義為 6

int close(int fd)
{
    long __res;
    __asm__ volatile ("int $0x80"
        : "=a" (__res)
        : "0" (__NR_close),"b" ((long)(fd)));
    if (__res >= 0)
        return (int) __res;
    errno = -__res;
    return -1;
}

在內核初始化時,主函數調用了 sched_init() 初始化函數:

void main(void)
{
//    ……
    time_init();
    sched_init();
    buffer_init(buffer_memory_end);
//    ……
}

sched_init() 在 kernel/sched.c 中定義為:

void sched_init(void)
{
//    ……
    set_system_gate(0x80,&system_call);
}

set_system_gate 是個巨集,在 include/asm/system.h有定義。這段巨集就是填寫 IDT(中斷描述符表),將 system_call 函數地址寫到 0x80 對應的中斷描述符中,也就是在中斷 0x80 發生後,自動調用函數 system_call

下麵是 system_call。該函數純彙編打造,定義在 kernel/system_call.s 中。system_call 用 .globl 修飾為其他函數可見

call sys_call_table(,%eax,4) 之前是一些壓棧保護,修改段選擇子為內核段,call sys_call_table(,%eax,4) 之後是看看是否需要重新調度,這些都與本實驗沒有直接關係,此處只關心 call sys_call_table(,%eax,4) 這一句。

根據彙編定址方法它實際上是:call sys_call_table + 4 * %eax,其中 eax 中放的是系統調用號,即 __NR_xxxxxx

顯然,sys_call_table 一定是一個函數指針數組的起始地址,它定義在 include/linux/sys.h 中:

fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,...

增加實驗要求的系統調用,需要在這個函數表中增加兩個函數引用 ——sys_iam 和 sys_whoami。當然該函數在 sys_call_table 數組中的位置必須和 __NR_xxxxxx 的值對應上。

!……
! # 這是系統調用總數。如果增刪了系統調用,必須做相應修改
nr_system_calls = 72
!……

.globl system_call
.align 2
system_call:

! # 檢查系統調用編號是否在合法範圍內
    cmpl \$nr_system_calls-1,%eax
    ja bad_sys_call
    push %ds
    push %es
    push %fs
    pushl %edx
    pushl %ecx

! # push %ebx,%ecx,%edx,是傳遞給系統調用的參數
    pushl %ebx

! # 讓ds, es指向GDT,內核地址空間
    movl $0x10,%edx
    mov %dx,%ds
    mov %dx,%es
    movl $0x17,%edx
! # 讓fs指向LDT,用戶地址空間
    mov %dx,%fs
    call sys_call_table(,%eax,4)  ! # 關鍵代碼
    pushl %eax
    movl current,%eax
    cmpl $0,state(%eax)
    jne reschedule
    cmpl $0,counter(%eax)
    je reschedule

 


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

-Advertisement-
Play Games
更多相關文章
  • ## 教程簡介 AOP為Aspect Oriented Programming的縮寫,意為:面向切麵編程,通過預編譯方式和運行期間動態代理實現程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP可以對 ...
  • 上一篇介紹了`DataFrame`的顯示參數,主要是對`DataFrame`中值進行調整。 本篇介紹`DataFrame`的顯示樣式的調整,顯示樣式主要是對錶格本身的調整,比如顏色,通過顏色可以突出顯示重要的值,觀察數據時可以更加高效的獲取主要信息。 下麵介紹一些針對單個數據和批量數據的樣式調整方式 ...
  • 在Java語言中,創建線程並不像創建對象一樣簡單。雖然只需要使用new Thread()即可創建線程,但實際上創建線程比創建對象複雜得多。創建對象只需在JVM的堆中分配記憶體,而創建線程需要調用操作系統內核的API,併為線程分配一系列資源,這個成本相對較高。因此,線程被視為重量級的對象,應儘量避免頻繁... ...
  • 最近閱讀了《ASP.NET Core 技術內幕與項目實戰——基於DDD與前後端分離》(作者楊中科)的第八章,對於Core入門的我來說體會頗深,整理相關筆記。 JWT:全稱“JSON web toke”,目前流行的跨域身份驗證解決方案; 標識框架(identity):由ASP.NET Core提供的框 ...
  • 在項目中經常會遇到類似如下要求的需求,創建允許自由拖動的控制項,這樣的需求可以使用WPF的裝飾器Adorner來實現。 一、什麼是裝飾器? 裝飾器是一種特殊類型的FrameworkElement,裝飾器始終呈現在被裝飾元素的頂部,用於向用戶提供可視化提示。裝飾器可以在不改變原有控制項結構的基礎上,將功能 ...
  • ## 一:背景 ### 1. 講故事 我發現有很多的 .NET程式員 寫了很多年的代碼都沒弄清楚什麼是 `虛擬地址`,更不用談什麼是 `物理地址` 以及Windows是如何實現地址映射的了?這一篇我們就來聊一聊這兩者之間的聯繫。 ## 二:地址映射研究 ### 1. 找虛擬地址 怎麼去找 `虛擬地址 ...
  • ## 一、什麼是SignalR: **SignalR** 是用於構建需要實時用戶交互或實時數據更新的Web 應用程式的一個開放源代碼.NET 庫。不僅僅用在Web應用中,後面會講到它的應用範圍。它簡化了簡化了構建實時應用程式的過程,包括**ASP.NET Server**庫和**JavaScript ...
  • 哈嘍大家好,我是鹹魚 在《[SELinux 入門 pt.1](https://mp.weixin.qq.com/s?__biz=MzkzNzI1MzE2Mw==&mid=2247486365&idx=1&sn=4b81b3cc70b085eec6f0a595fda719fb&chksm=c2930b ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...