日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

计算机系统大作业——hello的一生

發布時間:2023/12/10 windows 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 计算机系统大作业——hello的一生 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

?

計算機系統

大作業

題 ????目??程序人生-Hello’s P2P ?

專 ??????業 ???????計算機???????????

學   ??號 ??????120L020109 ???????

班 ??級????????2003003??????????

學 ??????生 ????????張異凡????

指 導 教 師??????????史先俊?????? ?

計算機科學與技術學院

2022年5月

摘 ?要

文章結合計算機系統的有關知識,分析了一個hello程序從編寫成功到成功運行所經歷的各個階段,包括預處理,編譯,匯編,鏈接,加載運行在進程中,管理存儲,IO管理等內容,能更加深入地理解計算機系統地有關知識。

關鍵詞:計算機系統;預處理;編譯;匯編;鏈接;進程管理;存儲管理;IO管理????????????????????????????

(摘要0分,缺失-1分,根據內容精彩稱都酌情加分0-1分

目 ?錄

第1章 概述

1.1 Hello簡介

1.2 環境與工具

1.3 中間結果

1.4 本章小結

第2章 預處理

2.1 預處理的概念與作用

2.2在Ubuntu下預處理的命令

2.3 Hello的預處理結果解析

2.4 本章小結

第3章 編譯

3.1 編譯的概念與作用

3.2 在Ubuntu下編譯的命令

3.3 Hello的編譯結果解析

3.4 本章小結

第4章 匯編

4.1 匯編的概念與作用

4.2 在Ubuntu下匯編的命令

4.3 可重定位目標elf格式

4.4 Hello.o的結果解析

4.5 本章小結

第5章 鏈接

5.1 鏈接的概念與作用

5.2 在Ubuntu下鏈接的命令

5.3 可執行目標文件hello的格式

5.4 hello的虛擬地址空間

5.5 鏈接的重定位過程分析

5.6 hello的執行流程

5.7 Hello的動態鏈接分析

5.8 本章小結

第6章 hello進程管理

6.1 進程的概念與作用

6.2 簡述殼Shell-bash的作用與處理流程

6.3 Hello的fork進程創建過程

6.4 Hello的execve過程

6.5 Hello的進程執行

6.6 hello的異常與信號處理

6.7本章小結

第7章 hello的存儲管理

7.1 hello的存儲器地址空間

7.2 Intel邏輯地址到線性地址的變換-段式管理

7.3 Hello的線性地址到物理地址的變換-頁式管理

7.4 TLB與四級頁表支持下的VA到PA的變換

7.5 三級Cache支持下的物理內存訪問

7.6 hello進程fork時的內存映射

7.7 hello進程execve時的內存映射

7.8 缺頁故障與缺頁中斷處理

7.9動態存儲分配管理

7.10本章小結

第8章 hello的IO管理

8.1 Linux的IO設備管理方法

8.2 簡述Unix IO接口及其函數

8.3 printf的實現分析

8.4 getchar的實現分析

8.5本章小結

結論

附件

參考文獻


第1章 概述

1.1 Hello簡介

P2P:From Program to Process

利用C語言在任意的編輯軟件中編寫Hello.c文件形成了program,經過code:blocks等IDE進行預處理生成hello.i文件,再通過編譯生成hello.s,通過匯編生成可重定位目標文件hello.o,通過鏈接后生成可執行文件Hello,在shell中運行Hello,shell用fork為Hello創建新進程,用exeve為進程加載函數,再為其分配虛擬內存,最終能成功運行,成為Process,即P2P。

020:From Zero-0 to Zero-0

起初并沒有hello的進程,再shell中運行Hello,通過fork為Hello創建新進程,用exeve為進程加載函數,再為其分配虛擬內存。實現從無到有。待Hello運行完畢后,再通過父進程利用waitpid函數對其進行回收,并接受其結束的信號,若父進程已結束,則由init進程對其進行回收,hello的內存將會被釋放回收,不留下痕跡,實現了From Zero-0 to Zero-0。

1.2 環境與工具

1. 硬件環境

AMD Ryzen 5 4600H with Radeon Graphics 3.00 GHz;16G RAM;512GB SSD

2.?軟件環境

Windows11 64 位;Vmware 15 Pro;Ubuntu 20.04.4 64 位

Visual Studio 2010 64 位以上;CodeBlocks 64 位;vi/vim/gedit+gcc

3.?開發工具

Visual Studio 2022 64 位;CodeBlocks 64 位;vim;gdb;edb

1.3 中間結果

列出你為編寫本論文,生成的中間結果文件的名字,文件的作用等。

文件名稱

作用

hello.c

c語言程序源文件

hello.i

預處理后生成的文本文件

hello.s

編譯后產生的匯編語言文件

hello.o

匯編后產生的二進制可重定位目標文件

hello

鏈接后產生的可執行文件

hello_o_elf.txt

Hello.o的ELF格式文件的文本

hello_o_obj.txt

Hello.o的反匯編文件的文本

hello_elf.txt

hello的ELF格式文件的文本

hello_obj.txt

Hello的反匯編文件的文本

1.4 本章小結

簡要介紹了hello程序P2P和O2O的特點,介紹了對hello進行分析的硬件環境,軟件環境及開發工具,說明了編寫該文章所產生的中間文件的名稱及作用。

(第1章0.5分)


第2章 預處理

2.1?預處理的概念與作用

預處理的概念:預處理一般是指在程序源代碼被翻譯為目標代碼的過程中,生成二進制代碼之前的過程。在匯編之前,將程序中的預編譯指令在源文件的基礎上進行處理,生成.i文件。

預處理的作用:預處理器(cpp)根據以字符#開頭的預編譯處理指令,修改原始的C程序。如將#include指令指示的文件加入到對應的位置,將#define刪除,并將對應的所有宏定義替換等等,最終生成.i文件。

2.2在Ubuntu下預處理的命令

Ubuntu下的預處理命令:gcc -E hello.c -o hello.i

圖2.2.1

成功生成hello.i文件

2.3 Hello的預處理結果解析

Hello.i文件中的main函數與之前相同,而main函數前的三千多行即為之前的三個頭文件的內容。

圖2.3.1

?

?

圖2.3.2

從hello.i文件的開頭可以看到,hello.c中的注釋也被刪除了。

?

圖2.3.3

2.4 本章小結

對源文件進行預處理后,源文件中的注釋被刪除,對應的頭文件內容也被加入程序中。使文件在后續編譯中更好的執行。

(第2章0.5分)


第3章 編譯

3.1 編譯的概念與作用

編譯的概念:通過編譯器將預處理后產生的高級語言程序(.i文件)轉變為匯編語言文件(.s文件)

編譯的作用:通過語法分析和詞法分析將人類更易理解的高級語言轉化為機器更容易識別的匯編語言,并且可以對程序進行一定程度的優化。

3.2 在Ubuntu下編譯的命令

編譯指令:gcc -S hello.i -o hello.s,生成了hello.s文件。

圖3.2.1

?

3.3 Hello的編譯結果解析

3.3.1 文件聲明

?

圖3.3.1.1

源文件:.file "hello.c" ?

代碼段:.text

只讀數據段:.section .rodata

對齊格式:.align 8

3.3.2 數據

(1)數字常量:

程序用到的常量由0,1,2,3,4,8儲存在.text代碼段中。

(2)字符串常量:

hello用到的兩個字符串常量為兩個printf函數中的內容,第一個字符串為LC0,其中的中文字符用UTF-8用三個字節表示。第二個字符串LC1.

圖3.3.2.1

?

(3)Main函數參數:

Main函數的兩個參數為int argc和字符型指針數組char *argv[],分別表示用戶輸入的參數個數,以及參數的內容,分別儲存在%edi和%rsi中。

  • 局部變量:
  • For循環用到了局部變量i。

    3.3.3 賦值

    局部變量賦值:

    將i初始化為0.可以看到在棧中為i分配了4個字節的空間。

    ?

    圖3.3.3.1

    3.3.4 算術運算

    在for循環中每次為i加一。

    ?

    ?

    圖3.3.4.1

    3.3.5 關系操作

    在if語句中判斷agrc與4的大小。其中argc的值被存入-20(%rbp)的位置,與4比較。

    ?

    圖3.3.5.1

    在for語句中判斷i與8的大小。(此處編譯器將其與7比較大小,小于等于時再次循環,與源函數中小于8時循環繼續意義相同。)

    ?

    圖3.3.5.2

    3.3.6數組/指針操作

    Hello程序中由char *arg[]指針數組,由之前的分析得到char *arg[]被存儲在%rsi被存入-32(%rdx)的位置。通過先將-32(%rdx)存入%rax中,在將其分別加8,16,24得到argv[1],argv[2],argv[3]的地址。在取其地址值,賦值到%rdi,%rsi,%rdx中,作為參數傳入對應的函數。

    ?

    圖3.3.6.1

    3.3.7 控制轉移

    Hello程序中一共有兩次控制轉移,一次是if語句,一次是for語句。

    如圖所示,if語句將argc與4進行比較,若等于,則跳轉到for語句段,否則,調用printf函數輸出程序用法。

    For語句將i初始化后,判斷其與7的大小,若小于等于,則跳轉到.L4進入循環,否則,調用getchar函數,并在之后return。

    ?

    圖3.3.7.1

    3.3.8 函數操作

    Hello程序中涉及的函數有main函數,printf函數,exit函數,atoi函數,sleep函數,getchar函數.

  • main函數
  • ?

    圖3.3.8.1

    ?

    圖3.3.8.2

  • printf函數
  • 傳入的參數是字符串LC0,函數作用是打印傳入的字符串參數。

    ?

    圖3.3.8.3

  • exit函數
  • 傳入的參數是進程返回的值,函數作用是結束進程。

    圖3.3.8.4

    ?

  • atoi函數和sleep函數
  • Atoi函數將字符型變量轉為整型變量,sleep函數傳入整型,則讓程序停止對應的秒數。

    ?

    圖3.3.8.5

  • getchar函數
  • 函數沒有參數,其返回值,程序也未使用,函數用處是讀取鍵盤的一次輸入。

    ?

    圖3.3.8.6

    3.4 本章小結

    利用linux下的指令對預處理后的hello.i文件進行了編譯,得到了hello.s的匯編語言文件,并分析了源文件中的各個部分在匯編語言中的實現。

    (第3章2分)


    第4章 匯編

    4.1 匯編的概念與作用

    注意:這兒的匯編是指從 .s 到 .o 即編譯后的文件到生成機器語言二進制程序的過程。

    匯編的概念:通過匯編器將匯編代碼轉化為機器語言。

    匯編的作用:匯編器(as)將hello.s翻譯成機器語言指令,把這些指令打包成一種叫做可重定位目標程序(relocatable object program)的格式,并將結果保存在目標文件hello.o。

    4.2 在Ubuntu下匯編的命令

    Linux下匯編的指令:gcc -c hello.s -o hello.o

    ?

    圖4.2.1

    4.3 可重定位目標elf格式

    分析hello.o的ELF格式,用readelf等列出其各節的基本信息,特別是重定位項目分析。

    將hello.o的elf格式文件存入hello_o_elf.txt中。使用指令readelf -a hello.o > hello_o_elf.txt

    ?

    圖4.3.1

  • elf頭
  • Elf頭描述了生成該文件的系統的字的大小和字節順序,以及系統架構等一系列信息。

    ?

    圖4.3.2

  • 節頭部表
  • 節頭部表描述了不同節的名稱,類型,大小,地址,對齊方式和偏移量。

    .text節:以編譯的機器代碼。

    .rela.text節:一個.text節中位置的列表。

    .data節:已初始化的靜態和全局C變量。

    .bss節:未初始化的全局和靜態C變量,以及所有被初始化為0的全局或靜態變量。在目標文件中這個節不占據實際的空間,它僅僅是一個占位符。

    .rodata節:存放只讀數據,例如printf中的格式串和開關語句中的跳轉表。

    .comment節:包含版本控制信息。

    .note節:注釋節詳細描述。

    .eh_frame節:處理異常。

    .rela.eh_frame節:一個.eh_frame節中位置的列表。

    .shstrtab節:該區域包含節區名稱。

    .symtab節:一個符號表,它存放在程序中定義和引用的函數和全局變量的信息。

    .strtab節:一個字符串表,其內容包括.symtab和.debug節中的符號表,以及節頭部的節名字。

    .symtab節:本節用于存放符號表。

    ?

    圖4.3.3

  • 重定位節
  • 表述了各個段引用的外部符號等,在鏈接時,需要通過重定位節對這些位置的地址進行修改。鏈接器會通過重定位條目的類型判斷該使用什么養的方法計算正確的地址值,通過偏移量等信息計算出正確的地址。

    本程序需要重定位的信息有:.rodata中的模式串,puts,exit,printf,atoi,sleep,getchar這些符號。

    ?

    圖4.3.4

  • 符號表
  • 符號表存放在程序中定義和引用的函數和全局變量的信息。可以看到,hello程序中引用的函數名,如exit,printf,atoi等。

    ?

    圖4.3.5

    4.4 Hello.o的結果解析

    objdump -d -r hello.o ?分析hello.o的反匯編,并請與第3章的 hello.s進行對照分析。

    說明機器語言的構成,與匯編語言的映射關系。特別是機器語言中的操作數與匯編語言不一致,特別是分支轉移函數調用等。

    將objdump產生的數據存入hello_o_obj.txt中,便于分析。

    可以看到機器語言中指令和寄存器及數字均是由二進制數表示的,而objdump產生的文件中左側為機器語言,而右側為其對應的匯編語言。

    ?

    圖4.4.1

    ?

    圖4.4.2

    通過分析由hello.o反匯編出的匯編語言與hello.s中的匯編語言進行對比,可以得到以下三種區別。

  • 操作數的區別
  • 經匯編后,匯編語言中的操作數如立即數,偏移量,地址等均采用16進制,而非hello.s文件中的十進制。

  • 分支轉移的區別
  • 經過匯編后,jump類的語句跳轉的地址是基址+偏移量的模式,而不是hello.s中通過label進行跳轉

  • 函數調用的區別
  • 經過匯編后,call進行函數調用其后調用的是函數相對main函數的偏移量,而不是hello.s中通過函數名稱進行調用。

    4.5 本章小結

    在linux中利用指令將hello.s文件匯編為hello.o文件,并且通過readelf指令查看了文件的elf格式,觀察了文件的有關信息。又利用objdump指令對hello.o進行反匯編,通過與hello.s中匯編語言的比較,得出了匯編后機器語言的一些特點,同時也感受到了機器語言與匯編語言的映射關系。

    (第4章1分)


    5鏈接

    5.1 鏈接的概念與作用

    注意:這兒的鏈接是指從 hello.o 到hello生成過程。

    鏈接的概念:鏈接是將各種代碼和數據片段收集并組合成為一個單一文件的過程,這個文件可被加載(復制)到內存并執行。

    鏈接的作用:提供了一種模塊化的方式,可以將程序編寫為一個較小的源文件的集合,且實現了分開編譯更改源文件,從而減少整體文件的復雜度與大小,增加容錯性,也方便對某一模塊進行針對性修改。

    5.2 在Ubuntu下鏈接的命令

    使用ld的鏈接命令,應截圖,展示匯編過程! 注意不只連接hello.o文件

    鏈接的命令:ld ?-dynamic-linker /lib64/ld-linux-x86-64.so.2 ?/usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o hello.o -lc /usr/lib/gcc/x86_64-linux-gnu/9/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o -z relro -o hello

    ?

    圖5.2.1

    5.3 可執行目標文件hello的格式

    分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。

    將hello的elf格式文件存入hello_elf.txt中。

    ?

    圖5.3.1

  • ELF頭:hello文件的elf頭與hello.o文件的elf頭與提供的內容基本相同,而hello文件的elf頭將文件類型改為了可執行文件,并且為程序分配了入口點地址和程序頭起點。
  • ?

    圖5.3.2

  • 節頭:
  • 節頭部表展示了文件中各個節的名稱,大小,類型,地址和偏移量等信息,而鏈接后的各個節的地址信息更加具體完善。

    ?

    ?

    圖5.3.3

  • 程序頭:程序頭部分是一個結構數組,描述了系統準備程序執行所需的段或其他信息。
  • ?

    圖5.3.4

  • 重定位節:
  • 鏈接后,重定位部分的各個符號的偏移量更加明確,類型也更加具體,符號名稱也發生了改變。

    ?

    圖5.3.6

  • 符號表:
  • 鏈接后的符號表,與hello.o相比,增加了更多的符號。符號的定義和引用都在此聲明。

    ?

    圖5.3.7

    5.4 hello的虛擬地址空間

    使用edb加載hello,查看本進程的虛擬地址空間各段信息,并與5.3對照分析說明。 ??

    從edb中data dump一欄可以看到加載到hello的虛擬地址空間,而從hello的elf文件中的程序頭一欄可以看到鏈接器加載是運行的內容及與動態鏈接相關的信息,每個項目均又對應的虛擬內存地址,可以在edb中找到。

    ?

    圖5.4

    5.5 鏈接的重定位過程分析

    objdump -d -r hello 分析hello與hello.o的不同,說明鏈接的過程。

    結合hello.o的重定位項目,分析hello中對其怎么重定位的。

    利用指令objdump -d -r hello > hello_obj.txt指令將hello的反匯編文件存入hello_obj.txt中。

    Hello與hello.o的不同之處

  • 增加了hello程序中需要調用的外部函數,放在.plt.sec節中。如printf,exit,atoi,sleep,getchar等。
  • ?

    圖5.5.1

  • 增加了.init節,.plt節,節中分別存放這init函數和plt函數。
  • ?

    圖5.5.2

  • 地址訪問:hello.o中的相對偏移量地址變成了hello中的虛擬內存地址。
  • ?

    ?

    圖5.5.3

  • 函數調用:函數調用的地址從hello.o中的相對偏移量地址變成了函數調用地址。
  • ?

    圖5.5.4

    鏈接的過程:通過上面的比較可以看出來,鏈接的過程就是把程序調用的外部函數加入到函數中,并且將所有的函數和指令根據偏移量賦予虛擬地址進行重定位。

    重定位的實現:在hello.o的elf文件中給出了函數相對elf頭的相對偏移量。而hello的elf頭中給出了程序首地址,根據首地址和相對偏移量即可對所有的函數進行重定位。

    5.6 hello的執行流程

    使用edb執行hello,說明從加載hello到_start,到call main,以及程序終止的所有過程。請列出其調用與跳轉的各個子程序名或程序地址。

    ld-2.27.so!_dl_start 0x7fce8cc38ea0

    ld-2.27.so!_dl_init 0x7fce8cc47630

    hello!_start 0x400500

    libc-2.27.so!__libc_start_main 0x7fce8c867ab0 -libc-2.27.so!__cxa_atexit 0x7fce8c889430

    -libc-2.27.so!__libc_csu_init 0x4005c0

    hello!_init 0x400488

    libc-2.27.so!_setjmp 0x7fce8c884c10

    -libc-2.27.so!_sigsetjmp 0x7fce8c884b70 --libc-2.27.so!__sigjmp_save 0x7fce8c884bd0

    hello!main 0x400532

    hello!puts@plt 0x4004b0

    hello!exit@plt 0x4004e0

    *hello!printf@plt ?--

    *hello!sleep@plt --

    *hello!getchar@plt ?--

    ld-2.27.so!_dl_runtime_resolve_xsave 0x7fce8cc4e680 -ld-2.27.so!_dl_fixup 0x7fce8cc46df0

    --ld-2.27.so!_dl_lookup_symbol_x 0x7fce8cc420b0

    libc-2.27.so!exit 0x7fce8c889128

    5.7 Hello的動態鏈接分析

    分析hello程序的動態鏈接項目,通過edb調試,分析在dl_init前后,這些項目的內容變化。要截圖標識說明。

    動態鏈接器使用過程鏈接表 PLT+全局偏移量表GOT實現函數的動態鏈接,在GOT中存放函數目標地址,PLT使用 GOT中地址跳轉到目標函數,在加載時,動態鏈接器會重定位GOT中的每個條目,使得它包含目標的正確的絕對地址。

    在hello的elf格式文件中可以看到有關節的地址。

    ?

    圖5.7.1

    5.8 本章小結

    本章講解了鏈接的概念和作用,分析了hello的elf格式文件與hello.o文件的不同,利用edb查看了hello程序的虛擬內存地址,分析了hello文件的執行過程,重定位過程,以及動態鏈接過程。

    (第5章1分)


    6hello進程管理

    6.1 進程的概念與作用

    進程的概念:進程是操作系統一個正在運行的程序的一種抽象。程序在系統上運行時,操作系統會提供一種假象,就好像系統上只有這個程序在運行。程序看上去是獨占地使用處理器,主存和I/O設備。處理器看上去就像在不間斷地一條接一條的執行程序中的指令。

    進程的作用:進程提供給應用程序兩個關鍵抽象

    1.邏輯控制流 (Logical control flow)

    每個程序似乎獨占地使用CPU,通過OS內核的上下文切換機制提供。

    2.私有地址空間 (Private address space)

    每個程序似乎獨占地使用內存系統,OS內核的虛擬內存機制提供。

    6.2 簡述殼Shell-bash的作用與處理流程

    Shell-bash的作用:shell是一個交互型應用級程序,代表用戶運行其他程序。如Windows下的命令行解釋器,cmd、powershell,圖形界面的資源管理器。Linux下的Terminal/tcsh、bash等等,當然也包括圖形化的GNOME桌面環境。

    Shell處理流程:

  • shell會不斷讀取命令行參數
  • 接著判斷輸入的參數是內部命令還是外部命令,如果是內部命令直接執行。
  • 如果是外部命令,則為其創建子進程并加載函數。
  • 在進程結束后,通過父進程或init進程對子進程進行回收。
  • 6.3 Hello的fork進程創建過程

    在shell中輸入./Hello 120L020109 張異凡 1,shell會讀取命令行參數,帶參數執行Hello程序。Shell會通過fork函數創建子進程,子進程擁有與父進程相同的上下文,并且擁有自己的pid。當子進程結束時,會由父進程進行回收,若父進程已結束,則由init進程進行回收。

    6.4 Hello的execve過程

    在創建完全新的子進程后,通過execve函數加載運行hello程序,從而該子進程便擁有了自己獨特的上下文。execve函數將刪除該進程的代碼和地址空間內的內容并將其初始化,然后通過跳轉到程序的第一條指令或入口點來運行該程序,將私有的區域映射進來,重新設置程序計數器pc,并設置映射共享區。

    6.5 Hello的進程執行

    結合進程上下文信息、進程時間片,闡述進程調度的過程,用戶態與核心態轉換等等。

    (1)上下文信息

    內核為每個進程維持一個上下文,上下文就是內核重新啟動一個被搶占的進程所需的狀態。并通過上下文切換的機制來將控制轉移到新的進程。

  • 進程時間片
  • 一個進程執行它的控制流的一部分的每一時間段叫做時間片。

  • 進程調度的過程
  • 程序運行后,shell為hello分配了子進程。若hello進程不被搶占,則可一直在用戶態運行,若遇到搶占,則由用戶態轉入內核態,進行上下文切換,切換到其他進程,切換后再從內核態轉回用戶態。例如程序中會運行的sleep函數,程序正常運行到sleep函數時,sleep會申請將進程掛起,轉入內核態,并開啟倒計時,待倒計時結束后,重新轉入用戶態,hello程序繼續進行。

    6.6 hello的異常與信號處理

    ?hello執行過程中會出現哪幾類異常,會產生哪些信號,又怎么處理的。

    ?程序運行過程中可以按鍵盤,如不停亂按,包括回車,Ctrl-Z,Ctrl-C等,Ctrl-z后可以運行ps ?jobs ?pstree ?fg ?kill 等命令,請分別給出各命令及運行結截屏,說明異常與信號的處理。

    (1)正常運行:

    ?

    圖6.6.1

  • ctrl-c
  • 在hello運行中途輸入ctrl-c后,回向內核發送SIGINT信號,將前臺運行的所有進程終止,輸入ps指令可以看到hello進程已終止

    ?

    圖6.6.2

  • ctrl-z
  • 在hello運行中途輸入ctrl-z后,回向內核發送SIGTSTP信號,將前臺運行的所有進程掛起,輸入ps指令可以看到hello進程已掛起。

    ?

    圖6.6.3

    輸入jobs,可以看到hello的作業已停止。

    ?

    圖6.6.4

    輸入pstree,可以看到hello的進程位置。

    ?

    圖6.6.5

    輸入fg 1,可以將hello轉回前臺作業,繼續執行。

    圖6.6.6

    ?

    輸入kill -9 2988(hello的pid),強制將hello進程殺死

    ?

    圖6.6.7

  • 亂按
  • 在hello程序中途亂按,shell會記錄輸入的字符,當hello程序結束后,shell會將字符作為指令嘗試運行。

    ?

    圖6.6.8

    6.7本章小結

    本章介紹了進程和shell的概念和作用,根據hello程序運行的情況分析了通過fork創建進程,通過execve加載運行程序,以及進程的異常信號處理等內容。

    (第6章1分)


    7hello的存儲管理

    7.1 hello的存儲器地址空間

    結合hello說明邏輯地址、線性地址、虛擬地址、物理地址的概念。

  • 邏輯地址
  • 包含在機器語言中用來指定一個操作數或一條指令的地址。每一個邏輯地址都由一個段和偏移量組成,偏移量指明了從段開始的地方到實際地址之間的距離。就是hello.o里相對偏移地址。

  • 線性地址
  • 邏輯地址經過段機制轉化后為線性地址,其為處理器可尋址空間的地址,

    用于描述程序分頁信息的地址。具體以 hello 而言,線性地址標志著hello

    應在內存上哪些具體數據塊上運行。

  • 虛擬地址
  • 與線性地址為同一概念。

  • 物理地址
  • 用于內存芯片級的單元尋址,與處理器和CPU連接的地址總線相對應。地址翻譯會將hello的一個虛擬地址轉化為物理地址。

    7.2 Intel邏輯地址到線性地址的變換-段式管理

    Intel 處理器從邏輯地址到線性地址的變換通過段式管理的方式實現。每個程

    序在系統中都保存著一個段表,段表保存著該程序各段裝入主存的狀況信息,包括段號或段名、段起點、裝入位、段的長度、主存占用區域表、主存可用區域表等,從而方便進行段式管理。

    在段寄存器中,存放著段選擇符,可以通過段選擇符來得到對應段首地址。其包含三部分:索引,TI,RPL 。

    通過索引可以確定當前使用的段描述符在描述符表中的位置,進而通過段描述符得到段基址。段基址與偏移量結合就得到了線性地址,即虛擬地址。

    7.3 Hello的線性地址到物理地址的變換-頁式管理

    VM系統通過將虛擬內存分割為稱為虛擬頁的大小固定的塊,類似的,物理內存被分割為物理頁。系統通過操作系統軟件、mmu中的地址翻譯硬件和一個存放在物理內存中叫做頁表的數據結構來完成緩存各種操作。頁表將虛擬頁映射到物理頁,每次地址翻譯硬件將一個虛擬地址轉換為物理地址時,都會讀取頁表。

    頁表就是一個頁表條目的數組。虛擬地址空間中的每個頁在頁表中一個固定的偏移量處都有一個PTE。PTE包含了一個有效位和一個n位字段,有效位表明了該虛擬頁當前是否被緩存在DRAM中。因為DRAM是全相聯的,所以任意物理頁都可包含任意虛擬頁。

    虛擬地址可被分為兩個部分:VPN(虛擬頁號)和 VPO(虛擬頁偏移量),根據計算機系統的特性可以確定 VPN 與 VPO 的具體位數,由于虛擬內存與物理內存的頁大小相同,因此 VPO 與 PPO(物理頁偏移量)一致。而PPN(物理頁號)則需通過訪問頁表中的頁表條目(PTE)獲取。若PTE有效位為1,則發生頁命中,若為1,則發生缺頁故障。

    7.4 TLB與四級頁表支持下的VA到PA的變換

    每次CPU產生一個虛擬地址,MMU(內存管理單元)就必須查閱一個PTE(頁表條目),以便將虛擬地址翻譯為物理地址。在最糟糕的情況下,這會從內存多取一次數據,代價是幾十到幾百個周期。如果PTE碰巧緩存在L1中,那么開銷就會下降1或2個周期。然而,許多系統都試圖消除即使是這樣的開銷,它們在MMU中包括了一個關于PTE的小的緩存,稱為翻譯后備緩存器(TLB)。

    通過虛擬地址得到虛擬頁號(VPN),然后將虛擬頁號(VPN)傳入TLB中得到對應的物理頁號,然后結合虛擬偏移(VPO),確定唯一的物理地址;若是在TLB中找不到,則需要從高速緩存或者磁盤中取出對應的虛擬頁表,放入TLB再進行操做。若TLB命中,則從TLB中可以直接找到各級頁表,然后得到PPN,與PPO結合即可得到物理地址。若TLB不命中,則需要從高速緩存中到PPN。

    7.5 三級Cache支持下的物理內存訪問

    物理地址由CT,CI,CO三部分組成。CT為緩存標記位;CI為緩存組索引;CO為緩存偏移。

    使用CI進行組索引,每組8路,對8路的塊分別匹配CT如果匹配成功且塊的valid標志位為1,則命中,根據數據偏移量CO取出數據返回。如果沒有匹配成功或者匹配成功但是標志位是1,則不命中,向下一級緩存中查詢數據。查詢到數據之后,可以采取多種放置策略進行解決。例如LFU策略,若有空閑頁則放置,若無,則尋找使用次數最少的頁覆蓋放置。

    7.6 hello進程fork時的內存映射

    當fork函數被當前進程調用時,內核為新進程創建各種數據結構,并分配給他一個唯一得PID。同時為這個新進程創建虛擬內存。

    新進程現在的虛擬內存剛好和調用fork時存在的虛擬內存相同,當這兩個進程中的任一個后來進行寫操作時,寫時復制機制就會創建新頁面。因此,也就為每個進程保持了私有空間地址的抽象概念。

    7.7 hello進程execve時的內存映射

    execve 函數在shell中加載并運行包含在可執行目標文件hello中的程序,用hello程序有效地替代了當前程序。加載并運行hello需要以下幾個步驟:

    1)刪除已存在的用戶區域。刪除shell虛擬地址的用戶部分中的已存在的區域結構。映射私有區域。為hello的代碼、數據、bss 和棧區域創建新的區域結構。所有這些新的區域都是私有的、寫時復制的。代碼和數據區域被映射為hello 文件中的.text和.data 區。bss 區域是請求二進制零的,映射到匿名文件,其大小包含在hello 中。棧和堆區域也是請求二進制零的,初始長度為零。圖7.7 概括了私有區域的不同映射。

    2)映射共享區域。如果hello程序與共享對象(或目標)鏈接,比如標準C 庫libc. so, 那么這些對象都是動態鏈接到這個程序的,然后再映射到用戶虛擬地址空間中的共享區域內。

    3)設置程序計數器(PC) 。execve 做的最后一件事情就是設置當前進程上下文中的程序計數器,使之指向代碼區域的入口點。

    7.8 缺頁故障與缺頁中斷處理

    DRAM緩存不命中稱為缺頁。假設某時刻CPU引用了VP3中的一個字,VP3并未緩存在DRAM中。地址翻譯硬件從內存中讀取PTE3,從有效位推斷出VP3未被緩存,并且觸發了一個缺頁異常。缺頁異常調用內核中的缺頁異常處理程序。缺頁異常處理程序會選擇一個犧牲頁,比如存放在PP3中的VP4。如果VP4已經被修改了,那么內核會將它復制回磁盤。內核會修改VP4的頁表條目,反映出VP4不再緩存在主存中這一事實。接下來,內核從磁盤復制VP3到內存中的PP3,更新PTE3,然后返回。當異常處理程序返回時,它會重啟導致缺頁的指令,該指令會把導致缺頁的虛擬地址重發送到地址翻譯硬件。

    7.9動態存儲分配管理

    Printf會調用malloc,請簡述動態內存管理的基本方法與策略。

    動態內存分配器維護著一個稱為堆(heap)的進程的虛擬內存區域。分配器將堆視為 一組不同大小的塊的集合來維護。每個塊就是一個連續的虛擬內存片,要么是已分配的,要么是空閑的。已分配的塊顯式地保留為供應用程序使用。

    空閑塊通過頭部中的大小字段隱含的鏈接著,分配器可以可以通過遍歷堆中所有的塊,從而間接的遍歷整個空閑塊的集合。當一個應用請求一個k字節的塊時,分配器搜索空閑鏈表,查找一個大小足夠放置所請求塊的空閑塊。分配器執行這種搜索的方式是由放置策略確定的。

    一旦分配器找到一個匹配的塊,他就必須做另外一個決定,那就是分配這個空閑塊中多少的空間。

    若找不到合適的空閑塊,一個選擇是合并那些在內存中物理上相鄰的空閑塊來創建一些更大的空閑塊。如果還不夠,就會通過sbrk函數,向內核請求額外的堆內存。

    分配器的多種類型

    (1)顯式空閑鏈表:

    將空閑塊組織為某種形式的顯式數據結構。因為根據定義,程序不需要一個空閑塊的主體,所以實現這個數據結構的指針可以存放在這些空閑塊的主體里面。

    (2)隱式空閑鏈表:

    堆中的空閑塊通過頭部中的大小字段隱含地連接,分配器通過遍歷堆中所有的塊,從而間接遍歷整個空閑塊的集合。

  • 分離的空閑鏈表:維護多個空閑鏈表,其中每個鏈表中的塊有大致相等的大小。
  • 獲取額外的堆內存,進行帶邊界標記的合并:

    在每個塊的結尾處添加一個腳部(頭部的一個副本),分配器可以通過檢查他的腳部,判斷前一個塊的起始位置和狀態。這個腳部總是在距離當前快開始位置一個字的距離。

    7.10本章小結

    本章主要介紹了 hello 的存儲器地址空間、intel 的段式管理、頁式管理,邏輯地址到線性地址到物理地址的變換,進程運行fork、execve 時的內存映射、缺頁故障與缺頁中斷處理、動態存儲分配管理。

    (第7章 2分)


    8hello的IO管理

    8.1 Linux的IO設備管理方法

    設備的模型化:文件

    設備管理:unix io接口

    所有的I/O設備(例如網絡、磁盤和終端)都被模型化為文件,而所有的輸入和輸出都被當做對相應文件的讀和寫來執行,這種將設備優雅地映射為文件的方式,允許Linux內核引出一個簡單低級的應用接口,稱為Unix I/O。

    8.2 簡述Unix IO接口及其函數

  • Unix IO接口
  • 1.打開文件,內核返回一個非負整數的文件描述符,通過對此文件描述符對文件進行所有操作。

    2.Linux shell創建的每個進程開始時都有三個打開的文件:標準輸入(文件描述符0)、標準輸出(描述符為1),標準出錯(描述符為2)。頭文件<unistd.h>定義了常量STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO,他們可用來代替顯式的描述符值。

    3.改變當前的文件位置,文件開始位置為文件偏移量,應用程序通過seek操作,可設置文件的當前位置為k。

    4.讀寫文件,讀操作:從文件復制n個字節到內存,從當前文件位置k開始,然后將k增加到k+n;寫操作:從內存復制n個字節到文件,當前文件位置為k,然后更新k

    5.關閉文件。當應用完成對文件的訪問后,通知內核關閉這個文件。內核會釋放文件打開時創建的數據結構,將描述符恢復到描述符池中

    (2)?Unix IO函數:

    1.Open()函數:

    功能描述:用于打開或創建文件,在打開或創建文件時可以指定文件的屬性及用戶的權限等各種參數。

    函數原型:int open(const char *pathname,int flags,int perms)

    參數:pathname:被打開的文件名(可包括路徑名如"dev/ttyS0")flags:文件打開方式,

    返回值:成功:返回文件描述符;失敗:返回-1

    2.Close()函數:

    功能描述:用于關閉一個被打開的的文件

    所需頭文件: #include <unistd.h>

    函數原型:int close(int fd)

    參數:fd文件描述符

    函數返回值:0成功,-1出錯

    3.Read()函數:

    功能描述: 從文件讀取數據。

    所需頭文件: #include <unistd.h>

    函數原型:ssize_t read(int fd, void *buf, size_t count);

    參數:fd:將要讀取數據的文件描述詞。buf:指緩沖區,即讀取的數據會被放到這個緩沖區中去。count: 表示調用一次read操作,應該讀多少數量的字符。

    返回值:返回所讀取的字節數;0(讀到EOF);-1(出錯)。

    4.Write()函數:

    功能描述: 向文件寫入數據。

    所需頭文件: #include <unistd.h>

    函數原型:ssize_t write(int fd, void *buf, size_t count);

    返回值:寫入文件的字節數(成功);-1(出錯)

    5.Lseek()函數

    功能描述: 用于在指定的文件描述符中將將文件指針定位到相應位置。

    所需頭文件:#include <unistd.h>,#include <sys/types.h>

    函數原型:off_t lseek(int fd, off_t offset,int whence);

    參數:fd;文件描述符。offset:偏移量,每一個讀寫操作所需要移動的距離,單位是字節,可正可負(向前移,向后移)

    返回值:成功:返回當前位移;失敗:返回-1

    8.3 printf的實現分析

    查看printf的函數體

    int printf(const char *fmt, ...)
    {
    int i;
    char buf[256];
    ???
    ???? va_list arg = (va_list)((char*)(&fmt) + 4);
    ???? i = vsprintf(buf, fmt, arg);
    ???? write(buf, i);
    ???
    ???? return i;
    ??? }

    其中va_list的定義是typedef char *va_list,是一個字符指針。而其中的(char*)(&fmt) + 4) 表示的是...中的第一個參數的地址。

    而其中的vsprintf函數它接受確定輸出格式的格式字符串fmt,并返回要打印出來的字符串的長度。

    而write函數的作用便是實現打印操作,它以INT_VECTOR_SYS_CALL結束,而INT_VECTOR_SYS_CALL通過系統調用syscall。

    syscall 將字符串中的字節從寄存器中通過總線復制到顯卡的顯存中,顯存中

    存儲的是字符的 ASCII 碼,符顯示驅動子程序:從 ASCII 到字模庫到顯示 vram(存儲每一個點的 RGB 顏色信息)。顯示芯片按照刷新頻率逐行讀取 vram,并通過信號線向液晶顯示器傳輸每一個點(RGB 分量)。

    [轉]printf 函數實現的深入剖析 - Pianistx - 博客園

    從vsprintf生成顯示信息,到write系統函數,到陷阱-系統調用 int 0x80或syscall等.

    字符顯示驅動子程序:從ASCII到字模庫到顯示vram(存儲每一個點的RGB顏色信息)。

    顯示芯片按照刷新頻率逐行讀取vram,并通過信號線向液晶顯示器傳輸每一個點(RGB分量)。

    8.4 getchar的實現分析

    getchar 調用系統函數 read,發送一個中斷信號,內核搶占這個進程,用戶輸入字符串,鍵入回車后(字符串和回車都保存在緩沖區內),再次發送信號,內核重新調度這個進程,getchar 從緩沖區讀入字符。

    異步異常-鍵盤中斷的處理:鍵盤中斷處理子程序。接受按鍵掃描碼轉成ascii碼,保存到系統的鍵盤緩沖區。

    getchar等調用read系統函數,通過系統調用讀取按鍵ascii碼,直到接受到回車鍵才返回。

    8.5本章小結

    本章主要介紹了linux 的IO設備管理方法和及其接口和函數,對 printf 函數和 getchar 函數的底層實現進行了簡單的分析。

    (第8章1分)

    結論

    用計算機系統的語言,逐條總結hello所經歷的過程。

    你對計算機系統的設計與實現的深切感悟,你的創新理念,如新的設計與實現方法。

    hello 程序的一生經歷了如下過程:

    1. 預處理

    將 hello.c 中include的頭文件的內容加入到hello中替換,生成hello.i文件。

    2. 編譯

    通過編譯器利用詞法分析和語法分析將 hello.i翻譯成匯編語言文件 hello.s,并進行一定程度的優化。

    3. 匯編

    將 hello.s 匯編程序翻譯成機器語言指令,并把這些指令打包成可重定位目

    標程序格式,生成hello.o文件。

    4. 鏈接

    通過鏈接器,將 hello 的程序編碼與動態鏈接庫等收集整理成為一個單一

    文件,生成完全鏈接的可執行的目標文件 hello;

    5. 創建進程加載運行

    打開 Shell,在其中鍵入 ./hello 120L020109?張異凡 1,shell為其 fork 新的子進程,并通過 execve 把代碼和數據加載入虛擬內存空間,程序開始執行;

    6. 執行指令

    在該進程被調度時,CPU 為hello其分配時間片,在一個時間片中,hello

    享有 CPU 全部資源,PC 寄存器一步一步地更新,CPU 不斷地取指,順序執

    行自己的控制邏輯流。

    7. 訪存

    內存管理單元 MMU 將邏輯地址,一步步映射成物理地址,進而通過三級

    高速緩存系統訪問物理內存/磁盤中的數據;

    8. 動態申請內存

    printf 會調用 malloc 向動態內存分配器申請堆中的內存。

    9. 信號處理

    進程時刻等待著信號,如果用戶輸入了ctrl-c或ctrl-z等輸入,則進程會被停止或掛起。

    10. 終止并被回收

    Shell父進程等待并回收hello子進程,并釋放為hello分配的空間,刪除hello的有關數據。

    (結論0分,缺失 -1分,根據內容酌情加分)


    附件

    列出所有的中間產物的文件名,并予以說明起作用。

    文件名稱

    作用

    hello.c

    c語言程序源文件

    hello.i

    預處理后生成的文本文件

    hello.s

    編譯后產生的匯編語言文件

    hello.o

    匯編后產生的二進制可重定位目標文件

    hello

    鏈接后產生的可執行文件

    hello_o_elf.txt

    Hello.o的ELF格式文件的文本

    hello_o_obj.txt

    Hello.o的反匯編文件的文本

    hello_elf.txt

    hello的ELF格式文件的文本

    hello_obj.txt

    Hello的反匯編文件的文本

    (附件0分,缺失 -1分)


    參考文獻

    為完成本次大作業你翻閱的書籍與網站等

    [1] ?printf 函數實現的深入剖析

    https://www.cnblogs.com/pianist/p/3315801.html

    [2] ?深入理解計算機系統

    [3] ?HIT-CS 課程相關PPT。

    (參考文獻0分,缺失 -1分)

    總結

    以上是生活随笔為你收集整理的计算机系统大作业——hello的一生的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。