1. 程式人生 > >Linux學習筆記(linux 0.11完全註釋)

Linux學習筆記(linux 0.11完全註釋)

第一章:概述

GNU計劃:旨在開發一個類似UNIX並且是自由軟體的完整作業系統。

POSIX標準:是由IEEE和ISO/IEC開發的一簇標準。該標準基於UNIX的實踐和經驗,描述了作業系統的呼叫和服務介面。用來保證編制的應用程式能在原始碼一級上在多個作業系統上移植和執行。

Linux-0.11 版本釋出時包括以下檔案:

bootimage.Z     --  具有美國鍵盤程式碼的壓縮啟動映像檔案

rootimage.Z     --  以 1200KB 壓縮的根檔案系統映像檔案

linux-0.11-tar.Z     --  核心原始碼檔案。大小為 94KB, 展開後也僅有 KB

as86.tar.Z     --  Bruce Evans' 的二進位制執行檔案。是 16 位的彙編程式和裝入程式

INSTALL-0.11    --  更新過的安裝資訊檔案

bootimage.Z 和 rootimage.Z 是 壓縮的軟盤映像檔案。 bootimage 是引導啟動 Image 檔案,主要包括磁碟引導扇區程式碼、 作業系統載入程式和核心執行程式。 PC 啟動時 ROM BIOS 中的程式會把預設啟動驅動器上的引導扇區程式碼和資料讀入記憶體, 而引導扇區程式碼負責把作業系統載入程式和核心執行程式碼讀入記憶體中,然後把作業系統的控制權交給作業系統載入程式去進 一步準備核心的初始化操作,最終載入程式會把控制權交給核心程式碼。核心若要正常執行就需要檔案系統的支援, rootimage 就是用於向核心提供最基本支援的根檔案系統。這 2 個盤合起來就相當於一張可啟動的 dos 作業系統盤。 這些檔案的下載地址如下:

第一部分:1至4章,基礎知識部分,包括微機原理和80x86保護模式程式設計。

第 二部分: 5至7章,描述核心引導啟動和32位執行方式的準備階段。

第 三部分: 8至13章,核心程式碼的主要部分。

第 四部分: 14至16章,作為第三部分原始碼閱讀的參考資訊。

第 五部分:針對linux 0.11核心的各種實驗活動。

第二章:微機組成

匯流排插槽是資料匯流排、地址匯流排、控制線與擴充套件裝置控制器的標準連線介面。匯流排介面標準通常有: ISA 、 EISA 、 PCI 、 AGP 、 PCIE 。

現代 PC 機主要使用 2 個超大規模晶片構成的晶片組或晶片集組成: southbridge 、 northbridge 。 Northbridge 用於與 CPU 、記憶體和 AGP 視訊介面,這些介面具有高傳輸速率; northbridge 還起儲存器控制作用,故 Intel 稱之為 MCH ( memory controller hub );南橋晶片用於管理中低速器件,如 PCI 匯流排介面、 IDE 硬碟介面、 USB 埠等,故南橋晶片稱之為 ICH ( I/O controller hub );

I/O 訪問

I/O 控制器包含資料埠、命令埠、狀態埠。 I/O 編址分 2 種:統一編址和獨立編址。

統一編址中訪問 I/O 跟訪問記憶體一樣,獨立編址中有單獨的 I/O 地址空間,用專門的指令進行訪問。

IBM pc 主要使用獨立編址, I/O 地址空間為 0~0x3ff; 也部分使用了統一編址,如視訊記憶體就直接佔用了記憶體空間 0xb800~0xbc00 。

在普通 Linux 系統下可以通過檢視 /proc/ioports 可以得到相關控制器或設定使用的 I/O 地址範圍。

介面資料傳輸有 3 種方式:查詢、終端、 DMA 。

主存、 BIOS 、 CMOS 儲存器

當計算機上電初始化時,實體記憶體被設為從 0 地址開始的連續區域。除了地址從 0XA0000 到 0XFFFFF(640K 到 1M) 和 0XFFFE0000 到 0xffffffff ( 4G 的最後 64K )範圍外所有的記憶體都可以用作系統記憶體。這 2 個特定範圍被特定用作 I/O 裝置和 BIOS 程式。

當上電或者復位時, CPU 會把程式指標知道 4G 記憶體空間的最後 64K 的最後 16 位元組處。 BIOS 會在這兒存放一條 JMP 指令跳轉到 BIOS 程式碼中 64KB 範圍內的某一條指令開始執行。 BIOS 在執行一系列硬體檢測和初始化後,就會把原來 pc 相容的 64KB 的程式碼和資料複製到記憶體低端 1M 末端的 64K 處,然後跳轉到這個地方進入真正的實地址模式工作,如圖 2-5 所示。最後, BIOS 就會從硬碟或者其他儲存介質把作業系統載入程式載入到記憶體 0x7c00 處,並跳轉到這個地方繼續執行載入程式。

注:由於效率原因, Linux 只利用了 BIOS 的一些最基本的資訊,並不使用 BIOS 。

在 PC/AT 機中,還使用了很小容量(只有 64 到 128 位元組)的 CMOS 來儲存計算機的實時時鐘資訊和硬體配置資訊。

軟盤和硬碟控制器

....................

第三章 核心語言和程式設計環境

Ø As86彙編器+ld86連結器 boot/bootsect.s boot/setup.s Intel彙編

Ø GNU as彙編器+GNU連結器 除as針對的2個檔案,包括c生成的彙編 AT&T彙編 預設輸出為a.out

clip_image002

clip_image004

嵌入式彙編基本格式

clip_image006

Linux 0.11目標檔案格式

clip_image008

clip_image010

第4章 80X86保護模式及其程式設計

4.1 80x86系統暫存器和系統指令

標誌暫存器:標誌暫存器EFLAGS中的系統標誌和IOPL用於控制I/O訪問、可遮蔽硬體中斷、除錯、任務切換以及虛擬8086模式。

clip_image002[6]

記憶體管理暫存器:共4個記憶體管理暫存器,GDTR、LDTR、IDTR和TR,用於指定分段記憶體管理所使用的系統表的基地址。

clip_image004[6]

GDTR:記錄GDT(全域性描述符表)的基地址和表長度。是用指令LGDT和SGDT分別用於load和store。

LDTR:記錄LDT(區域性描述符表)的基地址和表長度。是用指令LLDT和SSDT分別用於load和store。在任務切換是改變。

IDTR:記錄IDT(中斷描述符表)的基地址和表長度。是用指令LIDT和SIDT分別用於load和store。

TR:任務暫存器,在任務切換時改變。

控制暫存器:包括CR0~3

CR0中含有控制處理器操作模式和狀態的系統控制標識;CR1保留不用;CR2含有導致也錯誤的線性地址。CR3含有頁目錄表實體記憶體基地址,故也稱作頁目錄基地址暫存器PDBR.

clip_image008[6]

CR0:PE—protection enable;PG—Paging

CR3:含有存放頁目錄表頁面的實體地址,因為該地址是頁對齊的,所以僅高20位有效。

4.2 保護模式記憶體管理

任何完整的記憶體管理系統都包含2個關鍵部分:保護和地址變換。

clip_image010[7]

image

邏輯地址包含段選擇符和偏移量,通過段選擇符在段描述符表中查到段描述符(段描述符表[段選擇符])。段描述符指定:段基址、段大小、訪問許可權和特權級、段型別。

分頁機制即是把段再分為固定大小(常為4k)的頁,儲存在2個地方:1 實體記憶體,需要使用時直接使用; 2 硬碟或flash,需要使用時調入記憶體。

保護

任務之間的保護:全域性地址空間,所有的任務都能訪問,所有任務使用相同的虛擬地址訪問對映到全域性地址空間的實體地址,所有段的段描述符存於GDT眾。區域性地址空間,屬於各個任務私有的虛擬地址空間,段描述符存在該私有任務對應的LDT中。

特權級保護:定義資料段的特權級。

4.3 分段機制

image

邏輯地址轉成線性地址的步驟:

image

段描述符表:

image

image

4.4 分頁機制

分頁機制是實現虛擬儲存的基礎。

最近訪問的頁目錄和頁表會被放到處理器的緩衝器件中,該緩衝器件被稱為轉化查詢緩衝區TLB(Translation Lookaside Buffer)。

image

頁目錄表常駐記憶體,頁表可以存在Flash或硬碟上。並且任何時候,僅有一部分頁表儲存於物理記憶體中。

頁目錄表和頁表的表項格式

image

4.5 保護

保護分段級保護和頁級保護。

段級保護

段級保護依靠特權級:

image

資料訪問的保護:

image

程式訪問的保護:

1 呼叫或跳轉:RET、JMP、CALL指令的近轉移形式只是在當前程式碼段中執行程式的控制轉移,因此不會執行特權級檢查。遠轉移形式會變更程式碼段,所以會執行段轉移。

image

2 門描述符:為了對不同特權級的程式碼提供受控的訪問,處理器提供稱之為門描述符的特殊描述符集。共有4種:Call Gate,Trap Gate,Interrupte Gate,Task Gate.

image

image

頁級保護

分頁機制只識別2類許可權:特權級3為使用者級,特權級0/1/2為超級使用者級。

image

//note:CPU首先會執行段級保護,然後執行頁級保護。

4.5 中斷和異常處理

中斷源:1 外部硬體中斷,如中斷有NMI引腳接收,不可遮蔽,使用固定的中斷號2;如由INTR引腳接收,可遮蔽;2 軟體中斷(如INT)

//note:EFLAGs中的IF標誌無法遮蔽INT軟中斷。

異常源:1 程式錯誤異常;2 軟體產生的異常。異常可被細分為fault、trap、aborts。

中段描述符表(IDT):IDT存放3中型別的描述符:Interrupt Gate、Trap Gate、Task Gate.

image

image

當通過IDT中任務門來訪問中斷和異常處理過程就會導致任務切換

image

4.7 任務管理

任務是處理器可以分配排程、執行和掛起的一個工作單元。它可用於執行程式、任務或程序、作業系統服務、中斷或異常處理過程和核心程式碼。

80x86中,描述符表中與任務相關的描述符有2類:任務狀態段描述符和任務門。當執行權傳給這2種描述符時,就會任務切換。

任務不可重入,任務切換不會把任何資訊存入堆疊,而是存到任務狀態段TSS中。

任務的結構和狀態:

image

當前執行任務的狀態由處理器中以下資訊構成:

image

任務的執行:

image

TSS分動態欄位和靜態欄位。

TSS描述符:TSS描述符只能存在GDT中。

image

任務門描述符:提供對一個任務間接、受保護地的引用。任務門描述符可以存放於LDT、GDT、IDT中。下圖顯示這3個表中的任務門描述符時如何指向同一任務。

image

任務切換的4種方式:

1 當前任務對GDT中的TSS描述符執行JMP或CALL指令。

2 當前任務對GDT或LDT中的任務門描述符執行JMP或CALL指令。

3 中斷或異常向量指向IDT的任務門描述符。

4 當EFLAGS中的NT標誌置位時當前任務執行RET指令。

任務鏈:

image

第5章 Linux核心體系結構

5.1 Linux核心模式

Linux是一個單核心,其層次大概劃分如下:

image

5.2 Linux核心體系結構

Linux核心主要由5個模組組成:程序排程、記憶體管理、檔案系統、程序間通訊、網路介面。

image

image

5.3 Linux對記憶體的管理和使用

實體記憶體

image

記憶體地址空間概念:虛擬地址、邏輯地址、線性地址、實體地址。具體見前面的章節。

image

記憶體管理的缺頁載入機制:當程序引用一個不存在實體記憶體中的的記憶體地址時,就會觸發CPU產生一個缺頁異常,並把需要訪問的線性地址存放在CR2暫存器中。中斷服務程式就會根據CR2的值,把需要訪問的地址所在的頁,從二級儲存空間(如硬碟或者flash)載入到實體記憶體中。如果實體記憶體已全部被佔用,就藉助二級儲存空間上的swap區,把記憶體中暫時不用的頁面置換到二級儲存空間,再把需要訪問的頁面置換到實體記憶體。

真實模式下,段暫存器存放段基址;保護模式下,段暫存器存放段選擇符,段選擇符用來定位段描述符表中的表項。

image

而描述符表又分3中型別:GDT、LDT、IDT。

GDT是主要的基本描述符表,對應於全部地址空間。可被所有程式訪問,通過段選擇符定位到全部地址空間的一部分。

IDT的表項記錄的是中斷或異常處理程式入口的資訊,功能類似於真實模式下的中斷向量表。

LDT對應於某個具體任務,某個具體的LDT代表對應任務所能訪問到地址空間。所有LDT存放的位置,作為GDT的一個表項存放在GDT中。

image

//注:由GDT訪問到LDT,再由LDT定址到具體的地址空間。TSS段用於在任務切換時,用於儲存和恢復相關任務的執行上下文。

記憶體分頁管理:

image

CPU多工和保護方式:

image

5.3.6 虛擬地址、實體地址和線性地址之間的關係

核心程式碼和資料的地址:對於Linux0.11來說,head.s中已經把核心資料段和程式碼段都設定成了長度為16MB的段。所以Linux0.11 在預設情況下只能管理最大為16MB的空間。

image

(a)核心程式碼段和資料段線上性地址空間和實體地址空間的地址一致。(b)GDT和IDT屬於核心空間,故其線性地址和實體地址也一致。在真實模式setup.s 中,必須設定好這2個表,為進入保護模式head.s做好準備。(c)進入head.s後,設定GDTR和IDTR指向這2個表,並重新載入表項,無需更換位置。(d)除任務0外,其他所有任務所需實體記憶體頁面與線性地址不同或部分不同。

任務0的地址對應關係:

任務0是系統人啟動的第一個任務,程式碼長度和資料長度均設為640K。tss0也是手工預設好的。如圖所示:

image

任務1的地址對應關係:

任務1的程式碼也存在核心程式碼區域中。

image

任務1的使用者堆疊空間將直接共享使用處於核心程式碼和資料區域中任務0的使用者堆疊空間user_stack[]。建立任務1時,任務0和任務1共用user_stack[],當任務1開始執行時,任務1對映到user_stack[]的頁表項被設為只讀,使得任務在執行堆疊操作時會引起頁面異常,從而使核心另行分配主記憶體區域的頁面作為堆疊空間使用。

其他任務的地址對應關係

其他所有的任務(除任務0和任務1以外的任務),父程序都是init程序(任務1)。從任務2開始,如果任務號用nr來表示,那麼nr線上性地址空間的起始地址為nr*64MB。

image

在任務2被建立後,將在其中執行execve()函式來執行shell程式。execve()會釋放掉從任務1複製過來的頁目錄和頁表表項,執行shell程式會重新設定相關的頁目錄和頁表項。

注:在執行execve()系統呼叫時,系統雖然線上性地址空間為任務2分配了64MB的空間範圍,但核心並不立刻為其分配和對映實體記憶體頁面。當任務2開始執行,由於發生缺頁異常才會分配和對映實體記憶體。這種方法稱為需求載入(load on demand).同理,用malloc分配記憶體時,也沒有進行實體記憶體的對映。而free也只是把相應記憶體塊標記會空閒,並沒有釋放實體記憶體。

5.4 Linux中斷機制

image

5.5 Linux的系統呼叫

系統呼叫時Linux核心與上層應用進行互動通訊的唯一介面,其實質是通過int0x80實現。

系統呼叫的返回值為負表示錯誤,為0表示成功。錯誤時,錯誤碼被存在全域性變數errno中。呼叫庫函式perror能將其打印出來。

在Linux核心中,每個系統呼叫都具有一個唯一的一個系統呼叫功能號。這些功能號定義在include/unistd.h中。這些呼叫號實質是sys_call_table的索引值。

系統呼叫的處理過程為:_syscalln-->系統呼叫函式(此處會有int0x80)—>system_call.s,通過功能號找到核心系統呼叫實現的入口地址。-->功能實現函式

注:前2步在使用者空間,後2部在核心空間。

Linux系統呼叫最多傳遞3個引數。這3個引數被放在ebx、ecx、edx中,進入系統中斷服務程式後,這幾個暫存器會被自動儲存到堆疊。

5.6 系統時間和定時

全域性變數startup_time記錄系統開始執行的時間,jiffies記錄系統執行時間。故:

image

jiffies單位為10ms,hz為100。CURRENT_TIME的單位為s。

5.7 Linux程序控制

5.7.1 PCB(Process Control Block)

PCB儲存著用於控制和管理程序的所有資訊。主要包括程序當前執行的狀態資訊、訊號、程序號、父程序號、執行時間累計值、正在使用的檔案和本任務使用的區域性描述符以及任務狀態段資訊。

image

image

程序執行狀態:

image

5.7.3 程序初始化

在boot目錄中(含三個彙編檔案),載入程式把核心核心從磁碟載入到記憶體中,並讓系統進入保護模式後,然後啟動執行init/main.c。該程式首先分配實體記憶體,然後呼叫核心各部分的初始化函式對記憶體管理、中斷處理、塊裝置和字元裝置、程序管理以及對硬碟和軟盤硬體進行初始化操作。然後把自己手工移動到程序0中執行,並使用fork創建出程序1。然後進入程序1中進行,此後程序0只會執行pause函式,其中又會去執行任務排程函式。

5.7.4 建立新程序

使用fork建立新程序時,首先在任務陣列中尋找一個未被任何任務使用的空槽,併為新程序申請一頁的記憶體來存放PCB。然後把新程序設定為TASK_UNINTERRUPTIBLE,修改複製過來的PCB。然後設定新任務的程式碼段和資料段,但不分配實體記憶體,使用copy on write技術。新程序在執行execve後才真正執行。

5.7.5 程序排程

Linux程序時搶佔式的,被搶佔的程序仍然處於TASK_RUNNING狀態。核心態執行的程序不允許被搶佔。

任務排程程式schedule首先掃描任務陣列,比較處於TASK_RUNNING狀態的任務的counter,執行巨集swith_to切換至剩餘時間片多的任務。如果無程序執行,將執行程序0。

程序切換如下圖所示,整個過程從長跳轉至新任務的TSS指定的地址處,造成CPU的任務切換操作(儲存當前tss,載入新tss至暫存器)。

image

5.7.6 終止程序

使用exit系統呼叫,會執行核心函式do_exit().釋放記憶體,把所有的子程序的父程序設成init程序。釋放控制終端。設定狀態為TASK_ZOMBIE.並向原父程序發SIGGHLD訊號。父程序通常在呼叫wait或waitpid等待這個訊號。

5.8 linux的堆疊

Linux使用4中堆疊:boot的臨時堆疊;進入保護模式後的堆疊,也是後來任務0的使用者態堆疊;任務核心態堆疊;任務使用者態堆疊。

bootsect.s被載入到0x7c00時,不使用堆疊;bootsect.s被移至0x9000時,設定棧頂為0x9000:0xff00;setup.s也使用該堆疊段;

進入真實模式head.s,設定堆疊為user_stack的棧頂。

image

初始化main.c,在執行move_to_user_mode之後,main.c的程式碼被切換到任務0中進行。任務0繼續使用以上堆疊作為自己的使用者態堆疊。

5.8.2 任務的堆疊

每個任務有使用者態和核心態堆疊,核心態堆疊小,不能超過4k-PCB位元組數。而使用者態堆疊可以向用戶的64M空間延伸。

任務的使用者態堆疊:

image

任務的核心態堆疊:由ss0和esp0指定。與PCB在同一頁面。

image

任務態和使用者態的堆疊切換

image

5.10 Linux核心原始碼目錄結構

image

5.10.2 boot

bootsect.s編譯後駐留在磁碟第一個扇區。開機後被BIOS載入到0x7c00處。

setup.s讀取機器硬體配置引數,並把核心system模組移到適當的記憶體位置。

head.s是system模組的最前部分。

5.10.3 檔案系統目錄fs

分4個部分:高速緩衝區管理、底層檔案操作、檔案資料訪問、檔案高層函式。如圖:

image

5.10.4 include(略)

5.10.5 init

僅包含main.c。

5.10.6 kernel

image

asm.s處理系統硬體異常引起的中斷,呼叫traps.c進行中斷處理。

exit.c處理程序終止。包括程序釋放、會話終止、殺死程序、終止程序、掛起程序等。

fork.c給出了系統呼叫sys_fork使用的2個c函式:find_empty_process和copy_process.

panic.c顯示核心出錯資訊並停機。

sched.c排程相關的函式。如wake_up、sleep_on、schedule。

signal.c訊號處理相關,含do_signal()函式。

sys.c系統呼叫函式,部分還未實現。

system_call.s Linux系統呼叫的介面處理。

塊裝置驅動子目錄 kernel/blk_drv:

image

hd.c針對硬碟;floppy。c針對軟盤;ll_rw_blk.c針對其他塊裝置。

字元裝置驅動子目錄 kernel/chr_dev:

image

tty_io.c 包含tty字元裝置的讀寫函式tty_read和tty_write。還包含為中斷型別為讀字元的處理函式do_tty_interrupt。

console.c 控制檯初始化程式和寫函式con_write。以及鍵盤和顯示器中斷的初始化設定con_init。

rs_io.s 串列埠的中斷處理程式。

serial.c 序列通訊處理。

tty_ioctl.c 實現tty的io控制介面函式tty_ioctl。

keyboard。s 鍵盤中斷處理。

5.10.7 核心庫函式lib

為main.c和執行在使用者態的任務0和1提供庫支援。核心庫函式是提取了c庫函式的很小一部分,並經過優化。

5.10.8 核心管理程式目錄mm

page.s包括記憶體頁面異常中斷處理,主要是缺頁異常和防寫異常。

memory.c包括mem_init函式,和供page.s呼叫do_no_page()和do_wp_page()。

5.10.9 編譯核心工具目錄tools

該目錄下的build.c用於將編譯生成的.o連接合成可執行的image。

5.11 核心與APP的關係

Linux核心對使用者程式提供2方面的支援:(1)對任務0和1提供核心lib函式支援;(2)提供系統呼叫

系統呼叫主要是提供給系統軟體程式設計或者用於庫函式程式設計。而一般使用者開發程式則是通過呼叫像libc等庫函式來訪問核心資源。這些庫函式被稱為API。一個API可以對應於一個系統呼叫;也可以呼叫多個系統呼叫來實現某個功能。也有API不使用系統呼叫個,及不使用核心功能。

從移植性考慮,應用程式應該訪問API,而不是系統呼叫。

5.12 Makefile

image

第6章 系統載入程式

6.1 總體功能

image

image

6.2 bootsect.s程式

bootsect.s被BIOS載入到0x7c00執行,bootsect.s執行期間,把自己移到0x90000處,然後載入setup.s至0x90200,然後呼叫BIOS中斷13,顯示loading。。。;然後載入其餘system至0x10000處。

image