在嵌入式系統中,內存是比較緊缺的資源,特別是在消費類產品中, 為了節省成本,一般都會將硬件資源應用到極致。在開發過程中,就經常會遇到,運行內存(RAM)就還差一點,但就是不夠用的情況,比如:
需要在原系統上添加一個小算法
OTA只能將固件放到內存上時
需要動態分配比較大的空間
需要放置一些比較大的臨時文件
如果你原來設備內存已經使用到90%甚至更高,要實現上面功能,大概率會影響系統的整體性能,甚至會出現系統異常。那該怎么辦?
在不考慮硬件增加RAM大小的情況下,軟件上還有沒有其它的方式,可以擠出一點點空間呢?答案是可以的,但是需要綜合評估帶來的問題和風險。
下面以君正T31ZC為實例,來介紹一下它的內存使用情況,以及如何擠出更加多的內存空間。
一、內存使用情況分析
T31ZC?是一款基于MIPS架構的主處理器,上面集成了512Mbit的DDR2內存,使用的是linux操作系統。
1、物理內存分布
512Mbit的物理內存,也就是64MByte,在實際使用的時候,它被劃分為了兩塊,rmem?和?mem
rmem?用于多媒體,比如音視頻編碼、裁剪、OSD等功能
mem?是用于linux系統內存
它通過tag部分的cmdline來設置:
[root@Zeratul:~]# cat /proc/cmdline console=ttyS0,115200n8 mem=40M@0x0 rmem=24M@0x2800000 root=/dev/ram0 rw rdinit=/linuxrc mtdparts=jz_sfc:256K(boot),352K(tag),5M(kernel),6M(rootfs),2560K(recovery),1440K(system),512K(config),16M@0(all) lpj=6955008 quiet senv;[HW];init_vw=1920;init_vh=1080;nrvbs=2;mode=0;eenv; lzo_size=5907415 rd_start=0x80600000 rd_size=0xd35c00 [root@Zeratul:~]#
這里我們看到40M分配給了linux系統,24M分配給了多媒體。
通過?dmesg?命令可以看到更多內存的使用情況:
rd_start=0x80600000 rd_size=0xd35c00
這個是用來存放根文件系統的起始位置,以及這個空間的大小。
Memory: 20680k/40960k
40960K?是linux系統可使用的總大小,也就是上面設置的40MB,20680k?是系統實際可以使用的內存。
剩下的?40960K - 20680k = 20280K?內存到哪里去了?
3868k kernel code, 20280k reserved, 1052k data, 196k init, 0k highmem
一部分是給內核使用,包括內核代碼段、數據段、以及?init?段
剩下的20280k - 3868K -1052K - 196K = 15164K
這剩下的1516K?是預留給ramfs?使用的。
2、mem使用情況
linux?系統啟動后,不運行其它的應用程序,我們查看的內存使用情況如下:
cat /proc/meminfo
可用的實際物理內存大小還剩余12872 kB
分配給linux系統使用的內存有40M,但是應用程序都還沒開始運行,內存就只剩下12872K,內存都到哪里去了!
總結歸納如下:
總物理內存64M,24M分配給了多媒體,40M分配給了linux系統
?
總大小 | 多媒體內存 | linux系統 |
---|---|---|
64M | 24M | 40M |
?
linux系統內存中,可使用的內存為20680k,預留的內存為20280k
?
linux系統 | available | reserved |
---|---|---|
40960k | 20680k | 20280k |
?
linux系統中可使用的內存,內核模塊加載,緩沖緩存等系統服務占用7768K,剩余的為應用可使用的內存
available | MemFree | modules/others |
---|---|---|
20680k | 12912k | 7768K |
linux系統中預留的內存,主要是預留給kernel和根文件系統,其中kernel中主要有代碼段,數據段,init段。剩下的主要是根文件系統占用的空間。
reserved | kernel code | kernel data | kernel init | ramfs | others |
---|---|---|---|---|---|
20280k | 3868K | 1052K | 196K | 13527K | 1637K |
?
二、優化方向
從上面的分析來看,可以優化的方向有:
減少模塊加載,系統服務
kernel?優化
rootfs?優化
1、減少模塊加載,系統服務
這個可優化的空間有限,這里不做詳細討論,可以通過命令查看實際模塊加載情況:cat /proc/modules
2、kernel 優化
將內核的調試信息去除掉,可以在menuconfig?中取消?Load all symbols for debugging/ksymoops?選項,這里可以省出少量的空間。
3、rootfs 優化
從上面的分析,我們看到為ramfs預留的空間為13527K,這是個非常大的空間。從啟動cmdline中我們看到Flash的分區信息如下:
256K(boot),352K(tag),5M(kernel),6M(rootfs),2560K(recovery),1440K(system),512K(config),16M@0(all)
(1)flash為rootfs預留的空間為6M,而內存為rootfs預留的是13527K,為何相差如此之大?
主要的原因是我們rootfs在燒錄到Flash的時候是壓縮過的,在加載rootfs的時候,首先是將rootfs從Flash中讀取出來,再解壓到內存指定的地址去,然后再將解壓后的rootfs掛載成ramfs文件系統加載起來。
rootfs的打包命令如下:
cd ./_rootfs_camera find . | cpio -H newc -o > ../rootfs_camera.cpio cd .. lzop -9 -f rootfs_camera.cpio -o rootfs_camera.cpio.lzo ./mark_rootfs_pc rootfs_camera.cpio.lzo
將_rootfs_camera?目錄下的所有文件歸檔到上一目錄的rootfs_camera.cpio文件中
使用lzop?命令使用最高等級(9)的壓縮方式把rootfs_camera.cpio?壓縮到rootfs_camera.cpio.lzo?文件,-f?表示強制執行,如果rootfs_camera.cpio.lzo已經存在則覆蓋它
mark_rootfs_pc?是用來更新rootfs_camera.cpio.lzo的實際文件大小,實際是將壓縮后的文件大小寫入到rootfs_camera.cpio.lzo的最開始位置(4字節)。
(2)為什么要使用ramfs文件系統?
ramfs?適合用于臨時存儲、臨時文件、內核模塊載入等臨時性應用,其中數據不需要長期保存的場景,它是linux內核中的虛擬文件系統,不需要指定特定的掛載選項。
優點有:
快速讀寫操作
零延遲
輕量級
易于創建和銷毀
缺點有:
不具備持久性
內存限制
不適合大型文件
需要足夠的內存
君正T31ZC是屬于低功耗SOC,實時性,快速啟動要求比較高。一般應用場景是有另外的MCU來控制它上下電,有事件觸發的時候,SOC上電處理事件,事件處理完成后,SOC下電,只留MCU工作,以達到省功耗的目的,所以使用ramfs?也是合理的選擇。
官方手冊上的建議是這樣的:
rootfs中只存放對快起有要求的必要程序和庫文件,如果對快起沒有要求的程序或庫文件建議放到 system 分區,以達到快速啟動和節省內存的目的。
三、另類解決方案
1、問題分析
根據上面的分析,如果系統使用的是ramfs,優化空間最大的就是根文件系統,減小根文件系統的大小可以直接節省內存。
上面官方給的建議中需要面對另外一個問題,就是需要在應用程序啟動之后,再去system分區使用dl_open加載所需要的動態庫。如果你程序主要使用的是靜態庫,那這種方式就達不到想要的效果。
查看rootfs?文件系統里面的內容,占用空間大的,一個是stone目錄,另外一個是lib目錄。
./stone?目錄下放置的是需要運行的main程序
./lib?目錄下放置的主要是一些動態庫,ko驅動文件,以及一個wifi使用的?bin?文件
-rwxrwxr-x ?1 biao biao 6.2M Oct 15 18:14 main -rwxrwxrwx 1 biao biao 1003K Aug 21 10:04 cywdhd.ko -rwxrwxrwx 1 biao biao 404K ?Aug 21 10:04 fw_bcm43438a1.bin
2、解決方案
main?執行程序在啟動的時候就執行
./etc/init.d/rcS:/stone/main &
wifi?驅動也是在啟動的時候被加載
insmod /lib/modules/cywdhd.ko firmware_path=/lib/firmware/fw_bcm43438a1.bin nvram_path=/lib/firmware/nvram.txt iface_name=wlan0
是否可以這樣:
在啟動的時候,main 執行文件,cywdhd.ko驅動,驅動固件fw_bcm43438a1.bin被加載完之后,就把它們刪除,以達到釋放內存的目的?
因為這些文件都是放置在內存上,刪除它們并不會影響Flash中的文件,下次上電可以重新從Flash中讀取出來,刪除它們也確實是可以釋放一部分內存。
以我上面的例子,刪除?main?文件就可以釋放6.2M的空間,確實也是可以達到釋放內存的目的。
但是,這樣操作是否有風險?
四、刪除ELF文件是否有影響
我們知道,程序運行是被段頁式加載到內存的,那要怎么知道在刪除main程序執行文件的時候,main程序里面的內容已經被全部加載到內存中去了呢?
同樣,刪除ko文件和bin固件數據文件的時候同樣會遇到相同的疑問。
1、刪除 cywdhd.ko 驅動
通過?cat /proc/modules?命令可以查看模塊的加載情況
[root@Zeratul:bin]# cat /proc/modules | grep cywdhd cywdhd 671712 0 - Live 0xc0215000 jzmmc 17113 1 cywdhd, Live 0xc010f000 mmc_core 90139 3 mmc_block,cywdhd,jzmmc, Live 0xc00e5000
我們看到?cywdhd.ko被加載到內存的0xc0215000?這個地址,Live?表示已經加載到內核并且在在運行。671712?表示它的大小。
為什么我們前面看cywdhd.ko 大小是1003K ,加載到內核中去卻只剩下67171 ?
我們使用?file?命令查看cywdhd.ko文件信息,發現它是ELF文件格式,并且是not stripped,也就是它里面還包含一些調試信息和調試符號表等內容
biao@ubuntu: file cywdhd.ko cywdhd.ko: ELF 32-bit LSB relocatable, MIPS, MIPS32 version 1 (SYSV), BuildID[sha1]=a00e1928dd77ac04bb813b22cd485156f3392741, not stripped
strip?處理之后,文件大小變成了657788
biao@ubuntu: mips-linux-uclibc-gnu-strip cywdhd.ko biao@ubuntu:ll -rwxrwxrwx 1 biao biao 657788 Oct 15 19:42 cywdhd.ko
綜合上面信息,我們可以大致判斷刪除cywdhd.ko文件是沒有風險的。
還有一種確認方式是:通過查看?/proc/kallsyms?文件信息
/proc/kallsyms?包含了內核符號表信息,其中包括已加載模塊的地址范圍。如果模塊的地址范圍包括整個模塊,那么它通常已經完全加載到內存中。
2、刪除fw_bcm43438a1.bin固件
fw_bcm43438a1.bin?是作為cywdhd.ko?驅動的一個固件被加載到內核上的,至于它是什么時候使用,或者說是否一次全部加載到內存上了,應該取決于cywdhd.ko驅動
因此我個人認為刪除fw_bcm43438a1.bin是會有比較大的風險的
3、刪除main程序
在Linux系統中, 執行文件,動態庫,驅動文件都是屬于ELF文件格式。ELF文件里面分很多段,常見的有代碼段,數據段,BSS?段。
可以使用命令?readelf -S your_program?來具體查看:
我們看到代碼段的大小是0x46a6b0,在很多操作系統中,為了節省內存空間,都是使用動態加載的方式,也就是段頁式加載方式。
根據局部性原理,一般程序在執行的時候,都是將當前執行位置附近的數據加載到內存上運行。
但是對于ramfs文件系統,因為它已經是被全部加載到內存上了,那在它上面的main文件,在執行的時候,它是一次性加載(全局加載)還是按需加載(段頁式加載)呢?
如果它是在運行的時候一次性加載到內存上,那么,在它運行之后,把main文件刪除是沒有風險的,否則會引入系統風險。
對于執行文件在ramfs文件系統上的加載,暫時沒有找到更詳細的介紹資料,熟悉這一領域的同學可以給點建議。
五、手動釋放內存
可以使用命令手動釋放內存:
echo 1 > /proc/sys/vm/drop_caches
關于drop_caches?的介紹,可以通過?man proc?的介紹來了解,這里不做過多說明
六、總結
綜合上面介紹的內容,在嵌入式Linux系統內存不足的情況下,可以使用下面幾種方式進行優化:
將kernel的調試信息符號表去除,減少內核鏡像文件大小
將文件系統上的ELF文件(包括執行文件、動態庫和ko驅動文件)strip處理,去除多余的信息
將動態庫放置到其它分區上,程序運行后再通過dl_open來加載
執行文件或是驅動文件,在使用過后把它們刪除(可能存在風險)
手動釋放內存
具體使用哪種方式,可以根據實際使用情況進行評估和選擇。
編輯:黃飛
?
評論
查看更多