計算機系統
大作業
題 ????目 ?程序人生-hello’s P2P?? ???
專?????? 業 ?計算機與電子通信類???? ????
學 ?? 號 ?2023111990??????????? ??????
班 ?? 級 ?23L0514????????????? ????????
學?????? 生 ?袁騁??????????? ??????????????????
指 導 教 師 ?史先俊?????????? ??????????????????
計算機科學與技術學院
2025年5月
本文深入剖析了 hello 程序從源代碼編寫到運行結束的全過程,涵蓋預處理、編譯、匯編、鏈接、加載、運行、存儲管理、回收等多個階段。通過詳細分析每個階段的關鍵操作和機制,揭示了計算機系統底層的工作原理,進一步加深了對計算機系統設計與實現的理解,以及各組件之間的協同工作方式。這一過程不僅幫助我們更好地理解了程序的生命周期,也為后續的學習和研究提供了重要的參考。
關鍵詞:計算機系統;CSAPP;編譯;存儲;操作系統;OS???????????????????????????
(摘要0分,缺失-1分,根據內容精彩稱都酌情加分0-1分)
目? 錄
第1章 概述................................................................................................................ - 4 -
1.1 hello簡介......................................................................................................... - 4 -
1.2 環境與工具........................................................................................................ - 4 -
1.3 中間結果............................................................................................................ - 5 -
1.4 本章小結............................................................................................................ - 5 -
第2章 預處理............................................................................................................ - 6 -
2.1 預處理的概念與作用........................................................................................ - 6 -
2.2在Ubuntu下預處理的命令............................................................................. - 6 -
2.3 hello的預處理結果解析................................................................................. - 6 -
2.4 本章小結............................................................................................................ - 7 -
第3章 編譯................................................................................................................ - 8 -
3.1 編譯的概念與作用............................................................................................ - 8 -
3.2 在Ubuntu下編譯的命令................................................................................ - 8 -
3.3 hello的編譯結果解析..................................................................................... - 8 -
3.4 本章小結.......................................................................................................... - 12 -
第4章 匯編.............................................................................................................. - 13 -
4.1 匯編的概念與作用.......................................................................................... - 13 -
4.2 在Ubuntu下匯編的命令.............................................................................. - 13 -
4.3 可重定位目標elf格式.................................................................................. - 13 -
4.4 hello.o的結果解析........................................................................................ - 15 -
4.5 本章小結.......................................................................................................... - 17 -
第5章 鏈接.............................................................................................................. - 18 -
5.1 鏈接的概念與作用.......................................................................................... - 18 -
5.2 在Ubuntu下鏈接的命令.............................................................................. - 18 -
5.3 可執行目標文件hello的格式...................................................................... - 19 -
5.4 hello的虛擬地址空間................................................................................... - 21 -
5.5 鏈接的重定位過程分析.................................................................................. - 21 -
5.6 hello的執行流程........................................................................................... - 22 -
5.7 hello的動態鏈接分析................................................................................... - 22 -
5.8 本章小結.......................................................................................................... - 23 -
第6章 hello進程管理....................................................................................... - 24 -
6.1 進程的概念與作用.......................................................................................... - 24 -
6.2 簡述殼Shell-bash的作用與處理流程........................................................ - 24 -
6.3 hello的fork進程創建過程......................................................................... - 24 -
6.4 hello的execve過程..................................................................................... - 25 -
6.5 hello的進程執行........................................................................................... - 25 -
6.6 hello的異常與信號處理............................................................................... - 25 -
6.7本章小結.......................................................................................................... - 29 -
第7章 hello的存儲管理................................................................................... - 30 -
7.1 hello的存儲器地址空間............................................................................... - 30 -
7.2 Intel邏輯地址到線性地址的變換-段式管理............................................... - 30 -
7.3 hello的線性地址到物理地址的變換-頁式管理.......................................... - 30 -
7.4 TLB與四級頁表支持下的VA到PA的變換................................................ - 30 -
7.5 三級Cache支持下的物理內存訪問............................................................. - 31 -
7.6 hello進程fork時的內存映射..................................................................... - 31 -
7.7 hello進程execve時的內存映射................................................................. - 31 -
7.8 缺頁故障與缺頁中斷處理.............................................................................. - 32 -
7.9動態存儲分配管理.......................................................................................... - 32 -
7.10本章小結........................................................................................................ - 32 -
第8章 hello的IO管理.................................................................................... - 33 -
8.1 Linux的IO設備管理方法............................................................................. - 33 -
8.2 簡述Unix IO接口及其函數.......................................................................... - 33 -
8.3 printf的實現分析........................................................................................... - 33 -
8.4 getchar的實現分析....................................................................................... - 33 -
8.5本章小結.......................................................................................................... - 33 -
結論............................................................................................................................ - 34 -
附件............................................................................................................................ - 36 -
參考文獻.................................................................................................................... - 37 -
第1章 概述
1.1 hello簡介
根據hello的自白,利用計算機系統的術語,簡述hello的P2P,020的整個過程。
P2P:
預處理??:hello.c通過預處理器展開頭文件、宏定義,生成hello.i。
編譯??:將hello.i轉換為匯編代碼hello.s,完成語法分析和指令生成。
匯編??:hello.s被匯編器翻譯為可重定位目標文件hello.o,包含機器指令和符號表。
鏈接??:鏈接器合并hello.o與庫文件(如libc.so),解析符號引用,生成可執行文件hello。
運行??:Shell通過fork創建子進程,execve加載hello到內存,CPU執行指令,最終進程終止。
O2O:
從0到1??:程序從無到有的構建(編輯、編譯、鏈接)。
從1到0??:進程運行結束后釋放資源,回歸系統初始狀態。
1.2 環境與工具
我使用學校提供的Dell服務器完成本實驗,環境如下:
CPU:AMD EPYC 7763 64-Core Processor,16核心,16線程
內存:32GB DDR4
磁盤:200GB SATA HDD(系統盤)
操作系統:Ubuntu 22.04 LTS(Kernel 6.8.0-54-generic)
使用的軟件工具:
vscode(ssh連接服務器)
gcc
gdb
readelf
objdump
1.3 中間結果
從最初的hello.c源文件開始,逐步得到了以下結果文件:
hello.i
預處理后的文件,通過GCC的-E選項生成。該文件包含了展開的頭文件、宏定義以及刪除的注釋,為編譯階段提供了干凈的輸入。
hello.s
編譯階段生成的匯編代碼文件,通過GCC的-S選項生成。該文件包含了程序的匯編指令,反映了C代碼到匯編指令的轉換過程。
hello.o
匯編階段生成的目標文件,通過GCC的-c選項生成。該文件包含了機器指令和符號表,是鏈接階段的輸入。
hello
鏈接階段生成的可執行文件,ld生成。
hello.dis.txt
反匯編輸出文件,通過objdump工具生成。該文件包含了hello文件的反匯編代碼,用于分析匯編指令和機器碼之間的關系。
hello.elf.txt
ELF文件格式分析輸出,通過readelf工具生成。該文件包含了可執行文件hello的ELF格式信息,用于分析程序的加載和鏈接過程。
hello.o.dis.txt
目標文件hello.o的反匯編輸出文件,通過objdump工具生成。該文件用于分析目標文件中的匯編指令和符號信息。
1.4 本章小結
本章概述了hello的P2P/O2O流程,明確了實驗環境和中間產物,為后續章節的詳細分析奠定基礎。
(第1章0.5分)
第2章 預處理
2.1 預處理的概念與作用
預處理是C程序編譯的第一階段,由預處理器(cpp)執行,主要功能包括:
??????? 宏展開??:替換#define定義的宏和常量。
??????? 頭文件包含??:將#include指定的頭文件內容插入到源文件中。
??????? 條件編譯??:根據#if、#ifdef等指令選擇性地包含或排除代碼。
??????? 刪除注釋??:移除所有/* */和//注釋,減少編譯負擔。
??????? 添加行號和文件名標記??:便于調試時定位錯誤。
預處理后的文件(.i)仍然是C代碼,但已去除預處理指令,為后續編譯階段提供干凈的輸入。
2.2在Ubuntu下預處理的命令
gcc -E hello.c -o hello.i
圖 1 預處理命令
2.3 hello的預處理結果解析
stdio.h, unistd.h, stdlib.h的內容被完整插入到hello.i中,導致代碼行數大量增加;源代碼中的注釋完全消失,僅保留有效代碼;添加了原始文件和行號信息標記。
圖 2 預處理后的hello.i節選
2.4 本章小結
本章詳細分析了C語言程序的預處理階段。通過實際操作,我們使用gcc的-E選項生成了hello.i預處理文件,并解析了預處理過程中的關鍵操作,包括頭文件包含、注釋刪除和行號標記等。預處理階段為后續的編譯階段準備了純凈的C語言代碼,是程序編譯過程中不可或缺的第一步。
(第2章0.5分)
第3章 編譯
3.1 編譯的概念與作用
編譯是將預處理后的高級語言代碼(.i文件)轉換為匯編語言代碼(.s文件)的過程。其主要作用包括:
??????? 詞法分析??:將源代碼分解為有意義的詞法單元(tokens)
??????? 語法分析??:檢查語法結構,生成抽象語法樹(AST)
??????? 語義分析??:檢查類型匹配、變量聲明等語義規則
??????? 中間代碼生成??:生成與機器無關的中間表示
??????? 代碼優化??:對中間代碼進行優化
??????? 目標代碼生成??:生成特定平臺的匯編代碼
編譯階段是程序從高級語言向機器語言轉換的關鍵環節,決定了程序的底層實現方式。
3.2 在Ubuntu下編譯的命令
gcc -S hello.i -o hello.s -m64 -no-pie -fno-PIC
圖 3 編譯命令
3.3 hello的編譯結果解析
3.3.1 常量處理
??(1)字符串常量??
存儲在.rodata只讀數據段,比如UTF-8編碼的中文提示字符串
圖 4 字符串常量存儲
??(2)整型常量??
直接編碼為指令立即數,比如if(argc!=5)中的5
圖 5 整形常量存儲
3.3.2 變量處理
??(1)局部變量??
分配棧空間存儲。示例:循環變量i(初始值為0)
圖 6 局部變量存儲
??(2)參數變量??
存儲在寄存器中,以argc為例
圖 7 參數變量存儲
3.3.3 算術操作
直接生成對應的匯編指令,比如循環變量i的遞增
圖 8 算術操作的處理
??3.3.4數組操作
基址+偏移量,以數組argv[]訪問為例
圖 9 數組的處理
3.3.5 類型轉換
??(1)顯式轉換
顯式調用轉換函數。
圖 10 顯式類型轉換
?????? (2)隱式轉換
?????? 使用不同長度的寄存器和指令。
圖 11 隱式類型轉換
3.3.6 宏處理
在預處理階段完成。
3.3.7 函數調用
利用寄存器進行參數與返回值的傳遞,函數的返回值通常存儲在%eax寄存器中。
圖 12 返回值的存儲
通過ret指令返回。
??
圖 13 函數調用
3.3.8 控制轉移
??(1)條件分支
使用cmp指令比較操作數,然后根據比較結果使用je(跳轉相等)、jle(跳轉小于等于)等指令進行條件跳轉。if(argc!=5)對應的匯編指令為:
圖 14 條件分支
(2)循環
使用jmp指令實現循環跳轉。對于本程序中的循環體,.L3是循環的開始標簽,.L4是循環體的標簽。每次循環結束后與常數9進行比較,決定是否通過jle指令跳轉回.L4繼續執行。
圖 15 循環
3.3.9 關系操作
cmpl指令比較兩個操作數,并根據比較結果設置標志位,然后其他指令根據標志位完成某個功能。本程序中有兩處比較。
圖 16 比較1(相等跳轉)
圖 17 比較2(小于等于跳轉)
3.4 本章小結
本章詳細分析了hello程序在編譯階段的數據、函數調用、控制轉移和關系操作等的處理,明確了C代碼與匯編代碼的對應關系。編譯器將C語言中的高級邏輯轉換為具體的匯編指令,從而實現程序的功能。
(第3章2分)
第4章 匯編
4.1 匯編的概念與作用
匯編是將匯編語言代碼(.s文件)轉換為機器語言代碼(.o文件)的過程。匯編器的主要作用包括:
語法解析:將匯編語言中的指令和偽指令解析為機器語言指令。
符號處理:為代碼中的符號(如標簽、變量名等)生成符號表,記錄它們的地址和屬性。
指令編碼:將匯編指令轉換為對應的機器碼。
生成目標文件:生成可重定位目標文件(.o文件),該文件包含機器碼、符號表和重定位信息,用于后續的鏈接過程。
匯編是程序從高級語言向機器語言轉換的重要中間步驟,它將人類可讀的匯編代碼轉換為計算機可執行的機器碼。
4.2 在Ubuntu下匯編的命令
gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o
圖 18 匯編命令
4.3 可重定位目標elf格式
(1)ELF文件頭信息
圖 19 ELF文件頭
Class: ELF64,表示這是一個64位的ELF文件。
Data: 2's complement, little endian,表示數據以小端格式存儲。
Type: REL (Relocatable file),表示這是一個可重定位的目標文件,用于鏈接。
Machine: Advanced Micro Devices X86-64,表示目標架構是x86-64。
Entry point address: 0x0,可重定位目標文件的入口地址通常為0,因為實際入口地址在鏈接時確定。
Section headers: 包含14個節頭,節頭表的偏移為1080字節。
(2)重定位表信息
重定位表記錄了需要在鏈接時進行地址調整的符號信息。
圖 20 重定位表
hello.o包含兩個重定位表:
①.rela.text
包含8個重定位條目,主要針對.text節中的符號引用。
例如:
R_X86_64_32:表示32位直接地址重定位。
R_X86_64_PLT32:表示32位PLT(過程鏈接表)相對地址重定位。
具體條目:
puts、exit、printf、atoi、sleep、getchar等函數的調用需要在鏈接時解析其地址。
.rodata節中的字符串常量地址也需要重定位。
②.rela.eh_frame
包含1個重定位條目,針對異常處理框架(EH Frame)。
類型為R_X86_64_PC32,表示32位PC相對地址重定位。
4.4 hello.o的結果解析
將hello.o用objdump -d -r hello.o > hello.o.txt反匯編,輸出結果存儲在txt文件中。
圖 21 hello.o的反匯編輸出文件hello.o.txt
(1)hello.o反匯編文件與hello.s直觀對比
1.hello.o反匯編中不存在像hello.s中的.L2等分段,jmp時全部用地址來表示
圖 22 hello.s中跳入循環體的語句
圖 23 hello.o反匯編中跳入循環體的語句
2.相比hello.s,hello.o反匯編每行匯編代碼前有地址與機器代碼的十六進制表示。也如圖22,圖23所示。
(2)機器語言構成
機器語言由操作碼和操作數組成。操作碼決定了指令的操作類型,如算術運算、邏輯運算、數據傳輸等;操作數則指定了指令操作的對象,如寄存器、內存地址或立即數。
(3)機器語言與匯編語言的映射關系
1.指令:匯編指令直接對應機器碼。例如,push %rbp對應的機器碼是55,mov %rsp。這些指令在hello.s中以匯編語言的形式出現,而在hello.o的反匯編中則以機器碼的形式展示,但它們的邏輯和功能是一致的。
2.操作數:hello.s中的操作數是十進制表示的,而hello.o反匯編中是十六進制。
3.分支轉移和函數調用:在hello.s中,分支轉移用段來實現,而在反匯編中則使用相對地址或絕對地址。對于函數調用,hello.s中使用call 函數名稱,而在反匯編中使用call 地址。
4.5 本章小結
本章深入探討了匯編階段的關鍵過程及其輸出結果。通過分析 hello.o 的反匯編代碼,清晰地展示了匯編語言與機器語言之間的映射關系,揭示了指令、操作數以及分支轉移和函數調用在兩種語言中的具體表現形式。同時,對比 hello.s 和 hello.o 的內容,進一步闡釋了匯編階段如何將人類可讀的匯編代碼轉換為計算機可執行的機器碼,并生成包含機器碼、符號表和重定位信息的可重定位目標文件,為后續的鏈接過程奠定基礎。
(第4章1分)
第5章 鏈接
5.1 鏈接的概念與作用
鏈接是將多個目標文件和庫文件合并成一個可執行文件的過程。鏈接器(ld)的主要作用包括:
符號解析:解析目標文件和庫文件中的符號引用,確保所有符號都能正確解析到其定義的位置。
地址分配:為每個目標文件分配內存地址,確保它們在可執行文件中的位置正確。
重定位:根據符號的最終地址,調整目標文件中的指令和數據,使其能夠在運行時正確訪問。
生成可執行文件:將所有目標文件和庫文件的內容合并,生成一個可執行文件,該文件包含程序的機器碼、數據段、符號表等信息。
鏈接是程序從可重定位目標文件到可執行文件的關鍵步驟,它使得多個模塊能夠組合成一個完整的程序。
5.2 在Ubuntu下鏈接的命令
ld -o hello \
??? /usr/lib/x86_64-linux-gnu/crt1.o \
??? /usr/lib/x86_64-linux-gnu/crti.o \
??? hello.o \
??? /usr/lib/x86_64-linux-gnu/libc.so \
??? /usr/lib/x86_64-linux-gnu/crtn.o \
??? -dynamic-linker /lib64/ld-linux-x86-64.so.2
圖 24 鏈接命令
5.3 可執行目標文件hello的格式
?? ELF Header:顯示了 ELF 文件的基本信息。
圖 25 ELF Header
Section Headers:列出了所有的節(section),包括它們的名稱、類型、地址、大小等。
圖 26 Section Headers
5.4 hello的虛擬地址空間
圖 27 gdb查看的虛擬地址空間段信息
5.5 鏈接的重定位過程分析
hello的反匯編比hello.o的反匯編內容多得多,新增了庫函數和節。并且hello反匯編的跳轉使用虛擬內存地址,而hello.o反匯編使用偏移地址。
圖 28 hello反匯編一窺
圖 29 hello.o反匯編回顧
重定位條目:在 hello.elf.txt 中的 .rela.dyn 和 .rela.plt 節包含了重定位條目,這些條目指示鏈接器在運行時或加載時需要調整的地址。
圖 30 重定位條目
地址調整:在 hello.dis.txt 中,一些函數調用(如 puts、printf 等)被重定位到它們在動態鏈接庫中的實際地址。
PLT和GOT:程序鏈接表(PLT)和全局偏移表(GOT)用于動態鏈接。在 hello.dis.txt 中,我們可以看到 PLT 條目(如 puts@plt)和 GOT 條目(如 _GLOBAL_OFFSET_TABLE_)。
5.6 hello的執行流程
首先,程序加載后,控制流首先轉移到 _start 符號處,這是程序的入口點。在 _start 處,程序會進行一些初始化工作,例如設置棧,然后調用動態鏈接器。接下來,控制流轉移到動態鏈接器的 __libc_start_main 函數,該函數負責調用 main 函數并傳遞命令行參數。在 main 函數中,程序執行其主要邏輯,調用printf\atoi等函數。當 main 函數執行完畢并返回時,控制流回到 __libc_start_main,它隨后調用 exit 函數來清理資源并終止程序。exit 函數會進行必要的清理工作,比如調用全局對象的析構函數,然后程序最終終止。
子程序:_start, main, printf等
5.7 hello的動態鏈接分析
在分析hello程序的動態鏈接時,我們首先注意到該程序依賴于幾個動態鏈接庫,包括linux-vdso.so.1、libc.so.6以及ld-linux-x86-64.so.2。這些庫在程序啟動時由動態鏈接器ld-linux-x86-64.so.2加載進內存。libc.so.6提供了程序中使用的C標準庫函數,如printf和atoi。當程序開始執行,特別是當main函數被調用時,這些函數的地址已經被解析,并且程序能夠正常調用它們。
通過使用gdb(GDB)調試器,我們能夠觀察到程序在動態鏈接前后的變化。在程序啟動時,動態鏈接器負責加載所有必需的動態庫,并將程序中的符號引用解析到這些庫中的具體實現。隨著程序的執行,比如在main函數中調用printf和atoi函數時,這些函數的調用已經被正確地鏈接到它們在動態庫中的地址上,使得程序可以正常運行。當程序執行到exit函數并準備終止時,動態鏈接器會負責清理程序使用過的動態庫資源。
5.8 本章小結
第五章總結了hello程序的鏈接過程。通過分析ELF格式、虛擬地址空間和動態鏈接,展示了程序從目標文件到可執行文件的轉換,以及程序運行時的內存布局和行為。
(第5章1分)
第6章 hello進程管理
6.1 進程的概念與作用
進程是程序在計算機上的一次動態執行過程,是操作系統進行資源分配和調度的基本單位。它包含了程序運行所需的各種資源,如代碼段、數據段、堆棧等,以及程序的執行狀態信息,例如程序計數器(PC)的值、寄存器的值等。進程的作用在于使得程序能夠被操作系統有效地管理和調度,從而實現多任務的并發執行。通過創建多個進程,用戶可以同時運行多個程序,提高了計算機資源的利用率和系統的整體性能。每個進程在運行過程中都相對獨立,擁有自己的地址空間和資源,這使得它們之間不會相互干擾,保證了程序運行的穩定性和安全性。
6.2 簡述殼Shell-bash的作用與處理流程
Shell-bash作為用戶與操作系統之間的交互界面,承擔著命令解析、程序執行以及進程控制等多種重要功能。當用戶在終端輸入命令時,bash首先會解析這些命令,判斷其是否為內置命令。如果是內置命令,bash會直接在當前進程中執行;若為外部命令,bash會通過fork創建一個子進程。子進程隨后會調用execve函數,將外部命令對應的程序加載到內存中并開始執行。在程序執行過程中,bash會持續監控子進程的狀態,當子進程執行完畢后,bash會回收該子進程,清理相關資源,然后等待用戶輸入下一個命令,繼續上述處理流程。此外,bash還支持作業控制功能,允許用戶在后臺運行程序、暫停和恢復程序的執行等。
6.3 hello的fork進程創建過程
當用戶在bash終端中輸入hello程序的執行命令后,bash會通過調用fork系統調用創建一個新的子進程。在fork過程中,操作系統會為子進程分配一個唯一的進程標識符(PID),并復制父進程的大部分資源,包括代碼段、數據段、堆棧等,但實際的物理內存頁面并不會立即復制,而是采用寫時復制(Copy-On-Write,COW)機制。這意味著父子進程在創建初期共享相同的物理內存頁面,只有當某個進程試圖修改頁面內容時,操作系統才會為該進程創建一個新的內存頁面副本,從而避免了不必要的內存浪費。同時,子進程會繼承父進程的文件描述符、環境變量等資源,但子進程的程序計數器(PC)會被設置為fork系統調用的返回點,以便子進程能夠繼續執行后續的指令。在hello程序的場景中,子進程創建完成后,會繼續執行execve系統調用,加載hello程序到內存中并開始執行。
6.4 hello的execve過程
在hello程序的子進程通過fork創建完成后,緊接著會調用execve系統調用。execve的主要作用是將hello程序的可執行文件加載到子進程的內存空間中,并替換掉子進程原有的代碼段、數據段、堆棧等資源,從而使得子進程能夠開始執行hello程序。當execve被調用時,操作系統會首先檢查可執行文件的合法性,包括文件格式是否正確、是否有執行權限等。如果檢查通過,操作系統會根據可執行文件中的信息,將程序的代碼段、數據段等加載到子進程的內存中,并初始化堆棧,設置程序的入口點等。隨后,操作系統會將子進程的程序計數器(PC)設置為程序的入口地址,子進程便開始執行hello程序。在hello程序執行過程中,操作系統會按照進程調度算法為hello進程分配CPU時間片,使得hello程序能夠在多任務環境中與其他進程并發運行。
6.5 hello的進程執行
hello程序的執行過程是一個典型的用戶態與內核態不斷切換的過程。在程序運行期間,操作系統會按照一定的調度算法為hello進程分配CPU時間片。當hello進程獲得CPU時間片后,它會在用戶態開始執行程序代碼。在用戶態下,hello進程主要負責執行程序的邏輯運算、數據處理等操作。然而,在程序執行過程中,hello進程可能會遇到一些需要操作系統介入的情況,例如需要進行文件讀寫操作、請求更多的內存資源等。此時,hello進程會通過系統調用陷入內核態。在內核態下,操作系統會處理hello進程的請求,例如為進程分配內存、執行文件讀寫操作等。當操作系統完成請求處理后,hello進程會從內核態返回到用戶態,繼續執行程序代碼。此外,在hello進程的執行過程中,還可能會因為時間片用完、更高優先級的進程搶占CPU等原因而暫停執行,等待下一次被調度。當hello程序執行完畢后,操作系統會回收hello進程占用的資源,包括內存、文件描述符等,完成hello進程的生命周期。
6.6 hello的異常與信號處理
運行中可能產生中斷、陷阱、故障、中止。
可能產生SIGINT, SIGKILL, SIGTSTP等信號。具體處理見下。
(1)正常運行./hello 2023111990 袁騁 18567806985 3
正常每隔3秒重復10次內容,然后等待一個字符輸入后程序終止。
圖 31 hello正常運行
(2)亂按鍵盤(有回車)
可以發現程序運行中亂按鍵盤并不會造成程序停止,但是在程序結束后,第二個回車前打的字符被輸入到了Shell中,這說明鍵盤的輸入被暫存在緩沖區。(第一個回車前的字符都給hello里的getchar()了,不然這個程序也不會中止)
圖 32 hello運行中亂按鍵盤
(3)Ctrl+Z(SIGTSTP)
進程被掛起,通過ps可以看到進程Stopped,通過fg恢復運行。
圖 33 hello運行中按Ctrl+Z
圖 34 hello掛起后輸入ps命令
圖 35 hello掛起后輸入jobs命令
圖 36 hello掛起后輸入pstree命令(截圖不全)
圖 37 hello掛起后輸入fg命令
(4)kill命令
kill -9 3014359向hello進程發送SIGKILL,所以進程被殺死。
圖 38 hello掛起后輸入kill命令
(5)Ctrl+C(SIGINT)
按下Ctrl+C,hello收到SIGINT信號,默認是終止程序,因此hello被終止。使用ps驗證,看不到hello進程存在。
圖 39 hello運行中按Ctrl+C
6.7本章小結
本章深入探討hello程序的進程管理機制,包括進程的創建、執行、調度以及異常與信號處理。通過分析系統調用,揭示了進程從啟動到終止的全過程,展現了操作系統對進程的精細管控與高效調度。
(第6章2分)
第7章 hello的存儲管理
7.1 hello的存儲器地址空間
邏輯地址是程序中直接使用的地址,由段選擇符和段內偏移組成。
線性地址是邏輯地址經過段式管理轉換后得到的地址,它在現代操作系統中通常與虛擬地址相同。
虛擬地址是線性地址經過頁式管理轉換后得到的地址,它允許程序訪問比物理內存更大的地址空間。
物理地址則是虛擬地址最終映射到的內存地址,由硬件管理單元(MMU)負責完成從虛擬地址到物理地址的轉換。
hello程序在運行過程中,操作系統通過這些地址空間的轉換機制,確保程序能夠高效地訪問內存資源,同時提供內存保護和隔離功能。
7.2 Intel邏輯地址到線性地址的變換-段式管理
段式管理將內存劃分為多個段,每個段由一個段描述符定義,包含段的起始地址、長度和訪問權限等信息。段描述符存儲在全局描述符表(GDT)或局部描述符表(LDT)中。當hello程序運行時,處理器根據段選擇符從GDT或LDT中獲取段描述符,將邏輯地址中的段內偏移與段描述符中的段基址相加,得到線性地址。
7.3 hello的線性地址到物理地址的變換-頁式管理
頁式管理將線性地址空間劃分為固定大小的頁面,每個頁面映射到物理內存中的一個頁面幀。頁表是實現這種映射的關鍵數據結構,它記錄了頁面到頁面幀的映射關系。hello程序運行時,操作系統負責維護頁表,并通過硬件的內存管理單元(MMU)完成從線性地址到物理地址的轉換。當訪問一個線性地址時,MMU會查找頁表,找到對應的頁面幀,從而得到物理地址。如果頁表中沒有找到對應的映射,就會觸發缺頁中斷,操作系統會處理缺頁中斷,將所需的頁面加載到物理內存中,并更新頁表。
7.4 TLB與四級頁表支持下的VA到PA的變換
TLB是一個高速緩存,用于存儲最近訪問的頁表項,從而減少對頁表的訪問次數。在多級頁表結構中,TLB可以顯著提高地址轉換的速度。hello程序運行時,處理器首先在TLB中查找線性地址到物理地址的映射。如果TLB中沒有命中,才會訪問頁表。
在現代操作系統中,通常使用四級頁表結構,這種結構可以支持更大的地址空間,同時減少頁表的大小。
hello程序的虛擬地址通過四級頁表的逐級查找,最終映射到物理地址。TLB的存在使得這個過程更加高效,減少了地址轉換的延遲。
7.5 三級Cache支持下的物理內存訪問
現代處理器通過三級緩存(L1、L2、L3)來提高內存訪問速度。L1緩存是最快的,但容量較小,通常集成在處理器核心內部。L2和L3緩存的容量更大,速度相對較慢,但仍然比主內存快得多。當hello程序訪問物理內存時,處理器首先在L1緩存中查找所需的數據。如果L1緩存中沒有命中,就會依次訪問L2和L3緩存。如果三級緩存中都沒有找到所需的數據,才會訪問主內存。這種緩存層次結構可以顯著減少內存訪問的延遲,提高程序的運行效率。
7.6 hello進程fork時的內存映射
當hello程序通過fork創建子進程時,操作系統會為子進程創建一個新的地址空間。這個地址空間是父進程地址空間的副本,但實際的物理內存頁面并不會立即復制,而是采用寫時復制機制。
這意味著父子進程在創建初期共享相同的物理內存頁面,只有當某個進程試圖修改頁面內容時,操作系統才會為該進程創建一個新的內存頁面副本。這種機制可以顯著減少內存的使用量,提高進程創建的效率。在hello程序的場景中,fork操作后,子進程會繼承父進程的代碼段、數據段、堆棧等資源,但每個進程都有自己的獨立地址空間。
7.7 hello進程execve時的內存映射
當hello程序的子進程調用execve加載新的可執行文件時,操作系統會替換掉子進程的代碼段、數據段和堆棧等資源。這個過程涉及多個步驟:首先,操作系統會釋放子進程當前的內存映射;然后,根據新的可執行文件的ELF格式信息,重新分配內存,并將代碼段、數據段等加載到新的內存區域;最后,初始化堆棧,并設置程序的入口點。
7.8 缺頁故障與缺頁中斷處理
缺頁故障:訪問的頁面不在物理內存中,需要從磁盤或其他存儲設備中加載。
當發生缺頁故障時,處理器會觸發缺頁中斷,操作系統會處理這個中斷,將所需的頁面加載到物理內存中,并更新頁表。缺頁中斷的處理過程包括查找頁面的存儲位置、分配物理內存頁面、將頁面內容從磁盤加載到物理內存、更新頁表以及恢復程序的執行。hello程序在運行過程中,如果訪問了未加載到內存中的頁面,就會觸發缺頁中斷,操作系統會通過上述步驟處理缺頁故障,確保程序能夠繼續運行。
7.9動態存儲分配管理
動態內存分配允許程序在運行時根據需要分配和釋放內存。
操作系統通過堆管理器來實現動態內存分配,堆管理器負責維護一個堆區,程序可以從堆區中分配內存塊。當程序調用malloc時,堆管理器會從堆區中分配一個合適大小的內存塊,并返回其地址。當程序不再需要這塊內存時,可以通過free函數將其釋放回堆區。
Printf會調用malloc,請簡述動態內存管理的基本方法與策略。(此節課堂沒有講授,選做,不算分)
7.10本章小結
本章詳細探討了hello程序的存儲管理機制的多個方面。通過這些機制,操作系統為hello程序提供了高效的存儲管理功能,確保了hello的順利運行和資源的有效利用。
(第7章 2分)
第8章 hello的IO管理
8.1 Linux的IO設備管理方法
(以下格式自行編排,編輯時刪除)
設備的模型化:文件
設備管理:unix io接口
8.2 簡述Unix IO接口及其函數
(以下格式自行編排,編輯時刪除)
8.3 printf的實現分析
(以下格式自行編排,編輯時刪除)
[轉]printf 函數實現的深入剖析 - Pianistx - 博客園
從vsprintf生成顯示信息,到write系統函數,到陷阱-系統調用 int 0x80或syscall等.
字符顯示驅動子程序:從ASCII到字模庫到顯示vram(存儲每一個點的RGB顏色信息)。
顯示芯片按照刷新頻率逐行讀取vram,并通過信號線向液晶顯示器傳輸每一個點(RGB分量)。
8.4 getchar的實現分析
(以下格式自行編排,編輯時刪除)
異步異常-鍵盤中斷的處理:鍵盤中斷處理子程序。接受按鍵掃描碼轉成ascii碼,保存到系統的鍵盤緩沖區。
getchar等調用read系統函數,通過系統調用讀取按鍵ascii碼,直到接受到回車鍵才返回。
8.5本章小結
(以下格式自行編排,編輯時刪除)
(第8章 選做 0分)
結論
通過以上各個階段,hello程序從源代碼到可執行文件,再到運行中的進程,最后被回收,經歷了一系列復雜的轉換和管理過程。在此總結hello一生所經歷的過程。
1.編輯與預處理階段
hello的旅程始于源代碼的編寫,程序員將代碼保存為hello.c文件。預處理器對源代碼進行處理,展開頭文件、宏定義,刪除注釋,并添加行號和文件名標記,生成預處理文件hello.i。這一階段為后續編譯工作準備了純凈的C語言代碼。
2.編譯階段
編譯器將預處理后的hello.i文件轉換為匯編語言代碼hello.s。編譯過程包括詞法分析、語法分析、語義分析、中間代碼生成、代碼優化和目標代碼生成。編譯器將高級語言邏輯轉換為具體的匯編指令,為后續的匯編和鏈接工作奠定了基礎。
3.匯編階段
匯編器將hello.s文件中的匯編代碼翻譯為機器語言代碼,生成可重定位目標文件hello.o。匯編器解析匯編指令,生成機器碼,并為代碼中的符號生成符號表和重定位信息。這一階段將人類可讀的匯編代碼轉換為計算機可執行的機器碼。
4.鏈接階段
鏈接器將hello.o與標準庫文件(如libc.so)合并,解析符號引用,分配地址,完成重定位,最終生成可執行文件hello。鏈接過程是程序從目標文件到可執行文件的關鍵轉換,使得多個模塊能夠組合成一個完整的程序。
5.加載與運行階段
當用戶在Shell中運行hello時,操作系統通過fork創建子進程,子進程調用execve將hello加載到內存中。程序加載后,控制流轉移到入口點_start,經過初始化后調用main函數。程序在運行過程中,操作系統管理其內存、調度CPU時間片,并處理異常和信號。
6.存儲管理與內存映射
hello程序運行時,操作系統通過段式和頁式管理機制管理其地址空間,從邏輯地址到線性地址,再到虛擬地址和物理地址的轉換,確保程序高效訪問內存。同時,TLB和多級頁表加速了地址轉換過程,三級緩存提高了內存訪問效率。
7.I/O
hello與外界輸入輸出交互。
8.回收階段
當hello程序執行完畢并退出時,操作系統負責回收其占用的資源。這包括關閉程序打開的文件描述符,釋放分配的內存(包括堆和棧空間),清理進程的地址空間,以及回收進程的PID等資源。至此,hello的一生(暫時)結束了,直到下一次加載。
通過本課程學習,以及對hello從編寫到運行的全過程的深入分析,我對計算機系統的設計與實現有了更深刻的理解。計算機系統是一個復雜而精妙的協同工作體系,從硬件到軟件、從編譯器到操作系統,每一部分都扮演著不可或缺的角色。編譯器將高級語言轉化為機器可執行的代碼,優化程序性能;操作系統負責資源分配與管理,確保程序穩定運行。
隨著硬件技術的不斷進步和軟件需求的日益復雜,計算機系統的設計與實現將面臨更多挑戰。例如,多核處理器的普及要求操作系統和編譯器更好地支持并行計算;云計算和大數據技術的發展則對存儲管理和數據交換提出了更高的要求。面對這些挑戰,我們需要不斷探索新的設計與實現方法,比如引入人工智能技術優化系統資源分配。
(結論0分,缺失-1分)
附件
hello.i
預處理后的文件,通過GCC的-E選項生成。該文件包含了展開的頭文件、宏定義以及刪除的注釋,為編譯階段提供了干凈的輸入。
hello.s
編譯階段生成的匯編代碼文件,通過GCC的-S選項生成。該文件包含了程序的匯編指令,反映了C代碼到匯編指令的轉換過程。
hello.o
匯編階段生成的目標文件,通過GCC的-c選項生成。該文件包含了機器指令和符號表,是鏈接階段的輸入。
hello
鏈接階段生成的可執行文件,ld生成。
hello.dis.txt
反匯編輸出文件,通過objdump工具生成。該文件包含了hello文件的反匯編代碼,用于分析匯編指令和機器碼之間的關系。
hello.elf.txt
ELF文件格式分析輸出,通過readelf工具生成。該文件包含了可執行文件hello的ELF格式信息,用于分析程序的加載和鏈接過程。
hello.o.dis.txt
目標文件hello.o的反匯編輸出文件,通過objdump工具生成。該文件用于分析目標文件中的匯編指令和符號信息。
(附件0分,缺失 -1分)
參考文獻
[1]? Bryant, Randal E., & O'Hallaron, D. (2016). Computer Systems: A Programmer's Perspective (3rd ed.). Pearson.
[2] ? 奇小葩. (2021). 進程管理(一)--進程管理的基本概念-CSDN博客. https://blog.csdn.net/u012489236/article/details/115423024
[3]? 閆晟. (2020)邏輯地址、物理地址、虛擬地址_虛擬地址 邏輯地址-CSDN博客[EB/OL]. https://blog.csdn.net/TYUTyansheng/article/details/108148566
(參考文獻0分,缺失 -1分)