減少Linux的系統占用,為應用程序保留更多的資源,減少設備的硬件成本。“你能做到多小?”這個問題通常在嵌入式工程師開始他們的項目時問到。大多數時候,問這個問題的人是想減少RAM和Flash資源,減少設備的單位成本或能源需求。
因為Linux最初是為桌面或服務器系統設計的,默認情況下,它沒有為尺寸大小進行過優化,無論如何,Linux正被越來越多地用在嵌入式設備中,要讓Linux變小不是一件容易的事情,這裡有幾個減少系統占用內存的方法。
許多工程師是從減少內核大小開始的,這裡有個容易著手的方法,本文將詳細介紹如何減少內核的大小,主要通過移除那些在一個典型的嵌入式系統中用不到的代碼。
在一個系統中根文件系統(RFS)可能是內存資源最大的消耗者。根文件系統包括了應用程序和c庫使用到的基礎結構代碼。為RFS選擇文件系統對最後的大小有非常大的影響,標准是ext3,從一個嵌入式工程師的眼光來看它的效率是非常低的,但那是另一篇文章的主題了。
實際中,如何減小?
即使最小的Linux發行版至少也有兩部分:內核和根文件系統。有時,這些部件駐扎在同一個文件中,但是它們仍然分成不同的部件。從內核中移除特征幾乎差不多的所有代碼,這樣一個系統很容易就減少到不到1MB大小。但是,許多用戶選擇Linux支持網絡和不同設備,因此這不是一個現實的做法。
內核
Linux內核非常有趣,盡管在編譯時它依賴GCC,但運行時它卻不依賴。那些工程師將目光轉向Linux初始化RAM磁盤(所謂的initrd),它是內核運行時的附屬物。Initrd首先是由內核加載的,程序運行時詢問系統需要載入什麼模塊以便支持設備,這樣真實的根文件系統才能被加載。實際上,有兩步加載過程,加載initrd後再加載真實的根文件系統,很少發現嵌入式系統中有根文件系統,因為這樣在一個系統中會增加靈活性,對這個系統做改動要花費額外的空間或時間,嵌入式系統一般不需要靈活性。但本文稍後將討論根文件系統。
可載入模塊支持
內核載入模塊是重新定位運行時內核連接到它自身的代碼,典型的可載入模塊的例子是允許從用戶空間載入驅動到內核中(某些探測進程執行後),以及不關閉系統升級設備驅動。對於大多數嵌入式系統而言,一旦它們處於該范圍之外,修改根文件系統要麼不現實要麼不可能。因此系統設計者直接將模塊連接到內核中,移除那些可載入的模塊,節約出來的空間對於內核來說是很多的,無論如何,程序管理可載入的模塊(如insmod\rmmod\lsmod)和shell腳本載入它們不是必需的。
Linux-tiny補丁
Linux-tiny補丁集已經變得時有時無的項目了,最初是由Matt Mackall在主持。消費電子Linux論壇(CLEF)正在努力恢復這個項目,CLEF開發者WiKi已經給2.6.22.5內核(寫本文時的最新版本)發布了補丁,同時,Linux-tiny項目的許多改變已經包括在主線內核中,盡管許多原始的Linux-tiny補丁已經集成到內核中,但實質上節約空間的補丁還沒有集成進去。
如:
1、 Fine-grain printk support【細粒度printk支持】:用戶可以控制什麼文件可以使用printk。工程師將從不使用printk的文件大小中受益。
2、 Change CRC from calculation to use table lookup【改變CRC算法從計算到使用表查詢】:以太網數據包需要一個CRC來校驗數據包的准確性。這個CRC算法實現使用表查詢替換了計算,節約了大約2K。
3、 Network tweaking【網絡調整】:幾個補丁包減少支持的網絡協議,緩存大小和打開的套接字。許多嵌入式設備僅支持少量的協議,不需要有成百上千連接的服務。
4、 No panic reporting【無應急報告】:如果設備有三個狀態燈,一系列的連接,用戶不能看到,更少的影響,應急信息顯示在一個不存在的終端上。如果設備發生內核應急失效,用戶只需要重新啟動設備即可。
5、 Reduction of inlining【減少直接插入】:直接插入是編譯器將代碼作為宏拷貝到它調用的每個位置,而不是產生一個函數調用。GCC默認將直接插入任何函數。通過抑制直接插入函數,代碼運行稍微慢一點,因為編譯器需要為調用和返回產生代碼,得到的報酬是對象文件更小了。
Linux-tiny補丁發布成一個tar包,它可以一起應用,也可以一個一個單獨應用
內核調整建議補充
盡管Linux-tiny項目涉及到許多的方面,還有幾個額外的配置改動可以減少Linux腳印。
1、 移除ext2/3支持,使用另外一個不同的文件系統:ext2/3文件系統通常比較大,大於32K,大部分工程師啟用一個Flash文件系統,但是不禁用ext2/3,這樣會浪費內存。
2、 移除對sysctl的支持,sysctl允許用戶在運行時調整內核參數,在大部分嵌入式設備中,內核配置一旦定了就不需要改動了,使用這個特征會浪費1K。
3、 減少IPC選項,大多數系統沒有SysV IPC特性(grep你的msget、msgct、msgsnd和msgrcv代碼)和POSIX消息查詢(grep mq_*[a-z])一樣可以運行,移除它們可以節約18K。
4、 查看你改動的效果
Size命令報告一個對象文件中所有代碼和數據的大小,這與ls命令的輸入是不同的,ls報告的是在文件系統中的字節數大小。
例如,一個內核用armv51交叉編譯器編譯報告如下:
# armv5l-linux-size vmlinx
text data bss dec hex filename
2080300 99904 99312 2279516 22c85c vmlinux
Text小節是編譯器發出的代碼,data小節包括全局和其他使用初始靜態符號的值,bss小節包括作為初始化的一部分被調零的靜態數據。
雖然這個數據有啟迪作用,但它沒有展示系統消耗的內存部分,也無法通過查詢vmlinux得出,但是查看連接在一起的文件,創建vmlinux是接下來最好的事情,為了得到這個信息,使用find命令在內核項目中定位built-in.o文件並計算大小:
# find . -name "built-in.o" | xargs armv5l-linux-size
?--totals | sort -n -k4
這個命令的輸出類似下面:
text data bss dec hex filename
189680 16224 33944 239848 3a8e8 ./kernel/built-in.o
257872 10056 5636 273564 42c9c ./net/ipv4/built-in.o
369396 9184 34824 413404 64edc ./fs/built-in.o
452116 15820 11632 479568 75150 ./net/built-in.o
484276 36744 14216 535236 82ac4 ./drivers/built-in.o
3110478 180000 159241 3449719 34a377 (TOTALS)
這個技術可以確切地指出占用了大量空間的代碼,因此工程師可以首先移除這些代碼,當這樣做的時候,用戶應該在創建之前執行編譯清除動作,因為從內核中丟掉一個特征並不意味著那個之前編譯好的對象文件將會被刪除。
對於那些新添加到Linux內核中的東西,一個常見的問題是如何在內核配置程序中使用一個選項來聯合一些built-in.o文件,這可以通過查看目錄下的Makefile和Kconfig文件來實現,Makefile將包括象下面這樣的一行:
obj-$(CONFIG_ATALK) += p8022.o psnap.o
當用戶設置了配置變量CONFIG_ATALK後它將生成右手邊的文件。內核配置工具一般不會暴露下屬配置變量名。要查出變量名之間的連接以及什麼是可見的,在Kconfig中尋找沒有CONFIG_前綴的變量名。
find . -name Kconfig -exec fgrep -H -C3 "config ATALK" {} \;
它將產生下面這樣的輸出:
./drivers/net/appletalk/Kconfig-#
./drivers/net/appletalk/Kconfig-# Appletalk driver configuration
./drivers/net/appletalk/Kconfig-#
./drivers/net/appletalk/Kconfig:config ATALK
./drivers/net/appletalk/Kconfig- tristate "Appletalk protocol support"
./drivers/net/appletalk/Kconfig- select LLC
./drivers/net/appletalk/Kconfig- ---help---
還有一些事情需要做,因為用戶需要在配置樹中找到Appletalk協議支持,但是至少要清楚要查找的是什麼