環境:ubuntu64位 vmware虛擬機環境中運行html
gdblinux
edbshell
ida pro編程
visual studioubuntu
hexedit數組
notepad++緩存
objdumpbash
hello.c源程序app
hello.i預處理後的源程序異步
hello.s彙編代碼
hello.o目標程序
hello可執行文件
elfo.txt連接前的elf文件信息
elfe.txt連接後的elf文件信息
asmo.txt hello.o反編譯結果
asme.txt hello反編譯結果
題太多了,太累了QAQ
(第1章0.5分)
概念:在編譯以前進行的處理。
做用:1.宏定義2.文件包含3.條件編譯
gcc hello.c -E
hello.c程序中只包含三條預處理指令
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
做用是文件包含,即包含stdio.h,unistd.h,stdlib.h三個文件
經過-E指令,只激活預處理指令,能夠看到執行完這三條文件包含指令的結果:將三個文件的內容引入,代碼量提高至約800行(詳見hello.i)
預處理命令,能夠在編譯器編譯以前,提早進行一些操做,好比定義常量,還能夠進行條件編譯以方便調試,能夠進行文件引入來導入一些預先寫好的模塊,便於程序的組織和調試和一些特殊的編程技巧的實現,是一項很是有用的功能。
(第2章0.5分)
編譯(compilation , compile)
利用編譯程序從源語言編寫的源程序產生目標程序的過程。
做用:
把用高級程序設計語言書寫的源程序,翻譯成等價的計算機彙編語言
gcc hello.i -S
該句定義一個int類型全局變量sleepsecs,其值爲2(向整數向下取整後的結果),在僞彙編文件中,先定義一個全局符號sleepsecs,用於標識和鏈接。
.globl sleepsecs
在.data指令後,描述一些該變量的詳細信息
.type sleepsecs, @object 將sleepsecs定義爲對象類型
.size sleepsecs, 4 佔用4個字節
sleepsecs: 定義sleepsecs對應的標籤
.long 2 定義爲長整型數2
.text 在text節中定義
.globl main 聲明全局符號global
.type main, @function 將main聲明爲函數
隨後定義main:標籤,後跟main函數僞彙編指令
這是一個未初始化的局部變量,它存放在棧中,位置是-4(%rbp)
cmpl $3, -20(%rbp)
je .L2
argc做爲main的參數,存放在-20(%rbp)的位置,將它與3比較,若是相等就不執行後面大括號的部分
movl $.LC0, %edi
call puts
將.LC0的部分傳入參數,執行printf
.LC0定義在.rodata節中,是一個字符常量
.LC0:
.string "Usage:Hello\345\255\246\345\217\267\345\247\223\345\220\215\357\274\201"
movl $1, %edi
call exit
參數爲1,執行exit函數
.L2:
movl $0, -4(%rbp)
jmp .L3
該節初始化i爲0跳轉到判斷節
.L3:
cmpl $9, -4(%rbp)
jle .L4
若是知足條件就跳轉、繼續執行
addl $1, -4(%rbp)
循環變量遞增
movq -32(%rbp), %rax
addq $16, %rax
movq (%rax), %rdx
將argv[2]傳給%rdx
movq -32(%rbp), %rax
addq $8, %rax
movq (%rax), %rax
movq %rax, %rsi
將argv[1]傳給%rsi
movl $.LC1, %edi
將字符串常量"Hello %s %s\n"傳給%edi
movl $0, %eax
將%eax賦0
call printf
執行printf
(如下格式自行編排,編輯時刪除)
movl sleepsecs(%rip), %eax
movl %eax, %edi
call sleep
將sleep傳給%edi做爲參數,執行sleep函數
call getchar
執行getchar函數
leave
釋放堆棧空間
ret
返回
本節涉及到的指令所有爲gun彙編程序(gas)的僞彙編指令,相比最後的彙編指令內容更爲精簡,方便閱讀、分析。程序將常量放入.rodata節,初始化全局變量放入.data節,經過標籤訂義和跳轉等方式定義許多操做,爲後序的彙編和連接生成可執行文件準備。
(第3章2分)
概念:
把彙編語言翻譯成機器語言的過程稱爲彙編
做用:
將彙編語言翻譯成機器語言
gcc -c hello.s
分析hello.o的ELF格式,用readelf等列出其各節的基本信息,特別是重定位項目分析。
各Section基本信息:
Name:名稱
Type:類型
Address:地址
Offset:地址偏移量
Size:大小
EntSize:全體大小
Flag:旗標
Link:被重定位的符號所在的符號表的section index
Info:須要被重定位的section的index
Align:對齊信息
包含重定位信息的節
offset表示該符號在被重定位的section中的偏移
info的高4個字節表示該符號在.symtab中的index,低4字節表示重定位的類型
type表示符號類型
sym.value表示連接過程當中將要寫入地址的位置
sym.name表示符號名稱
append追加地址
機器語言是直接用二進制代碼指令表達的計算機語言,指令是用0和1組成的一串代碼,它們有必定的位數,並分紅若干段,各段的編碼表示不一樣的含義。
每一條彙編語句被映射爲若干二進制指令碼,將機器語言的每一條指令符號化:指令碼代之以記憶符號,地址碼代之以符號地址。
在彙編語言中,操做數用十進制表示,而在機器語言中,用十六進制表示,如hello.o中:
機器語言中命令
4: 48 83 ec 20 sub $0x20,%rsp
在彙編語言hello.s中對應爲
subq $32, %rsp
objdump -d -r hello.o 分析hello.o的反彙編,並請與第3章的 hello.s進行對照分析。
在彙編語言中,分支跳轉、函數調用,使用標籤訂位位置,而在機器語言中使用地址+偏移量計算要跳轉到的實際地址。
如hello.o中:
6f: 7e c1 jle 32 <main+0x32>
71: e8 00 00 00 00 callq 76 <main+0x76>
在hello.s中對應爲:
jle .L4
call getchar
彙編是將計算機不能讀懂的彙編語言翻譯成計算機能讀懂的機器語言的不可缺乏的重要步驟.
(第4章1分)
概念:
連接是將各類代碼和數據片斷收集並組合成一個單一文件的過程,這個文件可被加載(複製)到內存並執行。
做用:
連接在軟件開發中扮演着一個關鍵的角色,由於它使分離編譯成爲可能。
ld -o hello -dynamic-linker /lib/ld-linker.so.2 /usr/lib/crt1.o /usr/lib/crti.o -l hello.o /usr/lib/ctrn.o
offset:偏移量
virtaddr:虛擬地址
phyaddr:物理地址
filesiz:文件中的大小
memsiz:內存中的大小
flags:旗標
align:對齊
實際運行中,全部虛擬地址空間段大小都爲0x1000,且一段中包含一個或多個5.3中的程序段。動態連接庫中的文件映射到內存的內容,與hello文件中映射到內存的內容地址間隔較大。程序中還包括[stack],[vvar],[vdso],[vsyscall]等特殊用途的地址段。
不一樣:
hello中包含一些外部文件的宏定義、變量、庫函數和操做系統的啓動代碼等,且.o文件.text節從0開始,而可執行文件.text節並不是從0開始。
過程:分爲符號解析和重定位兩步
重定位:
hello.o的文件中包含一些重定位條目
這些重定位條目告訴連接器32位PC相對地址或32位絕對地址進行重定位,這些重定位條目經過計算地址或直接調用保存的絕對地址,達到重定位的目的。
不管什麼時候彙編器遇到對最終位置未知的目標引用,會生成一個重定位條目,告訴連接器在將目標文件合併成可執行文件時如何修改這個引用。代碼的重定位條目放在.rel.text中,已初始化數據的條目放在.rel.data中
_start
__libc_start_main
__GI___cxa_atexit
__internal_atexit
__GI___cxa_atexit
__internal_atexit
__new_exitfn
__internal_atexit
__GI___cxa_atexit
__libc_start_main
_setjmp
__sigsetjmp
__sigjmp_save
__libc_start_main
__GI_exit
__run_exit_handlers
__GI___call_tls_dtors
__run_exit_handlers
__do_global_dtors_aux
deregister_tm_clones
__do_global_dtors_aux
_fini
__run_exit_handlers
_IO_cleanup
_IO_unbuffer_all
_IO_cleanup
_IO_flush_all_lockp
_IO_cleanup
_IO_unbuffer_all
_IO_cleanup
_IO_unbuffer_all
_IO_new_file_setbuf
_IO_default_setbuf
_IO_new_file_sync
_IO_default_setbuf
__GI__IO_setb
_IO_default_setbuf
__GI__IO_setb
_IO_default_setbuf
_IO_new_file_setbuf
_IO_unbuffer_all
_IO_new_file_setbuf
_IO_default_setbuf
_IO_new_file_sync
_IO_default_setbuf
__GI__IO_setb
_IO_default_setbuf
__GI__IO_setb
_IO_default_setbuf
_IO_new_file_setbuf
_IO_unbuffer_all
_IO_cleanup
__run_exit_handlers
__GI__exit
動態連接項目以下圖(主要爲兩個.so文件相關內容)
分析GOT的變化
dl_init前:
dl_init後
0x6010208~0x601020d字節發生了變化
(我的認爲,在全書中,「連接」這一章的難度比全部其餘章節之和還要大,搞清楚動態連接的全過程十分困難,課本上也有意的跳過了一些部分,致使一些細節問題十分費解。)
連接是組建大型程序和團隊編程不可缺乏的重要部分,掌握連接器的一些原理和動態連接是很是有必要的,也是學習庫打樁等強大機制的基礎。雖然hello.c很簡單,可是也須要和標準庫進行連接。瞭解hello.c連接的前因後果,對掌握連接技術頗有幫助。
(第5章1分)
概念:
進程(Process)是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操做系統結構的基礎。在早期面向進程設計的計算機結構中,進程是程序的基本執行實體;在當代面向線程設計的計算機結構中,進程是線程的容器。程序是指令、數據及其組織形式的描述,進程是程序的實體。
做用:
因爲程序是靜態的,咱們看到的程序是存儲在存儲介質上的,它沒法反映出程序執行過程當中的動態特性,並且程序在執行過程當中是不斷申請資源,程序做爲共享資源的基本單位是不合適的,因此須要引入一個概念,它能描述程序的執行過程並且能夠做爲共享資源的基本單位,這個概念就是進程。進程解決了系統資源調度等一系列問題。
因此Shell-bash的「做用」是linux系統的一個命令行解釋器
流程:
shell調用fork函數,造成自身的一個拷貝(子進程),爲運行hello作準備
在shell的子進程中執行execve函數,將參數傳給Hello程序,並執行Hello
一開始,Hello運行在用戶模式,當程序收到一個信號時,進入內核模式,運行信號處理程序,以後再返回用戶模式。在Hello運行的過程當中,cpu不斷切換上下文,使Hello程序運行過程被切分紅時間片,與其餘進程交替佔用cpu,實現進程的調度。
1.輸入Ctrl-C時,程序終止
處理過程是向程序發送SIGINT信號,程序執行默認行爲:中止執行
2.輸入Ctrl-C時,程序掛起
處理過程是向程序發送SIGSTP信號,程序執行默認行爲:掛起程序,以後會返回shell中
輸入的內容會被留在緩衝區中,當hello執行結束,返回shell中,shell會從緩衝區讀取並嘗試解析這些內容
ps:顯示當前進程的狀態
jobs:查看後臺運行的進程
fg:恢復一個後臺進程
pstree:顯示進程樹
kill:結束一個進程
可能會產生IO中斷、時鐘中斷、系統調用等等,會產生SIGINT、SIGSTP等信號。
。
linux命令行shell是一個很是強大的工具,用它能夠更方便的執行Hello和發送各類命令請求。經過信號等方式能夠實現異常處理,讓Hello在順序執行者也能處理一些突發情況和實現一些功能。進程調度實現了各個進程計算資源合理分配,互不干擾,提升了系統穩定性和效率。
(第6章1分)
物理地址(physical address)
用於內存芯片級的單元尋址,與處理器和CPU鏈接的地址總線相對應。
邏輯地址(logical address)
邏輯地址指的是機器語言指令中,用來指定一個操做數或者是一條指令的地址。如Hello中sleepsecs這個操做數的地址。
線性地址(linear address)或也叫虛擬地址(virtual address)
跟邏輯地址相似,它也是一個不真實的地址,若是邏輯地址是對應的硬件平臺段式管理轉換前地址的話,那麼線性地址則對應了硬件頁式內存的轉換前地址。
在x86保護模式下,段的信息(段基線性地址、長度、權限等)即段描述符佔8個字節,段信息沒法直接存放在段寄存器中(段寄存器只有2字節)。Intel的設計是段描述符集中存放在GDT或LDT中,而段寄存器存放的是段描述符在GDT或LDT內的索引值(index)。
爲了節約頁表佔用的內存空間,x86將線性地址經過頁目錄表和頁表兩級查找轉換成物理地址。32位的線性地址被分紅3個部分:最高10位 Directory 頁目錄表偏移量,中間10位 Table是頁表偏移量,最低12位Offset是物理頁內的字節偏移量。頁目錄表的大小爲4k(恰好是一個頁的大小),包含1024項,每一個項4字節(32位),項目裏存儲的內容就是頁表的物理地址。若是頁目錄表中的頁表還沒有分配,則物理地址填0。頁表的大小也是4k,一樣包含1024項,每一個項4字節,內容爲最終物理頁的物理內存起始地址。
每一個活動的任務,必需要先分配給它一個頁目錄表,並把頁目錄表的物理地址存入cr3寄存器。頁表能夠提早分配好,也能夠在用到的時候再分配。
先訪問一級緩存,不命中時訪問二級緩存,再不命中訪問三級緩存,再不命中訪問主存,若是主存缺頁則訪問硬盤
執行新進程(hello)時,爲這個新進程建立虛擬內存
在新進程中返回時,新進程擁有與調用fork進程相同的虛擬內存, 隨後的寫操做經過寫時複製機制建立新頁面
(如下格式自行編排,編輯時刪除)
缺頁故障:須要訪問的頁不在主存,須要操做系統將其調入後才能訪問。
有三種狀況:
只有正常缺頁時,系統纔會調入須要訪問的頁,並再次執行訪問該頁的命令。
基本方法:維護一個虛擬內存區域「堆」,將堆視爲一組不一樣大小的 塊(blocks)的集合來維護,每一個塊要麼是已分配的,要麼是空閒的,須要時選擇一個合適的內存塊進行分配。
經過高速緩存、虛擬內存、動態內存分配,能夠實現快速、高校、利用率高的儲存空間管理。能夠經過內存映射等方式實現文件共享。儲存管理是一個至關重要、值得研究的機制。
(第7章 2分)
設備的模型化:將設備抽象成文件
設備管理:經過unix io接口管理
1.從vsprintf生成顯示信息到write系統函數,到陷阱-系統調用 int 0x80或syscall.
2.字符顯示驅動子程序:從ASCII到字模庫到顯示vram(存儲每個點的RGB顏色信息)。
3.顯示芯片按照刷新頻率逐行讀取vram,並經過信號線向液晶顯示器傳輸每個點(RGB份量)。
1.異步異常-鍵盤中斷的處理:鍵盤中斷處理子程序。接受按鍵掃描碼轉成ascii碼,保存到系統的鍵盤緩衝區。
2.getchar等調用read系統函數,經過系統調用讀取按鍵ascii碼,直到接受到回車鍵才返回。
輸入輸出看似簡單,實際是一個很是精巧的過程,從程序發出請求到系統函數調用到設備相應,須要執行許多步驟,每每也是拖慢程序的主要因素和一些崩潰異常的高發地,須要謹慎選用函數、命令實現目的。
(第8章1分)
hello的源碼hello.c文件,要生成可執行文件,首先要進行預處理,其次要進行編譯生成彙編代碼,接着進行彙編處理生成目標文件,目標文件經過連接器造成一個可執行文件,可執行文件須要一個執行環境,它能夠在linux下經過shell進行運行,與計算機其餘常常文件同步運行,並經過異常處理機制相應信號。在運行的過程當中,程序經過Intel內存管理機制一步步訪問邏輯地址、虛擬地址、物理地址,從而進行數據交換,還能夠經過IO機制進行輸入輸出交互。
經過學習ics這門課程,深感計算機這個龐大致系的複雜、精巧,從電路到電路組合,再到硬件集成、軟件調配,每一處都層次分明、隨處顯現着前人的智慧。很多複雜概念的學習,經過這門課感受僅僅只是入了個門,距離熟練運用甚至涉及差距仍然較大,但不妨礙進行一些思惟上的創新。
目前的電子計算機創建在二進制基礎上,未來基礎物理突破以後,可能會實現三進制、四進制的計算機,沒準能夠實現質的飛越。或許神經科學有了巨大飛越,讓「電腦」真正構建起一個相似於人腦的神經結構,實現一個強的人工智能。
爲完成本次大做業你翻閱的書籍與網站等
[1]《深刻理解計算機系統》
[2] 哈工大計算機系統課程講課ppt
[3] [轉]printf 函數實現的深刻剖析http://www.javashuo.com/article/p-fbeaelqg-y.html
[4] 【不周山之讀薄 CSAPP】肆 連接 https://wdxtub.com/2016/04/16/thin-csapp-4/
[5] Linux下庫函數動態連接過程分析-結合glibc-2.11源碼 https://blog.csdn.net/lzshlzsh/article/details/6066628
[6] Linux下 可視化 反彙編工具 EDB 基本操做知識 http://www.javashuo.com/article/p-fpntsgri-gm.html
[7] gdb基本命令(很是詳細)
https://blog.csdn.net/z15818264727/article/details/69668820
[8] Linux中查看進程的虛擬地址空間內存佈局 https://blog.csdn.net/ASJBFJSB/article/details/81429576
[9] GNU ARM 彙編僞指令(Assembler Directives) https://blog.csdn.net/liuzq/article/details/83615085
[10] GCC經常使用參數詳解 https://www.cnblogs.com/zhangsir6/articles/2956798.html