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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HIT CSAPP 大作业

發布時間:2023/12/10 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HIT CSAPP 大作业 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

HIT CSAPP 大作業

摘 要
本論文從hello.c到hello可執行程序再到程序的具體執行和終止逐步分析了hello程序。
hello的P2P,也就是program to process,從輸入hello.c文件的源代碼,到cpp預處理成hello.i,再到ccl編譯生成匯編語言文件hello.s,并組裝為可重定位文件hello.o,ld將其與各個庫鏈接,shell收到./hello的指令后,調用fork創建進程,execve加載到內存來完成這個程序。之后程序由cpu控制其邏輯流程的操作、中斷、上下文切換來處理異常,最后結束,父進程回收資源。
結合課本中的知識和一些資料,利用Ubuntu虛擬機對gdb、edb、gcc等工具進行調試和測試。 結合本學期計算機系統各章節的知識,展示自己的能力,形成自己的收獲,體現自己對此課程的理解。

關鍵詞:hello.c;大作業;深入理解計算機系統;

(摘要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的預處理結果解析 - 7 -
2.4 本章小結 - 7 -
第3章 編譯 - 8 -
3.1 編譯的概念與作用 - 8 -
3.2 在UBUNTU下編譯的命令 - 8 -
3.3 HELLO的編譯結果解析 - 8 -
3.4 本章小結 - 11 -
第4章 匯編 - 12 -
4.1 匯編的概念與作用 - 12 -
4.2 在UBUNTU下匯編的命令 - 12 -
4.3 可重定位目標ELF格式 - 12 -
4.4 HELLO.O的結果解析 - 14 -
4.5 本章小結 - 15 -
第5章 鏈接 - 16 -
5.1 鏈接的概念與作用 - 16 -
5.2 在UBUNTU下鏈接的命令 - 16 -
5.3 可執行目標文件HELLO的格式 - 17 -
5.4 HELLO的虛擬地址空間 - 17 -
5.5 鏈接的重定位過程分析 - 18 -
5.6 HELLO的執行流程 - 19 -
5.7 HELLO的動態鏈接分析 - 19 -
5.8 本章小結 - 20 -
第6章 HELLO進程管理 - 21 -
6.1 進程的概念與作用 - 21 -
6.2 簡述殼SHELL-BASH的作用與處理流程 - 21 -
6.3 HELLO的FORK進程創建過程 - 21 -
6.4 HELLO的EXECVE過程 - 22 -
6.5 HELLO的進程執行 - 22 -
6.6 HELLO的異常與信號處理 - 23 -
6.7本章小結 - 25 -
第7章 HELLO的存儲管理 - 26 -
7.1 HELLO的存儲器地址空間 - 26 -
7.2 INTEL邏輯地址到線性地址的變換-段式管理 - 26 -
7.3 HELLO的線性地址到物理地址的變換-頁式管理 - 27 -
7.4 TLB與四級頁表支持下的VA到PA的變換 - 28 -
7.5 三級CACHE支持下的物理內存訪問 - 28 -
7.6 HELLO進程FORK時的內存映射 - 29 -
7.7 HELLO進程EXECVE時的內存映射 - 29 -
7.8 缺頁故障與缺頁中斷處理 - 29 -
7.9動態存儲分配管理 - 30 -
7.10本章小結 - 32 -
第8章 HELLO的IO管理 - 33 -
8.1 LINUX的IO設備管理方法 - 33 -
8.2 簡述UNIX IO接口及其函數 - 34 -
8.3 PRINTF的實現分析 - 34 -
8.4 GETCHAR的實現分析 - 36 -
8.5本章小結 - 36 -
結論 - 37 -
附件 - 38 -
參考文獻 - 39 -

第1章 概述
1.1 Hello簡介
根據Hello的自白,利用計算機系統的術語,簡述Hello的P2P,020的整個過程。
P2P:首先hello.c 利用I/O設備通過總線存儲進內存中。GCC編譯器驅動然后讀取源程序文件hello.c,通過預處理器cpp轉化為hello.i(修改后的源程序),再利用編譯器ccl讀入hello.s(匯編語言),然后用as將其匯編變成hello.o(可重定位的目標程序),hello.o 是機器友好的二進制代碼。最后,它通過鏈接器ld與標準C庫動態鏈接,并最終成為hello(一個可執行的目標程序)。
此時,hello已經是一個可執行的程序。
然后在shell中輸入字符串“./hello”。 shell 程序將字符串讀入寄存器并解析。然后shell調用fork函數來創建一個新的子進程。子進程是父進程shell的副本,再通過execve函數調用啟動加載器。加載程序刪除子進程現有的虛擬內存段,然后使用mmap函數創建新的內存區域,創建一組新的代碼、數據、堆棧段。新的堆棧段被初始化為零。通過將虛擬地址空間中的頁面映射到可執行文件的頁面大小塊,新的代碼和數據段被賦值為可執行文件的對應內容。最后,加載器跳轉到_start的地址運行應用main函數。
020:程序在內存中From Zero to Zero,hello先是執行了P2P所述的過程,然后在程序執行結束之后,hello對應的進程會保持在終止狀態,直到被其父進程回收然后退出,shell會再次變成執行hello之前的狀態,也就是說又變成了Zero。
1.2 環境與工具
列出你為編寫本論文,折騰Hello的整個過程中,使用的軟硬件環境,以及開發與調試工具。
硬件環境
X64 CPU;2GHz;8G RAM;256GHD disk
軟件環境
Windows10 64位;Vmware 11;Ubuntu 18.04;LTS 64位
開發工具
Visual Studio 2017;CodeBlocks 64位;vi/vim/gedit+gcc
Readelf;gdb/edb;objdump
1.3 中間結果
列出你為編寫本論文,生成的中間結果文件的名字,文件的作用等。
hello.c:c源代碼文件
hello.i:預處理后的文件
hello.s:編譯后的匯編文件
hello.o:匯編后的匯編可重定位文件
hello:鏈接后生成的可執行文件
hello.elf: hello.o的ELF文件
hellold.elf: hello.out的ELF文件
1.4 本章小結
第一章簡單解釋了P2P和020的概念,說明了本次大作業的環境和工具,列出了為編寫本論文,生成的中間結果文件并解釋了其作用。
(第1章0.5分)

第2章 預處理
2.1 預處理的概念與作用
(以下格式自行編排,編輯時刪除)
預處理:
預編譯器根據以字符#開頭的預處理指令,對代碼進行初始的處理,最終的得到一個以.i為擴展名的C文件。
指令 作用

#define 定義宏
#undef 取消已定義的宏
#include 包含一個源文件
#if 如果給定條件為真,則編譯下面代碼
#ifdef 如果宏已經定義,則編譯下面代碼
#ifndef 如果宏沒有定義,則編譯下面代碼
#elif else if的簡寫
#endif 結束一個#if……#else條件編譯塊
#error 停止編譯并顯示錯誤信息
預處理的作用:
宏定義:用宏名與替換實際字串,增強代碼可讀性。
文件包含:將include的頭文件復制到#后,提高編程的模塊性。
條件編譯:區分被編譯和不被編譯的代碼,增加的選擇。
2.2在Ubuntu下預處理的命令
命令:gcc hello.c -E -o hello.i

經過預處理之后,hello.c轉化為hello.i文件
2.3 Hello的預處理結果解析
打開hello.i文件可以發現,文件main函數之前的內容變多,main函數C語言程序文本文件,只是對原文件中的宏進行了宏展開,頭文件中的內容被加入此文件中。如果代碼中有#define命令還會替換程序中對相應的符號。
(以下格式自行編排,編輯時刪除)
2.4 本章小結
第二章介紹了預處理的相關概念及處理的類型,實現替換定義的宏符號、加入頭文件的內容、根據指令進行選擇性編譯等。
(以下格式自行編排,編輯時刪除)

(第2章0.5分)

第3章 編譯
3.1 編譯的概念與作用
編譯是利用編譯器從.i產生.s(匯編文本)的過程。主要包含五個階段:詞法分析;語法分析;語義檢查、中間代碼生成、目標代碼生成。
作用:將文本文件hello.i翻譯成文本文件hello.s,并提示出現的語法錯誤,執行過程主要從其中三個階段進行分析:
詞法分析:詞法分析是掃描由字符組成的單詞,把源程序中的字符串改造成為由單詞符號串組成的的中間程序;
語法分析:將單詞符號串輸入語法分析器,其分析單詞符號串是否符合語法規則得生成了語法單位;
目標代碼生成:語法分析后的中間代碼傳入目標代碼生成器,經匯編生成匯編語言代碼,然后轉化為可執行的機器語言代碼。
3.2 在Ubuntu下編譯的命令
命令:gcc -S -o hello.s hello.i

3.3 Hello的編譯結果解析

打開hello.s文件

3.3.1 數據
局部變量:作為函數中的局部變量i被儲存在棧中,棧地址:%rbp-4
argc:傳入的參數,存在棧里,位于%rbp-20。
argv[]:傳入的數組,存在棧里,argv的地址位于%rbp-32,利用argv的地址加i8,就能得到argv[i]。
立即數:儲存在.data段中,在運行需要時加入寄存器中,如果無空閑寄存器則放入棧中。
表達式:存在代碼段的.rodata中
3.3.2 賦值
為棧上的局部變量i賦初值=0,用movl賦值。
movl $0, -4(%rbp)
3.3.3 類型轉換
atoi函數將字符型argv[3]轉換為整型數。
3.3.4 算術操作
編譯器將i++編譯成
addl $1, -4(%rbp)
3.3.5 關系操作
編譯器將i<8與跳轉編譯成
cmpl $7, -4(%rbp)
jle .L4
將argc!=4編譯成
cmpl $4, -20(%rbp)
je .L2
3.3.6 數組/指針/結構操作
argv數組是傳入的參數,儲存在棧上。
初始地址位于%rbp-32,利用argv的地址加i8,就能得到argv[i]
3.3.7 控制轉移
編譯器將if,for等控制轉移語句都使用了cmp來比較然后使用了條件跳轉指令來跳轉。編譯器將if(argc!=3)編譯成:
cmpl $3, -20(%rbp)
je .L2
將for循環里面的比較和轉移編譯成:
cmpl $9, -4(%rbp)
jle .L4
3.3.8 函數操作
將printf(“用法: Hello 學號 姓名 秒數!\n”);編譯為:

將printf(“Hello %s %s\n”,argv[1],argv[2]);編譯為:

將sleep(atoi(argv[3]));編譯為:


此部分是重點,說明編譯器是怎么處理C語言的各個數據類型以及各類操作的。應分3.3.1~ 3.3.x等按照類型和操作進行分析,只要hello.s中出現的屬于大作業PPT中P4給出的參考C數據與操作,都應解析。
3.4 本章小結
第三章講述了編譯階段編譯器如何把.i代碼轉換成.s匯編代碼的,同時也分析c代碼中的各數據與操作在匯編代碼中是如何儲存并實現的。匯編語言是每個程序員會經常接觸到的一種代碼語言,掌握匯編語言十分重要。(第3章2分)

第4章 匯編
4.1 匯編的概念與作用
概念:將.s匯編語言翻譯成.o機器語言的過程
作用:匯編器將hello.s翻譯成機器語言指令,并把這些指令打包轉化成可重定位目標程序的格式,并將結果保存在二進制目標文件hello.o中。
4.2 在Ubuntu下匯編的命令
命令:as hello.s -o hello.o

應截圖,展示匯編過程!
4.3 可重定位目標elf格式
命令:readelf -a hello.o > hello.elf

ELF頭:文件格式
.text:機器代碼
.rodata:只讀數據(跳轉表等)
.data:已初始化的全局變量
.bss: 未初始化的全局變量
.symtab:符號表
.rel.text:可重定位代碼
.rel.data:可重定位數據
.debug:調試符號表
ELF頭開始是一個16字節的序列,描述了生成該文件的系統的字的大小和字節順序,剩下的部分是幫助鏈接器實現語法分析和解釋目標文件的信息。其中包括ELF頭大小,目標文件類型,節頭部表的文件偏移,節頭部表中條目的大小和數量。

重定位節.rela.text,如上圖
重定位節:包含.text 節中需要進行重定位的代碼,當鏈接器將其與其他文件鏈接時,需要修改這些位置。
重定位節.rela.text中各項符號的信息:
Offset:需要被修改的節的偏移量Info:包括symbol和type,symbol是前四個字節,type是后四個字節。
symbol:標識被修改后的內容應該指向的符號。
type:重定位的類型。
value:標識鏈接器如何修改新的應用。
Attend:有符號整型數,重定位的偏移調整。
Name:重定位到的目標的名稱。
分析hello.o的ELF格式,用readelf等列出其各節的基本信息,特別是重定位項目分析。
4.4 Hello.o的結果解析
反匯編命令:objdump -d -r hello.o

將重定位信息(類型和相對位置等)加入,在連接時需修改其位置。
① 機器語言(由操作碼和操作數組成)能夠被機器識別
② 機器語言和匯編語言的關系是以一一對應的。
③ 機器語言的跳轉(jxx、call等)使用的是目的地址與下一條指令的頭部的偏移量來表示的,在匯編代碼中使用的是標號表示的。
④ 機器語言的反匯編代碼為每條指令都對應了具體的指令地址
⑤ 機器語言中,訪問全局變量采用了短名稱+%rip的方式
objdump -d -r hello.o 分析hello.o的反匯編,并請與第3章的 hello.s進行對照分析。
說明機器語言的構成,與匯編語言的映射關系。特別是機器語言中的操作數與匯編語言不一致,特別是分支轉移函數調用等。
4.5 本章小結
第四章介紹匯編和匯編后生成的可重定位文件,分析可重定位文件的結構和各個組成部分,以及各個組成部分的內容和功能,比較了機器語言和匯編代碼的區別和關系,生成hello.o可重定位文件
(以下格式自行編排,編輯時刪除)
(第4章1分)

第5章 鏈接
5.1 鏈接的概念與作用
概念:鏈接器將文件hello.o中的一些未確定的變量,函數與其所要連接的.o文件進行合并,生成可執行文件hello
作用:可重定位目標文件.o不能直接執行,需要鏈接過后形可執行目標文件.out計算機才能執行hello程序。鏈接器實現了分離編譯(模塊化)。
注意:這兒的鏈接是指從 hello.o 到hello生成過程。
5.2 在Ubuntu下鏈接的命令
(以下格式自行編排,編輯時刪除)
命令:ld -o hello -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 hello.o /usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o

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

5.3 可執行目標文件hello的格式
命令:readelf -a hello > hellold.elf


在ELF格式文件中,Section Headers聲明了hello中的所有section信息,包括程序中的size和offset,所以可以根據Section Headers中的信息使用HexEdit定位每個被占用的section在區間(起始位置,大小),其中Address是程序在虛擬地址處加載的起始地址。
只讀內存段:起始:0x402000 大小:0x3b
讀寫內存段:起始:0x404048 大小:0x4
符號表和調試信息:起始:0x000000 大小:0x4c8
代碼段:起始:0x4010f0 大小:0x145
……
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。
5.4 hello的虛擬地址空間
edb中0x400000所儲存的信息與表頭所儲存的信息一致

Init段存在于0x401000
rodata段在0x404048

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

不同:
跳轉:hello.o為相對偏移地址,hello為虛擬內存地址。
Hello含有外部鏈接得到的函數。
hello相對hello.o增加了部分節(.init,.plt等)。
重定位:
一旦鏈接器完成了符號解析這一步,就把代碼中的每個符號引用和正好一個符號定義(即其一個輸入目標模塊中的一個符號表條目)關聯起來。此時,鏈接器就知道其輸入目標模塊中的代碼節和數據節的確切大小?,F在就可以開始重定位步驟了,在這個步驟中,將合并輸入模塊,并為每個符號分配運行時地址。重定位由兩步組成:
·重定位節和符號定義。在這一步中,鏈接器將所有相同類型的節合并為同一類型的新的聚合節。例如,來自所有輸入模塊的.data節被全部合并成一個節,這個節成為輸出的可執行目標文件的.data節。然后,鏈接器將運行時內存地址賦給新的聚合節,賦給輸入模塊定義的每個節,以及賦給輸入模塊定義的每個符號。當這一步完成時,程序中的每條指令和全局變量都有唯一的運行時內存地址了。
·重定位節中的符號引用。在這一步中,鏈接器修改代碼節和數據節中對每個符號的引用,使得它們指向正確的運行時地址。要執行這一步,鏈接器依賴于可重定位目標模塊中稱為重定位條目(relocation entry)的數據結構,我們接下來將會描述這種數據結構。
objdump -d -r hello 分析hello與hello.o的不同,說明鏈接的過程。
結合hello.o的重定位項目,分析hello中對其怎么重定位的。
5.6 hello的執行流程
(以下格式自行編排,編輯時刪除)
hello!_start 0x401090
__libc_start_main 0x403ff0
main 0x4010c1
hello!puts@plt 0x401030
hello!exit@plt 0x401070
hello!printf@plt 0x401040
hello!atoi@plt 0x401060
hello!sleep@plt 0x401080
hello!getchar@plt 0x401050
使用edb執行hello,說明從加載hello到_start,到call main,以及程序終止的所有過程。請列出其調用與跳轉的各個子程序名或程序地址。
5.7 Hello的動態鏈接分析
動態鏈接:
共享庫(shared library)是致力于解決靜態庫缺陷的一個現代創新產物。共享庫是一個目標模塊,在運行或加載時,可以加載到任意的內存地址,并和一個在內存中的程序鏈接起來。這個過程稱為動態鏈樓(dynamic linking),是由一個叫做動態鏈接器(dytamic linkeg的程序來執行的。共享庫也稱為共享目標(shared object),在Linux系統中通常用,s0后綴來表示,微軟的操作系統大量地使用了共享庫,它們稱為DLL(動態鏈接庫)。
共享庫是以兩種不同的方式來“共享”的。首先,在任何給定的文件系統中,對于一個庫只有一個.so文件。所有引用該庫的可執行目標文件共享這個.so文件中的代碼和數據,而不是像靜態庫的內容那樣被復制和嵌入到引用它們的可執行的文件中。其次,在內存中,一個共享庫的.text節的一個副本可以被不同的正在運行的進程共享。
例:
GOT:GOT是一個數組,其中元素是8字節的地址。和PLT聯合使用時,GOT[0]和GOT[1]包含動態鏈接器在解析函數地址時需要用到的信息。GOT[2]是動態鏈接器在1d-linux.so模塊中的入口。其余的每個條目對應于一個被調用的函數,其地址需要在運行時被解析。根據hello的ELF文件可知GOT起始表位置為0x404000。
Dl_init執行前0x404000的16個字節均為0:
Dl_init執行后0x404000的16個字節有所改變:


分析hello程序的動態鏈接項目,通過edb調試,分析在dl_init前后,這些項目的內容變化。要截圖標識說明。
5.8 本章小結
第五章中主要介紹了鏈接的概念與作用,并且詳細說明了hello.o是怎么與其他.o(.so)文件鏈接成為一個可執行目標文件的過程,展示了hello.o的ELF文件形式和各個節的含義,分析了hello的虛擬地址空間、重定位過程與動態鏈接過程。
(以下格式自行編排,編輯時刪除)
(第5章1分)

第6章 hello進程管理
6.1 進程的概念與作用
概念:進程是一個正在運行的程序的實例。系統中的每個程序都在某個進程的上下文中運行。上下文由程序正確運行所必需的狀態組成。這種狀態包括存儲在內存中的代碼和程序數據、堆棧、通用寄存器的內容、程序計數器、環境變量和文件描述符的集合。
功能:進程為應用程序提供了兩種抽象,一種是獨立的邏輯控制流,一種是私有地址空間。提高CPU執行效率,減少因程序等待造成的CPU空閑和其他計算機軟硬件資源的浪費。
6.2 簡述殼Shell-bash的作用與處理流程
(以下格式自行編排,編輯時刪除)
Shell是一個交互型應用及程序,代表用戶運行其他命令,基本作用是解釋并運行用戶的指令。
流程:
① 讀取用戶命令行輸入。
② 解析命令行字符串,獲取命令行參數并構建傳遞給execve函數argv向量。
③ 檢查第一個命令行參數是否是shell內置命令。
④ 否則fork創建子程序。
⑤ 在子進程中,繼續步驟(2)獲取參數并調用excve()運行程序。
⑥ 命令行末尾沒有&,代表前臺的工作,shell使用waitpid等待工作完成返回。
⑦ 命令行末尾有&,代表后臺工作,shell返回。
6.3 Hello的fork進程創建過程
(以下格式自行編排,編輯時刪除)
在終端中輸入 ./hello ,shell 將解釋命令行。 由于這不是一個內置的 shell 命令,所以調用fork來創建一個新的運行子進程并運行可執行程序 hello。 新創建的子進程幾乎(但不完全)與父進程相同。 子進程從父進程獲取用戶級虛擬地址空間的相同但獨立的副本。 所以當父進程調用fork時,子進程可以讀寫在父進程中打開的任何文件。
6.4 Hello的execve過程
調用execve函數:使用駐留在內存中的稱為loader的操作系統代碼加載并運行可執行目標文件hello,并映射私有區域創建新的程序代碼、數據、bss和堆棧區。 code區和data區映射到hello中的.text和.data區,bss請求二進制0,映射到匿名文件。 棧和堆請求二進制零,初始長度為0。
映射共享區域:最后更新PC,將下一條指令指向代碼區域的入口點即-start函數的地址。start函數調用系統啟動函數 _libc_start_main來初始化執行環境,并調用main函數,此時argv向量被作為參數傳遞給主函數。
只有當出現錯誤時, execve 才會返回到調用程序。所以execve 調用一次并從不返回。
6.5 Hello的進程執行
(以下格式自行編排,編輯時刪除)

進程的抽象:
獨立的邏輯控制流,好像進程獨占的使用CPU。
私有的地址空間,好像程序獨占的使用內存。
hello進程的執行依賴于進程的抽象:
① 邏輯控制流::程序計數器 PC 的一系列值序列,進程輪流使用處理器,在同一個處理器核心中,每個進程執行其一部分流后被暫時掛起(上下文轉換機制),然后處理器運行其他進程。
② 并發流:一個邏輯控制流的執行時間與另一個控制流重疊,成為并發流,這兩個流(宏觀上)并發的運行。
③ 時間片:一個進程執行其一部分控制流的一段時間叫做時間片。
④ 用戶模式和內核模式:處理器通常使用一個寄存器區分兩種模式的,該寄 存器記錄著當前進程享有的權限,沒有設置模式位時進程處于用戶模式, 用戶模式的進程不允許執行特權指令,也不允許直接引用內存空間中內核區內的代碼和數據;設置模式位時進程處于內核模式,該進程可以執行指令集中的任何命令,并且可以訪問內存中的任何地址。
⑤ 上下文信息:上下文就是內核重新啟動一個掛起的進程所需要的資源,包括通用寄存器、浮點寄存器、程序計數器、用戶棧、狀態寄存器、內核棧和各種內核數據結構等。
⑥ 上下文切換:在內核調用了一個新的進程并使其運行后,當前進程就會掛起,并使用上下文切換機制來將控制權轉移到新的進程。
1)保存被掛起進程的上下文到內存
2)啟用恢復進程保存的上下文,
3)將控制傳遞給新恢復的進程,來完成上下文切換。
在hello進程運行開始,shell已經fork后為hello程序分配了新的虛擬的地址空間,調用execve函數之后將hello的.txt和.data節分配到虛擬地址空間的代碼區和數據區。當hello運行在用戶模式下,輸出hello 1190202001 楊永正,然后hello調用sleep函數后進入內核模式,它會處理sleep請求主動掛起當前進程,并將hello進程加入等待隊列,sleep定時器開始計時,內核進行上下文切換并傳遞 控制當前進程到其他進程。當定時器倒計時結束,會發送一個中斷信號。此時進入內核態進行中斷處理,將hello進程從等待隊列中移除并重新加入運行隊列,hello進程繼續其控制邏輯流。

結合進程上下文信息、進程時間片,闡述進程調度的過程,用戶態與核心態轉換等等。
6.6 hello的異常與信號處理
異常:

信號(部分):

按下Ctrl+z會令內核發送一個SIGINT信號到前臺進程組的每個進程,默認情況是終止前臺作業。輸入ps可以看到后臺hello進程還在執行。

按下Ctrl+c會令內核發送一個SIGTSTP信號到前臺進程組的每個進程,默認情況是終止前臺作業。

程序運行過程中不停亂按可以發現輸入的內容只是將屏幕的輸入緩存到 stdin,當getchar()的時候讀取了回車前的一個字符,其他字符串串只會作為 shell 命令行輸入。
hello執行過程中會出現哪幾類異常,會產生哪些信號,又怎么處理的。
程序運行過程中可以按鍵盤,如不停亂按,包括回車,Ctrl-Z,Ctrl-C等,Ctrl-z后可以運行ps jobs pstree fg kill 等命令,請分別給出各命令及運行結截屏,說明異常與信號的處理。
6.7本章小結
第八章解釋了hello進程管理的定義和功能。同時介紹了Shell的一般處理流程,分析了調用fork創建新進程、調用execve函數執行hello、hello進程執行、hello異常和信號處理的相關知識。
(第6章1分)

第7章 hello的存儲管理
7.1 hello的存儲器地址空間
邏輯地址:用于確定機器語言中的指令或操作數的地址。一個邏輯地址包含一個段和一個偏移量,偏移量是一個相對偏移量,段決定了偏移量從哪里開始,這樣就可以通過段和偏移量來確定地址。即hello.o中的相對偏移地址
線性地址:邏輯地址和物理地址之間的橋梁。即虛擬內存地址,可以通過將偏移量與段地址相加得到。也就是hello中的虛擬內存地址
虛擬地址:虛擬地址與線性地址相同。也就是hello中的虛擬內存地址
物理地址:裝入內存地址寄存器的地址,內存單元的真實地址。通過地址轉換器,可以將hello的虛擬地址轉換為物理地址。
結合hello說明邏輯地址、線性地址、虛擬地址、物理地址的概念。

7.2 Intel邏輯地址到線性地址的變換-段式管理
(1) 實地址模式:在實地址模式下,處理器使2位地址總線訪問1MB(0~FFFFF)內存。 8086模式只有16位地址線,不能直接表示20位地址,采用內存分段方案。段地址存儲在 16 位段寄存器中(CS、DS、ES、SS)
(2)保護模式:在保護模式下,段寄存器存儲段描述符表中段描述符的索引值,稱為段選擇器。此時,CS存儲代碼段描述符的索引值,DS存儲數據段描述。符號的索引值,SS存放堆棧段描述符的索引值
RPL代表程序當前的優先級,TI位代表段描述符的位置,TI=0段描述符在GDT中,TI=1段描述符在LDT中
48位全局描述符表寄存器GDTR指向GDT,即GDT在內存中的具體位置,16位局部描述符表寄存器LDTR指向LDT段在GDT中的位置。唯一的全局描述符表GDT包含了操作系統使用的代碼段、數據段、堆棧段的描述符,每個程序的LDT段,每個程序都有一個獨立的局部描述符表LDT,其中包含了操作系統的私有代碼段對應的程序、數據段、堆棧段描述符、對應程序使用的門描述符:任務門、調用門等。
在保護模式下:線性地址=基地址(由段描述符給出)+偏移量
7.3 Hello的線性地址到物理地址的變換-頁式管理
每個線性地址被分解為一個 10 位面目錄索引、一個 10 位頁表和一個 12 位索引偏移量:
然后根據線性地址的前十位找到索引項,根據頁索引項確定頁表的地址。根據線性地址的中間十位,在頁表中找到該頁的起始地址。將頁面的起始地址與線性地址的最后 12 位相加,得到最終的物理地址。
頁面管理就是把物理空間分成很多塊。邏輯地址空間被相應地劃分為許多頁號和頁地址,形成一個線性地址。我們通過頁碼找到頁的起始地址,加上頁地址得到物理地址。
線性地址分為兩部分,VPO與VPN。
VPN作為索引查詢頁表中的部分物理地址。
在 VPO 中找到的物理地址和頁表結合起來形成一個完整的物理地址。
使用 VPN 作為索引查找物理地址時會發生兩件事。
頁面命中:
1、處理器產生一個虛擬地址并傳送給MMU。
2、MMU生成PTE地址并向緩存或主存請求。
3、緩存或主存將PTE返回給MMU。
4、 MMU構造一個物理地址并發送給緩存或主存。
5、緩存或主存將請求的數據返回給處理器。
缺頁:
1、處理器產生一個虛擬地址并傳送給MMU。
2、MMU 生成 PTE 地址并向緩存或主存請求。
3、緩存或主存將PTE返回給MMU。
4、PTE中的有效位為0,MMU解析一次異常,將CPU的控制權交給操作系統內核中的缺頁異常處理程序。
5、頁面錯誤處理程序確定物理內存的犧牲,如果它已被修改,則將其換出到磁盤。
6、頁面錯誤處理程序被調用到一個新的頁面中,并更新內存中的PTE。
7、如果處理程序返回到原來的進程,再次執行導致頁錯誤的指令。 CPU 將導致頁面錯誤的指令地址重新發送到 MMU。對于這樣的命中,主存儲器將請求的字返回給處理器。
7.4 TLB與四級頁表支持下的VA到PA的變換
(以下格式自行編排,編輯時刪除)
36位VPN 被劃分成四個9 位的片,每個片被用作到一級頁表的偏移量。CR3 寄存器包含頁目錄表的物理地址。VPN1提供在Ll中的偏移量,這PTE包含L2頁表的基地址。VPN2提供在Ll中的偏移量,以此類推即可得到物理頁號,與虛擬地址VPO相連即可得到物理地址。

7.5 三級Cache支持下的物理內存訪問
(以下格式自行編排,編輯時刪除)
每一級Cache都分為S個組,每組都有E行,每一行有B個字節的塊,以及一位有效位,64位的tag標記。
對物理地址值而言分為組索引,tag標記,以及塊偏移。
首先對地址值的組索引找到相對應的Cache中的對應組。
然后根據tag標記找到符合tag標記的那一行。
如果有效值為1,則命中,在這一行中讀或寫(寫回)取相對應塊索引的數據
否則不命中,同理從下一級cache執行相同操作,未命中則再到下一級去找,如果三級cache都找不到則在內存中找到相對應的這一行,根據地址值對應的組加入緩存中,選擇有效位為0的行優先放入,否則按照LRU算法替換緩存行。
7.6 hello進程fork時的內存映射
(以下格式自行編排,編輯時刪除)
內存映射為每個進程創建一個獨立的虛擬地址空間。當一個進程調用 fork 函數時,內核會為新進程創建各種數據結構,并為其分配一個唯一的 PID。通過創建進程得到mm_struct,區域結構與頁表的原始副本,他將兩個進程中的每個頁面標記為只讀,并將兩個進程中的每個區域結構標記為copy-on-write。
當fork在新進程中返回時,新進程現在的虛擬內存與調用fork時存在的虛擬空間內存是一樣的。當兩個進程之一想要寫入其私有部分時,就會觸發只讀保護并觸發故障處理程序。把實現私有的寫時復制,回到原程序的當前指令,執行寫操作。對于共享部分,不同進程的不同虛擬頁面會映射到內存的同一個物理頁面。
7.7 hello進程execve時的內存映射
(以下格式自行編排,編輯時刪除)
execve 函數調用駐留在內核區的引導加載程序代碼,在當前進程中加載并運行可執行目標文件 hello 中包含的程序,有效地將當前程序替換為 hello 程序。加載和運行 hello 需要以下步驟:
1)刪除已有的用戶區,刪除當前進程虛擬地址的用戶部分中已有的區結構。
2)映射私有區域,為新程序的代碼、數據、bss和堆棧區域創建新的區域結構。所有這些新區域都是私有的,并在書面上復制。代碼和數據區域映射到 hello 文件中的 .text 和 .data 區域。bss 區域用于二進制零并映射到匿名文件。它的大小包含在你好。堆棧和堆地址也用于二進制零,初始長度為零。
3)映射共享區,hello程序鏈接共享對象libc.so,libc.so動態鏈接到這個程序,然后映射到用戶虛擬地址空間中的共享區。
4)設置程序計數器(PC),execve做的最后一件事是設置當前進程上下文的程序計數器,使其指向代碼區的入口點。
7.8 缺頁故障與缺頁中斷處理
(以下格式自行編排,編輯時刪除)
1、缺頁:使DRAM緩存漏掉一個page fault
2、缺頁故障:當cpu引用的虛擬地址所在的虛擬頁的PTE有效位為0時,即對應的虛擬頁不再在內存中時,會觸發缺頁異常。
3、缺頁中斷處理:缺頁異常調用內核的缺頁異常處理程序。 程序選擇內存中的一頁作為犧牲頁,如果該頁被修改(修改位被設置),則將該頁寫回磁盤; 然后根據目標虛擬頁的PTE的磁盤地址,取出磁盤的頁放入內存,同時修改PTE。然后在程序中斷時返回當前指令, 并繼續請求訪問虛擬地址。

7.9動態存儲分配管理
動態內存分配器維護著一個進程的虛擬內存區域,稱為堆(heap)。系統之間細節不同,但是不失通用性,假設堆是一個請求二進制零的區城,它緊接在未初始化的數據區域后開始,并向上生長(向更高的地址)。對于每個進程,內核維護著一個變量brk(讀做“break”),它指向堆的頂部。
分配器將堆視為一組不同大小的塊(block)的集合來維護。每個塊就是一個連續的虛擬內存片(chunk),要么是已分配的,要么是空閑的。已分配的塊顯式地保留為供應用程序使用??臻e塊可用來分配。空閑塊保持空閑,直到它顯式地被應用所分配。一個已分配的塊保持已分配狀態,直到它被釋放,這種釋放要么是應用程序顯式執行的,要么是內存分配器自身隱式執行的。
分配器有兩種基本風格:顯式分配器和隱式分配器。兩種風格都要求應用顯式地分配塊。它們的不同之處在于由哪個實體來負責釋放已分配的塊。
顯式分配器(explicit allocator),要求應用顯式地釋放任何已分配的塊。例如,C標準庫提供一種叫做malloc程序包的顯式分配器。C程序通過調用malloc函數來分配一個塊,并通過調用free函數來釋放一個塊。C++中的new和delete操作符與C中的malloc和free相當。
隱式分配器(implicit allocator),另一方面,要求分配器檢測一個已分配塊何時不再被程序所使用,那么就釋放這個塊。隱式分配器也叫做垃圾收集器(garbage colleetor),而自動釋放未使用的已分配的塊的過程叫做垃圾收集(garbage collection)。例如,諸如Lisp、ML.以及Java之類的高級語言就依賴垃圾收集來釋放已分配的塊。
Printf會調用malloc,請簡述動態內存管理的基本方法與策略。
帶邊界標簽的隱式空閑鏈表表示塊的邊界、已分配塊和空閑塊的方法如圖:

一個塊是由頭部、腳部、有效載荷,以及可能的填充組成。頭部編碼表示了這個塊的大小(包括頭部和所有的填充)和這個塊的分配狀態(最后一位)。
頭部后面是應用malloc時請求的有效載荷。有效載荷后面是一片不使用的填充塊,其大小可以是任意的。結尾的腳部是頭部的一個副本。塊的格式如圖所示,空閑塊通過頭部塊的大小字段隱含的連接著,所以我們稱這種結構就隱式空閑鏈表。
(1)放置已分配的塊
當一個進程申請一個n字節的內存空間時,分配器搜索空閑鏈表。查找一個大小可以放置所申請空間的空閑塊。搜索方式的常見策略是首次適配、下一次適配和最佳適配。
(2)分割空閑塊
一旦分配器找到一個匹配的空閑塊,就必須將空閑塊分割為兩部分。第一部分變為了已分配塊,第二部分變為了空閑塊。
(3)獲取額外堆內存
如果分配器不能找到找到合適的空閑塊,可以合并那些相鄰的空閑塊,如果還不能生成一個足夠大的塊,分配器就會調用sbrk函數,向內核申請額外的內存。
(4)合并空閑塊
合并的情況一共分為四種:前空后不空,前不空后空,前后都空,前后都不空。對于四種情況分別對空閑塊進行合并,只需要通過改變頭部的信息就能完成合并空閑塊。

7.10本章小結
(以下格式自行編排,編輯時刪除)
第七章主要講述了虛擬內存的知識,虛擬內存地址和物理內存地址的轉換,TLB和頁表的結構和查找原理,以及如何使用物理地址通過三級緩存訪問內存,以及 還學習了fork和execve內存映射,以及動態存儲分配管理的原理和實現。
(第7章 2分)

第8章 hello的IO管理
8.1 Linux的IO設備管理方法
設備的模型化:文件
設備管理:unix io接口
一個 Linux 文件是一個 m 字節的序列,所有的 I/O 設備(如網絡、磁盤和終端)都被建模為文件。并且所有的輸入和輸出都被認為是對相應文件的讀取和寫入并執行。這種將設備映射到文件的方法允許 Linux 內核在稱為 Unix I/O 上運行一個簡單的低級應用程序接口,它允許輸入和輸出以一致和一致的方式運行。
應用程序通過要求內核打開相應的文件來聲明它要訪問 I/O 設備。內核返回一個小的非負整數稱為描述符,文件的關聯數據由內核保存,應用程序只需要保存這個描述符。
Linux shell 創建的每個進程都包含三個文件:
標準輸入、標準輸出和標準錯誤以供操作時使用
對于每個打開的文件內核維護文件位置 k,它從 0 開始,它是從文件開頭的字節偏移量。應用程序可以通過執行搜索顯式更改該值。
對于讀操作,從文件復制n個字節到內存,文件位置k增加到k+n,當k大于等于文件大小時,觸發EOF條件,即結束文件被讀取。
最后,在文件訪問結束后。該文件將被內核關閉。內核釋放文件打開時創建的數據結構,并將描述符恢復到現有的描述符池中。

8.2 簡述Unix IO接口及其函數
Unix I/O 接口:
(1)打開文件:一個應用程序同步異常(陷阱)請求內核打開某文件,表示其要訪問一個 I/O 設備,內核返回一個小的非負整數(描述符),然后對此文件的所有操作中標識這個文件,內核把有關這個文件的所有信息進行記錄。
(2)Shell創建的每個進程都有三個打開的文件:標準輸入,標準輸出,標準錯誤。
(3)更改當前的文件位置:對于已打開的某個文件,內核保存著一個文件位置,初始化為0,是從文件開頭起始的字節偏移量是這個文件的位置,應用程序能夠通過執行seek函數更改當前文件位置。
(4)讀寫文件:對于讀操作,從文件復制n個字節到內存,文件位置k增加到k+n,當k大于等于文件大小時,觸發EOF條件,即結束文件被讀取。同理寫操作就是從內存中復制 n>0 個字節到一個文件,從當前文件位置k開始,寫完成后更新 k。
(5)關閉文件:內核釋放文件打開時創建的數據結構,并將描述符恢復到現有的描述符池中。
Unix I/O 函數:
(1)int open(char* filename,int flags,mode_t mode) ,通過調用open函數打開現有文件或創建新文件的過程。open函數將文件名轉換為文件描述符并返回描述符編號。返回的描述符始終是進程標志中未打開的最小描述符參數。指定進程打算如何訪問文件。 mode 參數指定新的文件訪問權限位。
(2)int close(fd),fd 是需要關閉的文件的描述符,close 返回操作結果(一個整型數)。
(3) ssize_t read(int fd,void *buf,size_t n),read函數從當前帶有fd描述符的文件位置到buf內存位置最多分配n個字節,返回值-1表示錯誤,0表示EOF;否則返回值表示實際傳輸的字節數。
4) ssize_t wirte(int fd,const void *buf,size_t n),write 函數從內存位置 buf 復制至多 n 個字節到當前描述符為 fd 的文件位置。
8.3 printf的實現分析
(以下格式自行編排,編輯時刪除)
https://www.cnblogs.com/pianist/p/3315801.html
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;
}
printf程序按照格式fmt結合參數args生成字符串,并返回串的長度。
然后是write函數:
write:
mov eax, _NR_write
mov ebx, [esp + 4]
mov ecx, [esp + 8]
int INT_VECTOR_SYS_CALL
在printf中調用系統函數write將長度為i的buf輸出,在write函數中,將棧中參數放入寄存器,ecx是字符個數,ebx存放第一個字符地址。
int INT_VECTOR_SYS_CALLA表示通過調用系統syscall。
然后是sys_call的實現:
sys_call:
call save
push dword [p_proc_ready]
sti
push ecx
push ebx
call [sys_call_table + eax * 4]
add esp, 4 * 3
mov [esi + EAXREG - P_STACKBASE], eax
cli
ret
sys_call函數通過總線將字符串中的字節從寄存器復制到顯卡的顯存。顯存存儲ASCII字符碼,字符顯示驅動子程序通過ASCII碼在字體庫中查找點陣信息,將點陣信息存儲在vram中。
顯示芯片根據刷新頻率逐行讀取vram。并通過信號線將每個點(RGB分量)發送到液晶顯示器。所以我們的輸入字符串出現在屏幕上。從vsprintf生成顯示數據,寫系統函數,int 0x80攔截系統調用,或者sys_call字符顯示驅動子程序:從ASCII到字體庫顯示vram。 (采集每個點的RGB顏色數據)
顯示芯片根據刷新頻率逐行讀取vram。并通過vsprintf的信號線將每個點(RGB分量)發送到液晶顯示器,生成顯示數據。編寫write函數,然后到陷阱-系統調用int 0x80 或 sys_call 等。
字符顯示驅動子程序:從ASCII到字體庫顯示vram(存儲每個點的RGB顏色數據),顯示芯片相應地逐行讀取vram 刷新頻率并通過信號線將每個點(RGB分量)發送到液晶顯示器。
8.4 getchar的實現分析
(以下格式自行編排,編輯時刪除)
getchar 的源代碼為:
int getchar(void)
{
static char buf[BUFSIZ];
static char *bb = buf;
static int n = 0;
if(n == 0)
{
n = read(0, buf, BUFSIZ);
bb = buf;
}
return(–n >= 0)?(unsigned char) *bb++ : EOF;
}
異步異常-鍵盤中斷的處理:當用戶按下一個鍵時,鍵盤接口會得到一個代表該鍵的鍵盤掃描碼,同時產生一個中斷請求。 中斷請求搶占當前進程運行鍵盤中斷子程序。 鍵盤中斷子程序首先從鍵盤接口獲取按鍵的掃描碼。 然后將按鍵掃描碼轉換成ASCII碼保存在系統的鍵盤緩沖區中。
getchar等調用read系統函數,通過系統調用讀取按鍵ascii碼,直到接受到回車鍵才返回。
8.5本章小結
第八章講述了IO設備的管理方法,IO接口及其函數,最后分析了printf和getchar函數的實現方法。(第8章1分)

結論
用計算機系統的語言,逐條總結hello所經歷的過程。
你對計算機系統的設計與實現的深切感悟,你的創新理念,如新的設計與實現方法。
編寫:通過編輯器將代碼輸入hello.c。
預處理:將hello.c調用的所有外部庫和宏替換預處理、擴展和合并到一個hello.i文件中。
編譯:將hello.i編譯成匯編文件hello.s。
匯編:把hello.s匯編成可重定位的目標文件hello.o。
鏈接:將hello.o與可重定位的目標文件和動態鏈接庫鏈接成可執行的目標程序hello。
運行:在shell命令行中輸入./hello 1190202001 楊永正 1。
創建子進程:shell進程調用fork為其創建子進程。
運行程序:shell調用execve,execve調用loader,添加映射的虛擬內存,程序進入程序入口后開始加載物理內存,然后進入main函數。
執行指令:CPU為其分配時間片,在一個時間片內,hello可以使用CPU,依次執行自己的控制邏輯流。
上下文切換:hello調用sleep函數之后進程進入內核模式,內核進行上下文切換將當前進程的控制權交給其他進程,當sleep函數調用完成時,內核執行上下文切換將控制返還給hello進程。
訪問內存:MMU將程序中使用的虛擬內存地址通過頁表映射到物理地址。
動態內存申請:printf調用malloc動態內存分配器在堆中申請內存。
信號:如果在運行時輸入ctr-c ctr-z,會分別調用shell的信號處理函數終止和停止。
結束:shell父進程或ini養父進程回收子進程,內核刪除為這個進程創建的所有數據結構。
計算機系統的設計思想和實現是基于抽象實現的:抽象體現在:用二進制01表示的最低層信息,操作系統管理硬件,進程是處理器、主存和I/O設備的抽象。虛擬內存是主內存與磁盤設備聯系的抽象等。
計算機系統的設計是統籌兼顧的:計算機系統的設計考慮了所有可能的實際情況,并相應地設計了一系列的處理方法來適應不同的情況。
計算機系統設計精巧:為了解決快設備小存儲與大存儲設備慢存儲的不平衡,設計了Cache和TLB等緩存設備作為下層存儲設備的緩存,很大程度上提高了CPU運行的速度。

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

附件
列出所有的中間產物的文件名,并予以說明起作用。
hello.c:c源代碼文件
hello.i:預處理后的文件
hello.s:編譯后的匯編文件
hello.o:匯編后的匯編可重定位文件
hello:鏈接后生成的可執行文件
hello.elf: hello.o的ELF文件
hellold.elf: hello.out的ELF文件
(附件0分,缺失 -1分)

參考文獻
為完成本次大作業你翻閱的書籍與網站等
[1] 蘭德爾E.布蘭恩特 大衛R.奧哈拉倫 深入理解計算機系統(第三版)
[2] https://www.cnblogs.com/pianist/p/3315801.html
(參考文獻0分,缺失 -1分)

總結

以上是生活随笔為你收集整理的HIT CSAPP 大作业的全部內容,希望文章能夠幫你解決所遇到的問題。

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