2012年11月1日 星期四

介紹如何在 debian wheezy amd64 上 cross compile Raspberry Pi 執行檔


 以三個方式來編 ffmpeg-dmo 這支程式,比較時間上的差異。

1. 直接在 Raspberry Pi arm 上編譯
2. 在 debian wheezy amd64 上裝上 cross compiler (raspberrypi-tools) 編譯出 arm 上的 binary。
3. 在 debian wheezy amd64 上裝上 cross compiler (raspberrypi-tools) 並加上 distcc,而 Raspberry Pi 上也裝上 distcc,控制自己和其他電腦共同幫忙編譯。

以 ffmpeg-dmo 來當範例,下載原始碼:

$ wget http://ftp.tw.debian.org/debian-multimedia/pool/main/f/ffmpeg-dmo/ffmpeg-dmo_1.0.orig.tar.gz
$ wget http://ftp.tw.debian.org/debian-multimedia/pool/main/f/ffmpeg-dmo/ffmpeg-dmo_1.0-dmo2.diff.gz
$ wget http://ftp.tw.debian.org/debian-multimedia/pool/main/f/ffmpeg-dmo/ffmpeg-dmo_1.0-dmo2.dsc


一、直接在 Raspberry Pi 上編譯:


解開並加上 patch
$ dpkg-source -x ffmpeg-dmo_1.0-dmo2.dsc

$ cd ffmpeg-dmo-1.0

正常程序即可
$ ./configure
$ make -j 8

通常單顆 cpu 是不需要用 -j 8 的,但為了統一,三種編譯方式全部都用 -j 8,減少變數。

在 Raspberry Pi 上,沒超頻,以 700MHz 來編,參數是 -j 8,花了約 153 分鐘完成。



二、於 debian wheezy amd64 裝上 cross compiler 來編譯:


Raspberry Pi 的 cpu 有點慢,所以拿來編譯程式,滿沒有效率的。既然有 desktop 比較快,可以先在 i386 或 x86_64 的 desktop 或 notebook 上將程式編好,再拷貝到 Raspberry Pi 上去執行。

第一步先在 desktop 裝上 raspberrypi-tools。
由於 raspberrypi-tools 是 32bits 的,所以在 debian wheezy amd64 要裝上 32bits 的程式庫。
$ sudo apt-get install libc6-i386 lib32stdc++6 lib32z1

下載 Raspberrypi-tools:
https://github.com/raspberrypi/tools

$ cd /tmp
$ sudo tar zxvf raspberrypi-tools-9c3d7b6.tar.gz
$ sudo mv raspberrypi-tools-9c3d7b6 /opt/cross

裝好後,試試看指令能不能執行
$ /opt/cross/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-gcc
arm-linux-gnueabihf-gcc: fatal error: no input files
compilation terminated.

這樣 no input files 就是正常可執行的狀況

-bash: /opt/cross/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-gcc: No such file or directory

若出現 No such file or directory 則應該是 32bits 程式庫沒裝好,再用 ldd 檢查一下。

接著拿 hello.c 來測試看看
$ cat > hello.c << EOF
#include
int main() {
printf("Hello, world!\n");
return 0;
}
EOF

會產生一個 hello.c 檔

編看看
$ /opt/cross/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-gcc -o hello hello.c
若沒問題會產生一個 hello 執行檔

$ ./hello
-bash: ./hello: cannot execute binary file

$ file hello
hello: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0x2c11b259c80ffd4ceccb9a2a0622fb12571858f7, not stripped

無法執行,因為是 arm 的執行檔,將 hello 拷貝到 Raspberry Pi 中去執行即可。


接下來編一個比較大的程式,ffmpeg-dmo:

解開並加上 patch
$ dpkg-source -x ffmpeg-dmo_1.0-dmo2.dsc

$ cd ffmpeg-dmo-1.0

加上剛才裝的 raspberrypi-tools 路徑
$ export PATH=/opt/cross/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin:$PATH

配置時指定打開 cross compile,目的平台為 arm,目的 os 為 linux,cross compiler 的前綴字首為 arm-linux-gnueabihf-
$ ./configure --enable-cross-compile --arch=arm --target-os=linux --cross-prefix=arm-linux-gnueabihf-

因為剛才設定 PATH 地方的檔案如下:
$ ls /opt/cross/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/
arm-linux-gnueabihf-addr2line arm-linux-gnueabihf-gfortran
arm-linux-gnueabihf-ar arm-linux-gnueabihf-gprof
arm-linux-gnueabihf-as arm-linux-gnueabihf-ld
arm-linux-gnueabihf-c++ arm-linux-gnueabihf-ld.bfd
arm-linux-gnueabihf-c++filt arm-linux-gnueabihf-ld.gold
arm-linux-gnueabihf-cpp arm-linux-gnueabihf-ldd
arm-linux-gnueabihf-ct-ng.config arm-linux-gnueabihf-nm
arm-linux-gnueabihf-elfedit arm-linux-gnueabihf-objcopy
arm-linux-gnueabihf-g++ arm-linux-gnueabihf-objdump
arm-linux-gnueabihf-gcc arm-linux-gnueabihf-pkg-config
arm-linux-gnueabihf-gcc-4.7.2 arm-linux-gnueabihf-pkg-config-real
arm-linux-gnueabihf-gcc-ar arm-linux-gnueabihf-ranlib
arm-linux-gnueabihf-gcc-nm arm-linux-gnueabihf-readelf
arm-linux-gnueabihf-gcc-ranlib arm-linux-gnueabihf-size
arm-linux-gnueabihf-gcov arm-linux-gnueabihf-strings
arm-linux-gnueabihf-gdb arm-linux-gnueabihf-strip
arm-linux-gnueabihf-gdbtui

所有執行檔都是以 arm-linux-gnueabihf- 為開頭,後面再加上原本的 gcc 、ar 之類的指令,所以前綴字要設定成 arm-linux-gnueabihf-

configure 完成後,直接 make 即可
$ make -j 8

若是在 configure 沒有指定 --arch,則在 make 時要加參數
$ make ARCH=arm -j 8

這個 ffmpeg-dmo 在我的 hp 6530b core 2 duo 2.26 G 上用 -j 8 來編譯,花了約 30 分鐘完成,這樣比起 Raspberry Pi 來得有效率多了,所以 cross compile 真的滿好用。




三、於 debian wheezy amd64 裝上 cross compiler 並配合 distcc 多台一起來編譯:


到目前為止,不管是 Raspberry Pi 、desktop 或 notebook 都只是在一台機器上編譯,再強的電腦能縮短的時間也是有限,若你剛好有很多台電腦呢?

這時就可配合 distcc 來做多台電腦同時編譯一套程式,預設 distcc 是用在同一平台上編程式,使用很廣泛,如 gentoo、chakra 等等由原始碼編成的 distribution,使用 distcc 就很適合。

但若我們在每台都裝上 cross compiler,就可以用 distcc 來做 cross compile。

我們拿二台來當例子,一台 Raspberry Pi arm (192.168.1.105),另一台 hp6530b x86_64 (192.168.1.123)
在 hp6530b 上剛才已經裝好 arm cross compiler,在二台機器都裝上 distcc


於 hp6530b (amd64 desktop):

$ sudo apt-get install distcc

$ sudo vi /etc/default/distcc
STARTDISTCC="true"
預設 distcc 是不啟動的,要在這裡改成 true 才會啟動

ALLOWEDNETS="192.168.1.105"
允許 192.168.1.105 連過來 (Raspberry Pi)

LISTENER="192.168.1.123"
預設只 listen 127.0.0.1,若要全部介面都能連,設成 0.0.0.0

最後面加上 cross compiler 路徑
PATH=/opt/cross/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/arm-linux-gnueabihf/bin/:\
/opt/cross/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/libexec/gcc/arm-linux-gnueabihf/4.7.2:\
${PATH}

儲存後啟動 distcc
$ sudo /etc/init.d/distcc start

防火牆記得打開 tcp port 3632 給 Raspberry Pi (192.168.1.105) 連過來。

觀察 distcc log Raspberry Pi 是否有連過來。

$ sudo tail -f /var/log/distccd.log
distccd[21702] (dcc_job_summary) client: 192.168.1.105:58440 COMPILE_OK exit:0 sig:0 core:0 ret:0 time:2477ms gcc libavutil/mathematics.c
distccd[21817] (dcc_job_summary) client: 192.168.1.105:58442 COMPILE_OK exit:0 sig:0 core:0 ret:0 time:779ms gcc libavutil/time.c

這時還不會有連線過來,等會從 Raspberry Pi 上開始編譯後才會有訊息,若有在動 log 大概長這樣。


於 Raspberry Pi:

在 Raspberry Pi 也裝上 distcc
$ sudo apt-get install distcc

當然可以二台 機器都透過 distccd 去編譯,但由於我們是在 Raspberry Pi 上編的,所以本機不要透過 distccd 而直接編效率會更好,因此,在 Raspberry Pi 上的 distccd 不用啟動,只要安裝好即可。

為了更乾淨些,將剛才的編譯的目錄砍了重新由原始碼解開
$ rm -rf ffmpeg-dmo-1.0

解開並加上 patch
$ dpkg-source -x ffmpeg-dmo_1.0-dmo2.dsc

$ cd ffmpeg-dmo-1.0

設定 /usr/lib/distcc 在路徑最前面,這樣在執行編譯指令時,會呼叫到 distcc 來運作
export PATH=/usr/lib/distcc:$PATH

$ ls -l /usr/lib/distcc/
lrwxrwxrwx 1 root root 16 Oct 25 21:52 c++ -> ../../bin/distcc
lrwxrwxrwx 1 root root 16 Oct 25 21:52 c89-gcc -> ../../bin/distcc
lrwxrwxrwx 1 root root 16 Oct 25 21:52 c99-gcc -> ../../bin/distcc
lrwxrwxrwx 1 root root 16 Oct 25 21:52 cc -> ../../bin/distcc
lrwxrwxrwx 1 root root 16 Oct 25 21:52 g++ -> ../../bin/distcc
lrwxrwxrwx 1 root root 16 Oct 25 21:52 g++-4.7 -> ../../bin/distcc
lrwxrwxrwx 1 root root 16 Oct 25 21:52 gcc -> ../../bin/distcc
lrwxrwxrwx 1 root root 16 Oct 25 21:52 gcc-4.7 -> ../../bin/distcc
lrwxrwxrwx 1 root root 16 Oct 25 21:52 x86_64-linux-gnu-g++ -> ../../bin/distcc
lrwxrwxrwx 1 root root 16 Oct 25 21:52 x86_64-linux-gnu-g++-4.7 -> ../../bin/distcc
lrwxrwxrwx 1 root root 16 Oct 25 21:52 x86_64-linux-gnu-gcc -> ../../bin/distcc
lrwxrwxrwx 1 root root 16 Oct 25 21:52 x86_64-linux-gnu-gcc-4.7 -> ../../bin/distcc

設定在使用 distcc 編譯時,有哪些成員
export DISTCC_HOSTS="192.168.1.123 localhost”


~/.distcc/hosts
192.168.1.123
localhost

注意,localhost 在這裡除了是本機外,還有額外意義的,寫成 localhost 告訴 distcc 直接執行編譯動作,而不要透過 distcc 去和本機的 distccd 溝通,這樣效率會更好些,所以,本機的 distccd 是不用啟動的。

機器列表是有順序的,比較快的電腦放在最前面,由於 Raspberry Pi 比較慢,放在最後面,一般來說,若主控機器 CPU 的效能不到全部機器加總效能的 1/5,最好從列表中拿掉,只讓其他機器編譯就好,由主控台全力做分配及連結的動作,不然會因此而拖垮整體效能。

前面設定完成後,這邊就一如往常的編就好:
$ ./configure
$ make -j 8

這次編 ffmpeg-dmo 透過 distccd 在 Raspberry Pi 及 hp6530b 上用 -j 8 來編譯,花了約 45 分鐘完成,雖然比起單用 6530b 一台編還慢些,但比起在 Raspberry Pi 上直接編要 153 分鐘還是來得有效率。

因為是從 Raspberry Pi 當主控者,而主控者要做的事比較多,除了分配資料給其他機器編譯外,完成的資料要做連結,所以主控者要用最強那台才對。也因為才二台,看不大出來差別,若是拿個 5 台來,相信時間會縮更短的。

沒有留言:

張貼留言