《嵌入式Linux記憶體使用與效能優化》筆記
這本書有兩個關切點:系統記憶體(使用者層)和效能優化。
這本書和Brendan Gregg的《Systems Performance》相比,無論是技術層次還是更高的理論都有較大差距。但是這不影響,快速花點時間簡單過一遍。
然後在對《Systems Performance》進行詳細的學習。
由於Ubuntu測試驗證更合適,所以在Ubuntu(16.04)+Kernel(4.10.0)環境下做了下面的實驗。
全書共9章:1~4章著重於記憶體的使用,儘量降低程序的記憶體使用量,定位和發現記憶體洩露;5~9章著重於如何讓系統性能優化,提高執行速度。
使用者空間的記憶體使用量是由程序使用量累積和系統使用之和,所以優化系統記憶體使用,就是逐個攻克每個程序的使用量和優化系統記憶體使用。。
俗話說“知己知彼,百戰不殆”,要優化一個程序的使用量,首先得使用工具去評估記憶體使用量(第1章 記憶體的測量);
然後就來看看程序那些部分耗費記憶體,並針對性進行優化(第2章 程序記憶體優化);
最後從系統層面尋找方法進行優化(第3章 系統記憶體優化)。
記憶體的使用一個致命點就是記憶體洩露,如何發現記憶體洩露,並且將記憶體洩露定位是重點(第4章 記憶體洩露)
第1章 記憶體的測量
作者在開頭的一段話說明了本書採用的方法論:
關於系統記憶體使用,將按照(1)明確目標->(2)尋找評估方法,(3)瞭解當前狀況->對系統記憶體進行優化->重新測量,評估改善狀況的過程,來闡述系統的記憶體使用與優化。
(1)明確目標,針對系統記憶體優化,有兩個:
A.每個守護程序使用的記憶體儘可能少
B.長時間執行後,守護程序記憶體仍然保持較低使用量,沒有內存洩露。
(2)尋找評估方法,第1章關注點。
(3)對系統記憶體進行優化,第2章針對程序進行優化,第3章針對系統層面進行記憶體優化,第4章關注記憶體洩露。
系統記憶體測量
free用以獲得當前系統記憶體使用情況。
在一嵌入式裝置獲取如下:
busybox free -/+ buffers: 15584 8356 Swap: 0 0 0 |
和PC使用的free對比:
total used free shared buffers cached |
可見這兩個命令存在差異,busybox沒有cached。這和實際不符,實際可用記憶體=free+buffers+cached。
buffers是用來給Linux系統中塊裝置做緩衝區,cached用來緩衝開啟的檔案。下面是通過cat /proc/meminfo獲取,可知實際可用記憶體=8352+0+3508=11860。已經使用記憶體為=23940-11860=12080。可見兩者存在差異,busybox的free不太準確;/proc/meminfo的資料更準確。
MemTotal: 23940 kB |
程序記憶體測量
在程序的proc中與記憶體有關的節點有statm、maps、memmap。
cat /proc/xxx/statm
1086 168 148 1 0 83 0 |
這些引數以頁(4K)為單位,分別是:
1086 Size,任務虛擬地址空間的大小。
168 Resident,應用程式正在使用的實體記憶體的大小。
148 Shared,共享頁數。
1 Trs,程式所擁有的可執行虛擬記憶體的大小。
0 Lrs,被映像到任務的虛擬記憶體空間的的庫的大小。
83 Drs,程式資料段和使用者態的棧的大小。
0 dt,髒頁數量(已經修改的物理頁面)。
Size、Trs、Lrs、Drs對應虛擬記憶體,Resident、Shared、dt對應實體記憶體。
cat /proc/xxx/maps
00400000-00401000 r-xp 00000000 08:05 18561374 /home/lubaoquan/temp/hello |
第一列,代表該記憶體段的虛擬地址。
第二列,r-xp,代表該段記憶體的許可權,r=讀,w=寫,x=執行,s=共享,p=私有。
第三列,代表在程序地址裡的偏移量。
第四列,對映檔案的的主從裝置號。
第五列,映像檔案的節點號。
第六列,映像檔案的路徑。
kswapd
Linux存在一個守護程序kswapd,他是Linux記憶體回收機制,會定期監察系統中空閒呢村的數量,一旦發現空閒記憶體數量小於一個閾值的時候,就會將若干頁面換出。
但是在嵌入式Linux系統中,卻沒有交換分割槽。沒有交換分割槽的原因是:
1.一旦使用了交換分割槽,系統系能將下降的很快,不可接受。
2.Flash的寫次數是有限的,如果在Flash上面建立交換分割槽,必然導致對Flash的頻繁讀寫,影響Flash壽命。
那沒有交換分割槽,Linux是如何做記憶體回收的呢?
對於那些沒有被改寫的頁面,這塊記憶體不需要寫到交換分割槽上,可以直接回收。
對於已經改寫了的頁面,只能保留在系統中,,沒有交換分割槽,不能寫到Flash上。
在Linux實體記憶體中,每個頁面有一個dirty標誌,如果被改寫了,稱之為dirty page。所有非dirty page都可以被回收。
第2章 程序記憶體優化
當存在很多守護程序,又要去降低守護程序記憶體佔用量,如何去推動:
1.所有守護程序記憶體只能比上一個版本變少。
2.Dirty Page排前10的守護程序,努力去優化,dirty page減少20%。
可以從三個方面去優化:
1.執行檔案所佔用的記憶體
2.動態庫對記憶體的影響
3.執行緒對記憶體的影響
2.1 執行檔案
一個程式包括程式碼段、資料段、堆段和棧段。一個程序執行時,所佔用的記憶體,可以分為如下幾部分:
棧區(stack):由編譯器自動分配釋放,存放函式的引數、區域性變數等
堆區(heap):一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可有作業系統來回收
全域性變數、靜態變數:初始化的全域性變數和靜態變數在一塊區域,未初始化的全域性變數和靜態變數在另一塊區域,程式結束後由系統釋放
文字常量:常量、字串就是放在這裡的,程式結束後有系統釋放
程式程式碼:存放函式體的二進位制程式碼
下面結合一個例項分析:
#include <stdlib.h> int n=10; int main() printf("pid:%d\n", pid); |
執行程式結果:
pid:18768 |
檢視cat /proc/17868/maps
00400000-00401000 r-xp 00000000 08:05 18561376 /home/lubaoquan/temp/example |
第3章 系統記憶體優化
3.1 守護程序的記憶體使用
守護程序由於上期執行,對系統記憶體使用影響很大:
1.由於一直存貨,所以其佔用的記憶體不會被釋放。
2.即使什麼都不做,由於引用動態庫,也會佔用大量的實體記憶體。
3.由於生存週期很長,哪怕一點記憶體洩露,累積下來也會很大,導致記憶體耗盡。
那麼如何降低風險呢?
1.設計守護程序時,區分常駐部分和非常駐部分。儘量降低守護程序的邏輯,降低記憶體佔用,降低記憶體洩露機率。或者將幾個守護程序內容合為一個。
2.有些程序只是需要儘早啟動,而不需要變成守護程序。可以考慮加快啟動速度,從而使服務達到按需啟動的需求。優化方式有優化載入動態庫、使用Prelink方法、採用一些程序排程方法等。
3.2 tmpfs分割槽
Linux中為了加快檔案讀寫,基於記憶體建立了一個檔案系統,成為ramdisk或者tmpfs,檔案訪問都是基於實體記憶體的。
使用df -k /tmp可以檢視分割槽所佔空間大小:
Filesystem 1K-blocks Used Available Use% Mounted on |
在對這個分割槽進行讀寫時,要時刻注意,他是佔用實體記憶體的。不需要的檔案要及時刪除。
3.3 Cache和Buffer
系統空閒記憶體=MemFree+Buffers+Cached。
Cache也稱快取,是把從Flash中讀取的資料儲存起來,若再次讀取就不需要去讀Flash了,直接從快取中讀取,從而提高讀取檔案速度。Cache快取的資料會根據讀取頻率進行組織,並最頻繁讀取的內容放在最容易找到的位置,把不再讀的內容不短往後排,直至從中刪除。
在程式執行過程中,發現某些指令不在記憶體中,便會產生page fault,將程式碼載入到實體記憶體。程式退出後,程式碼段記憶體不會立即丟棄,二是作為Cache快取。
Buffer也稱快取,是根據Flash讀寫設計的,把零散的寫操作集中進行,減少Flash寫的次數,從而提高系統性能。
Cache和BUffer區別簡單的說都是RAM中的資料,Buffer是即將寫入磁碟的,而Cache是從磁碟中讀取的。
使用free -m按M來顯示Cache和Buffer大小:
total used free shared buffers cached |
降低Cache和Buffer的方法:
sync
該命令將未寫的系統緩衝區寫到磁碟中。包含已修改的 i-node、已延遲的塊 I/O 和讀寫對映檔案。/proc/sys/vm/drop_caches
a)清理pagecache(頁面快取)
# echo 1 > /proc/sys/vm/drop_caches 或者 # sysctl -w vm.drop_caches=1
b)清理dentries(目錄快取)和inodes
# echo 2 > /proc/sys/vm/drop_caches 或者 # sysctl -w vm.drop_caches=2
c)清理pagecache、dentries和inodes
# echo 3 > /proc/sys/vm/drop_caches 或者 # sysctl -w vm.drop_caches=3
上面三種方式都是臨時釋放快取的方法,要想永久釋放快取,需要在/etc/sysctl.conf檔案中配置:vm.drop_caches=1/2/3,然後sysctl -p生效即可!
vfs_cache_pressure
vfs_cache_pressure=100 這個是預設值,核心會嘗試重新宣告dentries和inodes,並採用一種相對於頁面快取和交換快取比較”合理”的比例。
減少vfs_cache_pressure的值,會導致核心傾向於保留dentry和inode快取。
增加vfs_cache_pressure的值,(即超過100時),則會導致核心傾向於重新宣告dentries和inodes
總之,vfs_cache_pressure的值:
小於100的值不會導致快取的大量減少
超過100的值則會告訴核心你希望以高優先順序來清理快取。
3.4 記憶體回收
kswapd有兩個閾值:pages_high和pages_low,當空閒記憶體數量低於pages_low時,kswapd程序就會掃描記憶體並且每次釋放出32個free pages,知道free page數量達到pages_high。
kswapd回收記憶體的原則?
1.如果物理頁面不是dirty page,就將該物理頁面回收。
- 程式碼段,只讀不能被改寫,所佔記憶體都不是dirty page。
- 資料段,可讀寫,所佔記憶體可能是dirty page,也可能不是。
- 堆段,沒有對應的對映檔案,內容都是通過修改程式改寫的,所佔實體記憶體都是dirty page。
- 棧段和堆段一樣,所佔實體記憶體都是dirty page。
- 共享記憶體,所佔實體記憶體都是dirty page。
就是說,這條規則主要面向程序的程式碼段和未修改的資料段。
2.如果物理頁面已經修改並且可以備份迴文件系統,就呼叫pdflush將記憶體中的內容和檔案系統進行同步。pdflush寫回磁碟,主要針對Buffers。
3.如果物理頁面已經修改但是沒有任何磁碟的備份,就將其寫入swap分割槽。
kswapd再回首過程中還存在兩個重要方法:LMR(Low on Memory Reclaiming)和OMK(Out of Memory Killer)。
由於kswapd不能提供足夠空閒記憶體是,LMR將會起作用,每次釋放1024個垃圾頁知道記憶體分配成功。
當LMR不能快速釋放記憶體的時候,OMK就開始起作用,OMK會採用一個選擇演算法來決定殺死某些程序。傳送SIGKILL,就會立即釋放記憶體。
3.5 /proc/sys/vm優化
此資料夾下面有很多介面控制記憶體操作行為,在進行系統級記憶體優化的時候需要仔細研究,適當調整。
block_dump
表示是否開啟Block Debug模式,用於記錄所有的讀寫及Dirty Block寫回操作。0,表示禁用Block Debug模式;1,表示開啟Block Debug模式。dirty_background_ratio
表示髒資料達到系統整體記憶體的百分比,此時觸發pdflush程序把髒資料寫回磁碟。dirty_expires_centisecs
表示髒資料在記憶體中駐留時間超過該值,pdflush程序在下一次將把這些資料寫回磁碟。預設值3000,單位是1/100s。dirty_ratio
表示如果程序產生的髒資料達到系統整體記憶體的百分比,此時程序自行吧髒資料寫回磁碟。dirty_writeback_centisecs
表示pdflush程序週期性間隔多久把髒資料協會磁碟,單位是1/100s。vfs_cache_pressure
表示核心回收用於directory和inode cache記憶體的傾向;預設值100表示核心將根據pagecache和swapcache,把directory和inode cache報紙在一個合理的百分比;降低該值低於100,將導致核心傾向於保留directory和inode cache;高於100,將導致核心傾向於回收directory和inode cache。min_free_kbytes
表示強制Linux VM最低保留多少空閒記憶體(KB)。nr_pdflush_threads
表示當前正在進行的pdflush程序數量,在I/O負載高的情況下,核心會自動增加更多的pdflush。overcommit_memory
指定了核心針對記憶體分配的策略,可以是0、1、2.
0 表示核心將檢查是否有足夠的可用記憶體供應用程序使用。如果足夠,記憶體申請允許;反之,記憶體申請失敗。
1 表示核心允許分配所有實體記憶體,而不管當前記憶體狀態如何。
2 表示核心允許分配查過所有實體記憶體和交換空間總和的記憶體。overcommit_ratio
如果overcommit_memory=2,可以過在記憶體的百分比。page-cluster
表示在寫一次到swap區時寫入的頁面數量,0表示1頁,3表示8頁。swapiness
表示系統進行交換行為的成都,數值(0~100)越高,越可能發生磁碟交換。legacy_va_layout
表示是否使用最新的32位共享記憶體mmap()系統呼叫。nr_hugepages
表示系統保留的hugetlg頁數。
第4章 記憶體洩露
4.1 如何確定是否有記憶體洩露
解決記憶體洩露一個好方法就是:不要讓你的程序成為一個守護程序,完成工作後立刻退出,Linux會自動回收該程序所佔有的記憶體。
測試記憶體洩露的兩種方法:
1.模仿使用者長時間使用裝置,檢視記憶體使用情況,對於那些記憶體大量增長的程序,可以初步懷疑其有記憶體洩露。
2.針對某個具體測試用例,檢查是否有記憶體洩露。
在發現程序有漏洞之後,看看如何在程式碼中檢查記憶體洩露。
4.2 mtrace
glibc針對記憶體洩露給出一個鉤子函式mtrace:
1.加入標頭檔案<mcheck.h>
2.在需要記憶體洩露檢查的程式碼開始呼叫void mtrace(),在需要記憶體洩露檢查程式碼結尾呼叫void muntrace()。如果不呼叫muntrace,程式自然結束後也會顯示記憶體洩露
3.用debug模式編譯檢查程式碼(-g或-ggdb)
4.在執行程式前,先設定環境變數MALLOC_TRACE為一個檔名,這一檔案將存有記憶體分配資訊
5.執行程式,記憶體分配的log將輸出到MALLOC_TRACE所執行的檔案中。
程式碼如下:
#include <stdio.h> int main(void) char *p=malloc(10); |
編譯,設定環境變數,執行,檢視log:
gcc -o mem-leakage -g mem-leakage.c export MALLOC_TRACE=/home/lubaoquan/temp/malloc.og ./mem-leakage = Start |
加入mtrace會導致程式執行緩慢:
1.日誌需要寫到Flash上(可以將MALLOC_TRACE顯示到stdout上。)
2.mtrace函式內,試圖根據呼叫malloc程式碼指標,解析出對應的函式
效能優化是一個艱苦、持續、枯燥、反覆的過程,涉及到的內容非常多,編譯器優化、硬體體系結構、軟體的各種技巧等等。
另外在嵌入式電池供電系統上,效能的優化也要考慮到功耗的使能。PnP的兩個P(Power and Performance)是不可分割的部分。
第5章 效能優化的流程
5.1 效能評價
首先“快”與“慢”需要一個客觀的指標,同時明確定義測試階段的起訖點。
同時優化也要考慮到可移植性以及普適性,不要因為優化過度導致其他問題的出現。
5.2 效能優化的流程
1. 測量,獲得資料,知道和目標效能指標的差距。
2. 分析待優化的程式,查詢效能瓶頸。
3. 修改程式。
4. 重新測試,驗證優化結果。
5. 達到效能要求,停止優化。不達目標,繼續分析。
5.3 效能評測
介紹兩種方法:可視操作(攝像頭)和日誌。
話說攝像頭錄影評測,還是很奇葩的,適用範圍很窄。但是貌似還是有一定市場。
5.4 效能分析
導致效能低下的三種主要原因:
(1) 程式運算量很大,消耗過多CPU指令。
(2) 程式需要大量I/O,讀寫檔案、記憶體操作等,CPU更多處於I/O等待。
(3) 程式之間相互等待,結果CPU利用率很低。
簡單來說即是CPU利用率高、I/O等待時間長、死鎖情況。
下面重點放在第一種情況,提供三種方法。
1. 系統相關:/proc/stat、/proc/loadavg
cat /proc/stat結果如下:
cpu 12311503 48889 7259266 561072284 575332 0 72910 0 0 0-----分別是user、nice、system、idle、iowait、irq、softirq、steal、guest、guest_nice
user:從系統啟動開始累計到當前時刻,使用者態CPU時間,不包含nice值為負的程序。
nice:從系統啟動開始累計到當前時刻,nice值為負的程序所佔用的CPU時間。
system:從系統啟動開始累計到當前時刻,核心所佔用的CPU時間。
idle:從系統啟動開始累計到當前時刻,除硬碟IO等待時間以外其他等待時間。
iowait:從系統啟動開始累計到當前時刻,硬碟IO等待時間。
irq:從系統啟動開始累計到當前時刻,硬中斷時間。
softirq:從系統啟動開始累計到當前時刻,軟中斷時間。
steal:從系統啟動開始累計到當前時刻,involuntary wait
guest:running as a normal guest
guest_nice:running as a niced guest
cpu0 3046879 11947 1729621 211387242 95062 0 1035 0 0 0 cpu1 3132086 8784 1788117 116767388 60010 0 535 0 0 0 cpu2 3240058 12964 1826822 116269699 353944 0 31989 0 0 0 cpu3 2892479 15192 1914705 116647954 66316 0 39349 0 0 0 intr 481552135 16 183 0 0 0 0 0 0 175524 37 0 0 2488 0 0 0 249 23 0 0 0 0 0 301 0 0 3499749 21 1470158 156 33589268 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-------------------Counts of interrupts services since boot time.Fist column is the total of all interrupts services, each subsequent if total for particular interrupt.
ctxt 2345712926-------------------------------------------------Toal number of context switches performed since bootup accross all CPUs. btime 1510217813------------------------------------------------Give the time at which the system booted, in seconds since the Unix epoch. processes 556059------------------------------------------------Number of processes and threads created, include(but not limited to) those created by fork() or clone() system calls. procs_running 2-------------------------------------------------Current number of runnable threads procs_blocked 1-------------------------------------------------Current number of threads blocked, waiting for IO to complete. softirq 415893440 117 134668573 4001105 57050104 3510728 18 1313611 104047789 0 111301395---總softirq和各種型別softirq產生的中斷數:RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
由cpu的各種時間可以推匯出:
CPU時間=user+nice+system+idle+iowait+irq+softirq+steal+guest+guest_nice
CPU利用率=1-idle/(user+nice+system+idle+iowait+irq+softirq+steal+guest+guest_nice)
CPU使用者態利用率=(user+nice)/(user+nice+system+idle+iowait+irq+softirq+steal+guest+guest_nice)
CPU核心利用率=system/(user+nice+system+idle+iowait+irq+softirq+steal+guest+guest_nice)
IO利用率=iowait/(user+nice+system+idle+iowait+irq+softirq+steal+guest+guest_nice)
cat /proc/loadavg結果如下:
0.46 0.25 0.16 2/658 13300
1、5、15分鐘平均負載;
2/658:在取樣時刻,執行佇列任務數目和系統中活躍任務數目。
13300:最大pid值,包括執行緒。
2. 程序相關:/proc/xxx/stat
24021 (atop) S 1 24020 24020 0 -1 4194560 6179 53 0 0 164 196 0 0 0 -20 1 0 209898810 19374080 1630 18446744073709551615 1 1 0 0 0 0 0 0 27137 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0
3. top
top是最常用來監控系統範圍內程序活動的工具,提供執行在系統上的與CPU關係最密切的程序列表,以及很多統計值。
第6章 程序啟動速度
程序啟動可以分為兩部分:
(1) 程序啟動,載入動態庫,直到main函式值錢。這是還沒有執行到程式設計師編寫的程式碼,其效能優化有其特殊方法。
(2) main函式之後,直到對使用者的操作有所響應。涉及到自身編寫程式碼的優化,在7、8章介紹。
6.1 檢視程序的啟動過程
hello原始碼如下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("Hello world!\n");
return 0;
}
編譯:
gcc -o hello -O2 hello.c
strace用於檢視系統執行過程中系統呼叫,同時得知程序在載入動態庫時的大概過程,-tt可以列印微妙級別時間戳。
strace -tt ./hello如下:
20:15:10.185596 execve("./hello", ["./hello"], [/* 82 vars */]) = 0
20:15:10.186087 brk(NULL) = 0x19ad000
20:15:10.186206 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
20:15:10.186358 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f24710ea000
20:15:10.186462 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
20:15:10.186572 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
20:15:10.186696 fstat(3, {st_mode=S_IFREG|0644, st_size=121947, ...}) = 0
20:15:10.186782 mmap(NULL, 121947, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f24710cc000
20:15:10.186857 close(3) = 0
20:15:10.186975 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
20:15:10.187074 open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
20:15:10.187153 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832----------------libc.so.6檔案控制代碼3,大小832。
20:15:10.187270 fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0
20:15:10.187358 mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f2470afd000
20:15:10.187435 mprotect(0x7f2470cbd000, 2097152, PROT_NONE) = 0
20:15:10.187558 mmap(0x7f2470ebd000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7f2470ebd000---引數依次是:addr、length、prot、flags、fd、offset。
20:15:10.187662 mmap(0x7f2470ec3000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2470ec3000
20:15:10.187749 close(3) = 0
20:15:10.187887 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f24710cb000
20:15:10.187992 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f24710ca000
20:15:10.188072 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f24710c9000
20:15:10.188191 arch_prctl(ARCH_SET_FS, 0x7f24710ca700) = 0--------------------------------set architecture-specific thread state, the parameters are code and addr。
20:15:10.188334 mprotect(0x7f2470ebd000, 16384, PROT_READ) = 0
20:15:10.188419 mprotect(0x600000, 4096, PROT_READ) = 0
20:15:10.188541 mprotect(0x7f24710ec000, 4096, PROT_READ) = 0
20:15:10.188633 munmap(0x7f24710cc000, 121947) = 0
20:15:10.188785 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
20:15:10.188965 brk(NULL) = 0x19ad000
20:15:10.189158 brk(0x19ce000) = 0x19ce000
20:15:10.189243 write(1, "Hello world!\n", 13Hello world!-----------------------------------往控制代碼1寫13個字元Hello world!\n。
) = 13
20:15:10.189299 exit_group(0) = ?
20:15:10.189387 +++ exited with 0 +++
通過設定LD_DEBUG環境變數,可以打印出在程序載入過程中都做了那些事情:
LD_DEBUG=all ./hello如下。看似簡單的一個Hello world!,其系統已經做了很多準備工作。
13755: 13755: file=libc.so.6 [0]; needed by ./hello [0]----------(1) 搜尋其所依賴的動態庫。 13755: find library=libc.so.6 [0]; searching 13755: search cache=/etc/ld.so.cache 13755: trying file=/lib/x86_64-linux-gnu/libc.so.6 13755: 13755: file=libc.so.6 [0]; generating link map 13755: dynamic: 0x00007fbac5cedba0 base: 0x00007fbac592a000 size: 0x00000000003c99a0 13755: entry: 0x00007fbac594a950 phdr: 0x00007fbac592a040 phnum: 10 13755: 13755: checking for version `GLIBC_2.2.5