在嵌入式系統(tǒng)中,內(nèi)存是比較緊缺的資源,特別是在消費(fèi)類產(chǎn)品中, 為了節(jié)省成本,一般都會(huì)將硬件資源應(yīng)用到極致。在開(kāi)發(fā)過(guò)程中,就經(jīng)常會(huì)遇到,運(yùn)行內(nèi)存(RAM)就還差一點(diǎn),但就是不夠用的情況,比如:
需要在原系統(tǒng)上添加一個(gè)小算法
OTA只能將固件放到內(nèi)存上時(shí)
需要?jiǎng)討B(tài)分配比較大的空間
需要放置一些比較大的臨時(shí)文件
如果你原來(lái)設(shè)備內(nèi)存已經(jīng)使用到90%甚至更高,要實(shí)現(xiàn)上面功能,大概率會(huì)影響系統(tǒng)的整體性能,甚至?xí)霈F(xiàn)系統(tǒng)異常。那該怎么辦?
在不考慮硬件增加RAM大小的情況下,軟件上還有沒(méi)有其它的方式,可以擠出一點(diǎn)點(diǎn)空間呢?答案是可以的,但是需要綜合評(píng)估帶來(lái)的問(wèn)題和風(fēng)險(xiǎn)。
下面以君正T31ZC為實(shí)例,來(lái)介紹一下它的內(nèi)存使用情況,以及如何擠出更加多的內(nèi)存空間。
一、內(nèi)存使用情況分析
T31ZC?是一款基于MIPS架構(gòu)的主處理器,上面集成了512Mbit的DDR2內(nèi)存,使用的是linux操作系統(tǒng)。
1、物理內(nèi)存分布
512Mbit的物理內(nèi)存,也就是64MByte,在實(shí)際使用的時(shí)候,它被劃分為了兩塊,rmem?和?mem
rmem?用于多媒體,比如音視頻編碼、裁剪、OSD等功能
mem?是用于linux系統(tǒng)內(nèi)存
它通過(guò)tag部分的cmdline來(lái)設(shè)置:
[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系統(tǒng),24M分配給了多媒體。
通過(guò)?dmesg?命令可以看到更多內(nèi)存的使用情況:
rd_start=0x80600000 rd_size=0xd35c00
這個(gè)是用來(lái)存放根文件系統(tǒng)的起始位置,以及這個(gè)空間的大小。
Memory: 20680k/40960k
40960K?是linux系統(tǒng)可使用的總大小,也就是上面設(shè)置的40MB,20680k?是系統(tǒng)實(shí)際可以使用的內(nèi)存。
剩下的?40960K - 20680k = 20280K?內(nèi)存到哪里去了?
3868k kernel code, 20280k reserved, 1052k data, 196k init, 0k highmem
一部分是給內(nèi)核使用,包括內(nèi)核代碼段、數(shù)據(jù)段、以及?init?段
剩下的20280k - 3868K -1052K - 196K = 15164K
這剩下的1516K?是預(yù)留給ramfs?使用的。
2、mem使用情況
linux?系統(tǒng)啟動(dòng)后,不運(yùn)行其它的應(yīng)用程序,我們查看的內(nèi)存使用情況如下:
cat /proc/meminfo
可用的實(shí)際物理內(nèi)存大小還剩余12872 kB
分配給linux系統(tǒng)使用的內(nèi)存有40M,但是應(yīng)用程序都還沒(méi)開(kāi)始運(yùn)行,內(nèi)存就只剩下12872K,內(nèi)存都到哪里去了!
總結(jié)歸納如下:
總物理內(nèi)存64M,24M分配給了多媒體,40M分配給了linux系統(tǒng)
?
總大小 | 多媒體內(nèi)存 | linux系統(tǒng) |
---|---|---|
64M | 24M | 40M |
?
linux系統(tǒng)內(nèi)存中,可使用的內(nèi)存為20680k,預(yù)留的內(nèi)存為20280k
?
linux系統(tǒng) | available | reserved |
---|---|---|
40960k | 20680k | 20280k |
?
linux系統(tǒng)中可使用的內(nèi)存,內(nèi)核模塊加載,緩沖緩存等系統(tǒng)服務(wù)占用7768K,剩余的為應(yīng)用可使用的內(nèi)存
available | MemFree | modules/others |
---|---|---|
20680k | 12912k | 7768K |
linux系統(tǒng)中預(yù)留的內(nèi)存,主要是預(yù)留給kernel和根文件系統(tǒng),其中kernel中主要有代碼段,數(shù)據(jù)段,init段。剩下的主要是根文件系統(tǒng)占用的空間。
reserved | kernel code | kernel data | kernel init | ramfs | others |
---|---|---|---|---|---|
20280k | 3868K | 1052K | 196K | 13527K | 1637K |
?
二、優(yōu)化方向
從上面的分析來(lái)看,可以優(yōu)化的方向有:
減少模塊加載,系統(tǒng)服務(wù)
kernel?優(yōu)化
rootfs?優(yōu)化
1、減少模塊加載,系統(tǒng)服務(wù)
這個(gè)可優(yōu)化的空間有限,這里不做詳細(xì)討論,可以通過(guò)命令查看實(shí)際模塊加載情況:cat /proc/modules
2、kernel 優(yōu)化
將內(nèi)核的調(diào)試信息去除掉,可以在menuconfig?中取消?Load all symbols for debugging/ksymoops?選項(xiàng),這里可以省出少量的空間。
3、rootfs 優(yōu)化
從上面的分析,我們看到為ramfs預(yù)留的空間為13527K,這是個(gè)非常大的空間。從啟動(dòng)cmdline中我們看到Flash的分區(qū)信息如下:
256K(boot),352K(tag),5M(kernel),6M(rootfs),2560K(recovery),1440K(system),512K(config),16M@0(all)
(1)flash為rootfs預(yù)留的空間為6M,而內(nèi)存為rootfs預(yù)留的是13527K,為何相差如此之大?
主要的原因是我們r(jià)ootfs在燒錄到Flash的時(shí)候是壓縮過(guò)的,在加載rootfs的時(shí)候,首先是將rootfs從Flash中讀取出來(lái),再解壓到內(nèi)存指定的地址去,然后再將解壓后的rootfs掛載成ramfs文件系統(tǒng)加載起來(lái)。
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?命令使用最高等級(jí)(9)的壓縮方式把rootfs_camera.cpio?壓縮到rootfs_camera.cpio.lzo?文件,-f?表示強(qiáng)制執(zhí)行,如果rootfs_camera.cpio.lzo已經(jīng)存在則覆蓋它
mark_rootfs_pc?是用來(lái)更新rootfs_camera.cpio.lzo的實(shí)際文件大小,實(shí)際是將壓縮后的文件大小寫入到rootfs_camera.cpio.lzo的最開(kāi)始位置(4字節(jié))。
(2)為什么要使用ramfs文件系統(tǒng)?
ramfs?適合用于臨時(shí)存儲(chǔ)、臨時(shí)文件、內(nèi)核模塊載入等臨時(shí)性應(yīng)用,其中數(shù)據(jù)不需要長(zhǎng)期保存的場(chǎng)景,它是linux內(nèi)核中的虛擬文件系統(tǒng),不需要指定特定的掛載選項(xiàng)。
優(yōu)點(diǎn)有:
快速讀寫操作
零延遲
輕量級(jí)
易于創(chuàng)建和銷毀
缺點(diǎn)有:
不具備持久性
內(nèi)存限制
不適合大型文件
需要足夠的內(nèi)存
君正T31ZC是屬于低功耗SOC,實(shí)時(shí)性,快速啟動(dòng)要求比較高。一般應(yīng)用場(chǎng)景是有另外的MCU來(lái)控制它上下電,有事件觸發(fā)的時(shí)候,SOC上電處理事件,事件處理完成后,SOC下電,只留MCU工作,以達(dá)到省功耗的目的,所以使用ramfs?也是合理的選擇。
官方手冊(cè)上的建議是這樣的:
rootfs中只存放對(duì)快起有要求的必要程序和庫(kù)文件,如果對(duì)快起沒(méi)有要求的程序或庫(kù)文件建議放到 system 分區(qū),以達(dá)到快速啟動(dòng)和節(jié)省內(nèi)存的目的。
三、另類解決方案
1、問(wèn)題分析
根據(jù)上面的分析,如果系統(tǒng)使用的是ramfs,優(yōu)化空間最大的就是根文件系統(tǒng),減小根文件系統(tǒng)的大小可以直接節(jié)省內(nèi)存。
上面官方給的建議中需要面對(duì)另外一個(gè)問(wèn)題,就是需要在應(yīng)用程序啟動(dòng)之后,再去system分區(qū)使用dl_open加載所需要的動(dòng)態(tài)庫(kù)。如果你程序主要使用的是靜態(tài)庫(kù),那這種方式就達(dá)不到想要的效果。
查看rootfs?文件系統(tǒng)里面的內(nèi)容,占用空間大的,一個(gè)是stone目錄,另外一個(gè)是lib目錄。
./stone?目錄下放置的是需要運(yùn)行的main程序
./lib?目錄下放置的主要是一些動(dòng)態(tài)庫(kù),ko驅(qū)動(dòng)文件,以及一個(gè)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?執(zhí)行程序在啟動(dòng)的時(shí)候就執(zhí)行
./etc/init.d/rcS:/stone/main &
wifi?驅(qū)動(dòng)也是在啟動(dòng)的時(shí)候被加載
insmod /lib/modules/cywdhd.ko firmware_path=/lib/firmware/fw_bcm43438a1.bin nvram_path=/lib/firmware/nvram.txt iface_name=wlan0
是否可以這樣:
在啟動(dòng)的時(shí)候,main 執(zhí)行文件,cywdhd.ko驅(qū)動(dòng),驅(qū)動(dòng)固件fw_bcm43438a1.bin被加載完之后,就把它們刪除,以達(dá)到釋放內(nèi)存的目的?
因?yàn)檫@些文件都是放置在內(nèi)存上,刪除它們并不會(huì)影響Flash中的文件,下次上電可以重新從Flash中讀取出來(lái),刪除它們也確實(shí)是可以釋放一部分內(nèi)存。
以我上面的例子,刪除?main?文件就可以釋放6.2M的空間,確實(shí)也是可以達(dá)到釋放內(nèi)存的目的。
但是,這樣操作是否有風(fēng)險(xiǎn)?
四、刪除ELF文件是否有影響
我們知道,程序運(yùn)行是被段頁(yè)式加載到內(nèi)存的,那要怎么知道在刪除main程序執(zhí)行文件的時(shí)候,main程序里面的內(nèi)容已經(jīng)被全部加載到內(nèi)存中去了呢?
同樣,刪除ko文件和bin固件數(shù)據(jù)文件的時(shí)候同樣會(huì)遇到相同的疑問(wèn)。
1、刪除 cywdhd.ko 驅(qū)動(dòng)
通過(guò)?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被加載到內(nèi)存的0xc0215000?這個(gè)地址,Live?表示已經(jīng)加載到內(nèi)核并且在在運(yùn)行。671712?表示它的大小。
為什么我們前面看cywdhd.ko 大小是1003K ,加載到內(nèi)核中去卻只剩下67171 ?
我們使用?file?命令查看cywdhd.ko文件信息,發(fā)現(xiàn)它是ELF文件格式,并且是not stripped,也就是它里面還包含一些調(diào)試信息和調(diào)試符號(hào)表等內(nèi)容
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文件是沒(méi)有風(fēng)險(xiǎn)的。
還有一種確認(rèn)方式是:通過(guò)查看?/proc/kallsyms?文件信息
/proc/kallsyms?包含了內(nèi)核符號(hào)表信息,其中包括已加載模塊的地址范圍。如果模塊的地址范圍包括整個(gè)模塊,那么它通常已經(jīng)完全加載到內(nèi)存中。
2、刪除fw_bcm43438a1.bin固件
fw_bcm43438a1.bin?是作為cywdhd.ko?驅(qū)動(dòng)的一個(gè)固件被加載到內(nèi)核上的,至于它是什么時(shí)候使用,或者說(shuō)是否一次全部加載到內(nèi)存上了,應(yīng)該取決于cywdhd.ko驅(qū)動(dòng)
因此我個(gè)人認(rèn)為刪除fw_bcm43438a1.bin是會(huì)有比較大的風(fēng)險(xiǎn)的
3、刪除main程序
在Linux系統(tǒng)中, 執(zhí)行文件,動(dòng)態(tài)庫(kù),驅(qū)動(dòng)文件都是屬于ELF文件格式。ELF文件里面分很多段,常見(jiàn)的有代碼段,數(shù)據(jù)段,BSS?段。
可以使用命令?readelf -S your_program?來(lái)具體查看:
我們看到代碼段的大小是0x46a6b0,在很多操作系統(tǒng)中,為了節(jié)省內(nèi)存空間,都是使用動(dòng)態(tài)加載的方式,也就是段頁(yè)式加載方式。
根據(jù)局部性原理,一般程序在執(zhí)行的時(shí)候,都是將當(dāng)前執(zhí)行位置附近的數(shù)據(jù)加載到內(nèi)存上運(yùn)行。
但是對(duì)于ramfs文件系統(tǒng),因?yàn)樗呀?jīng)是被全部加載到內(nèi)存上了,那在它上面的main文件,在執(zhí)行的時(shí)候,它是一次性加載(全局加載)還是按需加載(段頁(yè)式加載)呢?
如果它是在運(yùn)行的時(shí)候一次性加載到內(nèi)存上,那么,在它運(yùn)行之后,把main文件刪除是沒(méi)有風(fēng)險(xiǎn)的,否則會(huì)引入系統(tǒng)風(fēng)險(xiǎn)。
對(duì)于執(zhí)行文件在ramfs文件系統(tǒng)上的加載,暫時(shí)沒(méi)有找到更詳細(xì)的介紹資料,熟悉這一領(lǐng)域的同學(xué)可以給點(diǎn)建議。
五、手動(dòng)釋放內(nèi)存
可以使用命令手動(dòng)釋放內(nèi)存:
echo 1 > /proc/sys/vm/drop_caches
關(guān)于drop_caches?的介紹,可以通過(guò)?man proc?的介紹來(lái)了解,這里不做過(guò)多說(shuō)明
六、總結(jié)
綜合上面介紹的內(nèi)容,在嵌入式Linux系統(tǒng)內(nèi)存不足的情況下,可以使用下面幾種方式進(jìn)行優(yōu)化:
將kernel的調(diào)試信息符號(hào)表去除,減少內(nèi)核鏡像文件大小
將文件系統(tǒng)上的ELF文件(包括執(zhí)行文件、動(dòng)態(tài)庫(kù)和ko驅(qū)動(dòng)文件)strip處理,去除多余的信息
將動(dòng)態(tài)庫(kù)放置到其它分區(qū)上,程序運(yùn)行后再通過(guò)dl_open來(lái)加載
執(zhí)行文件或是驅(qū)動(dòng)文件,在使用過(guò)后把它們刪除(可能存在風(fēng)險(xiǎn))
手動(dòng)釋放內(nèi)存
具體使用哪種方式,可以根據(jù)實(shí)際使用情況進(jìn)行評(píng)估和選擇。
編輯:黃飛
?
評(píng)論