Linux作業系統學習筆記(二)核心執行

2021-10-05 14:12:15 字數 4714 閱讀 8970

上文中,我們分析了從按下電源鍵到bootloader完成載入的過程。載入完成之後,就要正式啟動linux核心了,而在這之前首先要完成從實模式到保護模式的切換。本文主要分析以下幾部分內容

其實整個過程中還有很多內容,比如檢查各種硬體裝置等,在此略過不提。下面就開始潛入linux原始碼的海洋暢遊啦。

在實模式下的中斷顯然不可以和保護模式的中斷同日而語,因此我們需要關閉舊的中斷(cli)並確立新的中斷(sti)。main函式能夠適應保護模式的中斷服務體系被重建完畢才會開啟中斷,而那時候響應中斷的服務程式將不再是bios提供的中斷服務程式,取而代之的是由系統自身提供的中斷服務程式。

cli、sti總是在乙個完整操作過程的兩頭出現,目的是避免中斷在此期間的介入。接下來的**將為作業系統進入保護模式做準備。此處即將進行實模式下中斷向量表和保護模式下中斷描述符表(idt)的交接工作。試想,如果沒有cli,又恰好發生中斷,如使用者不小心碰了一下鍵盤,中斷就要切進來,就不得不面對實模式的中斷機制已經廢除、保護模式的中斷機制尚未完成的尷尬局面,結果就是系統崩潰。cli、sti保證了這個過程中,idt能夠完整建立,以避免不可預料中斷的進入造成idt建立不完整或新老中斷機制混用。

#boot/setup.s

……do _move:

mov es,ax!destination segment

add ax,#0x1000

cmp ax,#0x9000

jz end_move

mov ds,ax!source segment

sub di,di

sub si,si

mov cx,#0x8000

repmovsw

jmp do_move

如上**主要完成了一項工作:將位於0x10000處的核心程式複製至記憶體位址起始位置0x00000處。在上一節我們分析了實模式中的儲存分布圖,在此位置原來存放著由bios建立的中斷向量表及bios資料區。這個複製動作將bios中斷向量表和bios資料區完全覆蓋,使它們不復存在。這樣做的好處如下:

廢除bios的中斷向量表,等同於廢除了bios提供的實模式下的中斷服務程式。

收回剛剛結束使用壽命的程式所佔記憶體空間。

讓核心**佔據記憶體實體地址最開始的、天然的、有利的位置。

此時,重要角色要登場了,他們就是中斷描述符表idt和全域性描述符表gdt。

32位的中斷機制和16位的中斷機制,在原理上有比較大的差別。最明顯的是16位的中斷機制用的是中斷向量表,中斷向量表的起始位置在0x00000處,這個位置是固定的;32位的中斷機制用的是中斷描述符表(idt),位置是不固定的,可以由作業系統的設計者根據設計要求靈活安排,由idtr來鎖定其位置。gdt是保護模式下管理段描述符的資料結構,對作業系統自身的執行以及管理、排程程序有重大意義。

此時此刻核心尚未真正執行起來,還沒有程序,所以現在建立的gdt第一項為空,第二項為核心**段描述符,第三項為核心資料段描述符,其餘項皆為空。idt雖然已經設定,實為一張空表,原因是目前已關中斷,無需呼叫中斷服務程式。此處反映的是資料「夠用即得」的思想。

建立這兩個表的過程可理解為是分兩步進行的:

在設計核心**時,已經將兩個表寫好,並且把需要的資料也寫好。此處的資料區域是在核心源**中設定、編譯並直接載入至記憶體形成的一塊資料區域。專用暫存器的指向由程式中的lidt和lgdt指令完成

將專用暫存器(idtr、gdtr)指向表。

a20啟用是乙個標誌性的動作,由上文提到的lzma_decompress.img 呼叫real_to_prot啟動。開啟a20,意味著cpu可以進行32位定址,最大定址空間為4 gb。注意圖1-19中記憶體條範圍的變化:從5個f擴充套件到8個f,即0xffffffff(4 gb)。

實模式下,當程式定址超過0xfffff時,cpu將「回滾」至記憶體位址起始處定址(注意,在只有20根位址線的條

件下,0xfffff+1=0x00000,最高位溢位)。例如,系統的段暫存器(如cs)的最大允許位址為0xffff,指令指標(ip)的最大允許段內偏移也為0xffff,兩者確定的最大絕對位址為0x10ffef,這將意味著程式中可產生的實模式下的定址範圍比1 mb多出將近64 kb(一些特殊定址要求的程式就利用了這個特點)。這樣,此處對a20位址線的啟用相當於關閉cpu在實模式下定址的「回滾」機制。如下所示為利用此特點來驗證a20位址線是否確實已經開啟。注意此處**並不在此時執行,而是在後續head執行過程中為了檢測是否處於保護模式中使用。

#boot/head.s

……xorl %eax,%eax

1:incl%eax#check that a20 really is enabled

movl %eax,0x000000#loop forever if it isn't

cmpl %eax,0x100000

je 1b

……

a20如果沒開啟,則計算機處於20位的定址模式,超過0xfffff定址必然「回滾」。乙個特例是0x100000會回滾到0x000000,也就是說,位址0x100000處儲存的值必然和位址0x000000處儲存的值完全相同。通過在記憶體0x000000位置寫入乙個資料,然後比較此處和1 mb(0x100000,注意,已超過實模式定址範圍)處資料是否一致,就可以檢驗a20位址線是否已開啟。

這裡涉及到乙個硬體知識:在x86體系中,採用的終端控制晶元名為8259a,此晶元,是可以用程式控制的中斷控制器。單個的8259a能管理8級向量優先順序中斷,在不增加其他電路的情況下,最多可以級聯成64級的向量優先順序中斷系統。cpu在保護模式下,int 0x00~int 0x1f被intel保留作為內部(不可遮蔽)中斷和異常中斷。如果不對8259a進行重新程式設計,int 0x00~int 0x1f中斷將被覆蓋。例如,irq0(時鐘中斷)為8號(int 0x08)中斷,但在保護模式下此中斷號是intel保留的「double fault」(雙重故障)。因此,必須通過8259a程式設計將原來的irq0x00~irq0x0f對應的中斷號重新分布,即在保護模式下,irq0x00~irq0x0f的中斷號是int 0x20~int 0x2f。

setup程式通過下面**將cpu工作方式設為保護模式。這裡涉及到乙個cr0暫存器:0號32位控制暫存器,放系統控制標誌。第0位為pe(protected mode enable,保護模式使能)標誌,置1時cpu工作在保護模式下,置0時為實模式。將cr0暫存器第0位(pe)置1,即設定處理器工作方式為保護模式。cpu工作方式轉變為保護模式,乙個重要的特徵就是要根據gdt決定後續執行**的程式。前文提到gdt初始時已寫好了資料,這些將用來完成從setup程式到head程式的跳轉。

#boot/setup.s

mov ax,#0x0001!protected mode(pe)bit

lmsw ax!this is it!

jmpi 0,8!jmp offset 0 of segment 8(cs)

head程式是進入main之前的最後一步了。head在空間建立了核心分頁機制,即在0x000000的位置建立了頁目錄表、頁表、緩衝區、gdt、idt,並將head程式已經執行過的**所佔記憶體空間覆蓋。這意味著head程式自己將自己廢棄,main函式即將開始執行。具體的分頁機制因為較為複雜,所以打算放在後續介紹記憶體管理的部分再單獨介紹

head構造idt,使中斷機制的整體架構先搭建起來(實際的中斷服務程式掛接則在main函式中完成),並使所有中斷服務程式指向同一段只顯示一行提示資訊就返回的服務程式。從程式設計技術上講,這種初始化操作,既可以防止無意中覆蓋**或資料而引起的邏輯混亂,也可以對開發過程中的誤操作給出及時的提示。idt有256個表項,實際只使用了幾十個,對於誤用未使用的中斷描述符,這樣的提示資訊可以提醒開發人員注意錯誤。

除此之外,head程式要廢除已有的gdt,並在核心中的新位置重新建立gdt。原來gdt所在的位置是設計**時在setup.s裡面設定的資料,將來這個setup模組所在的記憶體位置會在設計緩衝區時被覆蓋。如果不改變位置,將來gdt的內容肯定會被緩衝區覆蓋掉,從而影響系統的執行。這樣一來,將來整個記憶體中唯一安全的地方就是現在head.s所在的位置了。

下來步驟主要包括

初始化段暫存器和堆疊

清零eflag暫存器以及核心未初始化資料區

呼叫decompress_kernel()解壓核心映像並跳轉至0x00100000處。

段暫存器初始化為最終值並填充bss欄位為0

初始化臨時核心頁表

最終完成了分頁機制初始化後,pg(paging) 標誌位將會置1,表示位址對映模式採取分頁機制,最終跳轉至main函式,核心開始初始化工作。

注意,至此為止,我們尚未開啟中斷,而必須通過main函式完成一系列的初始化後才會開啟新的中斷,從而使核心正式執行起來。該部分主要包括:

為程序0建立核心態堆疊

清零eflags暫存器

呼叫setup_idt()用空的中斷處理程式填充idt

把bios中獲得的引數傳遞給第乙個頁框

用gdt和idt表填充暫存器

完成這些之後,核心就正式執行,開始建立0號程序了。

本文介紹了實模式到保護模式的整個切換過程,完成了核心的載入並開始正式準備建立0號程序。後續將繼續分析啟動核心建立0號、1號、2號程序的整個過程。本文介紹過程中忽略了很多彙編**以及一些雖然很重要但是不屬於基本流程的知識,有興趣了解的可以根據文中鏈結、文末的原始碼和參考資料進行更深入的學習研究。

[1] gurb 2

[2] syslinux

[1] linux-insides

[2] 深入理解linux核心原始碼

[3] linux核心設計的藝術

[4] 極客時間 趣談linux作業系統

作業系統學習筆記(二)

命令方式 作業系統外殼 互動性的介面 系統呼叫方式 作業系統核心 管理物件 1 硬體資源 處理器 儲存器 記憶體 外存 外圍裝置 鍵盤 滑鼠 印表機 顯示器等 2 資訊 資料和軟體 是以檔案的方式來進行管理,作業系統是通過檔案系統模組具體來對軟體 資料 實施管理。管理內容 1 資源的分配 和訪問操作...

作業系統學習筆記二

1 管理處理機 2 管理儲存器 3 管理輸入 輸出裝置 4 管理資料檔案 5 提供介面服務 二 處理機管理 程序控制 建立和撤銷程序以及控制程序的狀態轉換 進行同步 協調,互斥訪問臨界資源,協調執行進度 程序通訊 程序間的資訊交換 程序排程 按一定演算法從程序就緒佇列中選出乙個程序,把處理機分配給它...

作業系統學習(二)

程式的可併發執行 程序的定義與特徵 程序的狀態 程序的掛起狀態 程序控制塊 程序的組織形式 程序控制 程序的建立 程序終止 程序的終止過程 引起程序阻塞和喚醒的事件 中斷型別分為如下兩大類 按中斷功能分類 引入,前驅圖 定義 前驅圖是乙個又向無迴圈圖,可用來描述程式段或程序之間的先後次序關係。前趨圖...