2010年12月25日 星期六

kernel的開機執行順序

Linux是一個目前非常普遍的作業系統,因為它為免費且公開原始碼的作業系統,所以在嵌入系統中大家都很喜歡使用它,而且它的執行可以很小,且又有效率。我們現在就來大概地了解一下它的開機順序。
首先我們要了解的是,Linux kernel可以被編譯成兩種形式,一種是有壓縮的,另一種是沒有壓縮的,兩種形式其第一個執行指令不同,我們先來了解有壓縮的,它是將另一種沒有壓縮的kernel將其做壓縮,並在其前面加一段解壓縮的程式碼,而這段程式碼則是可以執行在記憶中任何位置,並且會將kernel作解壓縮之後放在記憶上,之後將其控制權交給kernel,之後的執行順序,有壓縮和没壓縮的順序是一樣的。
那麼我們先來看看有壓縮的第一個指令在那裹,以ARM CPU來說,它是在arch/arm/boot/compressed/head.S的程式裹,這一段是使用組合語言寫的,其中有兩個重要的暫存器,一個是r1存放的是CPU的代碼,這個代碼是由使用自行定義,由bootloader傳過來,這個值定義在arch/arm/tools/mach-types中,在編譯時會產生一個include/asm/mach-types.h,這時會使用CP15來讀回CPU的ID做比對,確認是否正確,並且使用相對應的cache操作副程式,因此在正確的CPU ID比對之後會啓動cache,以加快對kernel的解壓縮動作。
在解壓縮kernel之前,解壓縮程式會先檢查現在的位置是否和解壓縮之後的位址是否有衝突,若沒有則直接開始解壓縮,若有將會解壓縮至其它地方,之後再搬至最後kernel所要擺放的位置,並且跳至kernel的第一個指令,開始執行,而kernel的第一個指令放在arch/arm/kernel/head.S,開始執行時kernel會比對CPU ID是否正確,再初始化系統的cache HAL call back function,以及各種初始化動作,這些定在MACHINE_START的巨集宣告中,而這是使用者所宣告的,這時也會初始化整記憶體管理,最後會呼叫kernel_start,這是在init/main.c之中,接下來會載入各個driver,以及會mount root file system,當一切都好了之後則會呼叫第一支應用程式,default為/sbin/init,你可以在make menuconfig中將其更換你所要執行的程式,而這支程式可以是binary file也可以是shell script,到此就結束所有的開機動作。

2010年12月14日 星期二

如何製作kernel的default setting

當我們取得kernel的source code之後,將其解壓縮,原則上我們第一動作當然會修改根目錄中的Makefile,將其ARCH & CROSS_COMPILE的環境變數修改,例如如下:
ARCH = arm
CROSS_COMPILE = arm-linux-
接著我們將會執行make menuconfig的動作,在結束這個動作之後會產生兩個一個是 .config 它是存在原紿檔的根目錄中,這個檔是給Makefile讀取用的,另一個檔為include/linux/autoconfig.h這是給kernel中的C程式用的,當你完成所有的開發之後,可以將 .config 檔拷貝至 arch/arm/default目錄之下,並且取名為 xxxx_defconfig,其中的 xxxx 為你個人自行取名,如此一來當其它人拿到你的source code之後不知該如何設定時,他可以先執行 make xxxx_defconfig 如此一來將會用你之前的設定設置至 .config & include/linux/autoconfig.h 之中,如此一來這將會是你的default setting。

2010年10月26日 星期二

Linux Device Driver的基本架構

每一種OS都會有驅動程式, 而驅動程式主要是做為應用程式要使用各種系統的週邊元件的一中間橋樑, kernel接收及管理應用程式的需求, 並將其需求轉送給驅動程式, 而kernel將會處理多工的問題, 為了讓各種驅動程式好撰寫, 所以每一種OS將會規定驅動程式的寫法, 就好像C語言也有規定應用程式的寫法, 而在linux的OS中規定驅動程式的寫法如下:
  • 目前都是使用C語言來撰寫驅動程式
  • 程式務必include & include
  • 每一個程式都會有一個進入點, 或者第一個指令的地方, linux device driver的進入點如下:
static int __init your_driver_name_init(void)
{
return 0;
}
module_init(your_driver_name_init);

其進入點的名字你可以隨意取一個名字, 但是其原型的宣告方式必須是固定的, 請參考上面的範例程式, 最後必須使用一個巨集指令module_init()來宣告其為程式進入點。
  • Linux的驅動程式可以是static link至kernel, 也可以是動態的方式, linux稱做module, 若是static的方式則必須要有完整的kernel source code, 並且在編譯時會將整個kernel source code編譯過, 而module的方式則不用, 只需要kernel的include檔即, 但是不管是static或者module其進入點宣告方式都一樣
  • 離開或者是下載驅動程式時的進入點宣告如下 :
static void __exit your_driver_name_exit(void)
{
}
module_exit(your_driver_name_exit);

這和進入點一樣, 名字可以隨意取, 但是原型宣告則必須固定, 並且使用module_exit()巨集指令來確定離開驅動式的進入點。

以上是Linux的驅動程式的基本概念。

2010年10月21日 星期四

如何使用NFS做為開機時的root file system

我們都知道Linux在開機時必須要mount一個檔案系統做為根目錄系統, 而這個根目錄通常會指定定在kernel bootup command line的root及rootfstype選項中指定, 但是初期我們在開發embedded系統時通常會有問題, 或者embedded flash的空間不大, 所以若可以使用NFS做為root file system, 將會對開發embedded系統來說會是一項非常方便的事情, 以下即是說明如何設定來使用NFS做為開機時的root file system。
若要使用NFS做為開機時的根目錄, 在kernel的選項中有幾個是必須被打開, 如NFS client (CONFIG_ROOT_NFS), 以及可以自動設定IP的選項, 如DHCP或者BOOTP或RARP, 這是讓kernel開機時即可取得IP address (CONFIG_IP_PNP), 因為NFS是架構在網路上。
而其中的boot command line的設定如下 :

root=/dev/nfs

而其中的/dev/nfs為一個虛擬的device node, 並不是真實存在, 所以也並不需要真的在檔案系統中建立這個device node

nfsroot=[{server-ip}:]{root-dir}[,{nfs-options}]

nfsroot是指定NFS server的資料, 若沒有指定nfsroot則會default使用/tftpboot/%s, 做為mount的點,其各個選項說明如下:
{server-ip} 是指定NFS server的IP address
{root-dir} 是指定NFS server export出來的目錄, 也是會被mount為根目錄的目錄
{nfs-options} 為選項如下:
  • port = portmap所使用的port number
  • rsize = 4096
  • wsize = 4096
  • timeo = 7
  • retrans = 3
  • acregmin = 3
  • acregmax = 60
  • acdirmin = 30
  • acdirmax = 60
  • flags = hard, nointr, noposix, cto, ac
ip={client-ip}:{server-ip}:{gw-ip}:{netmask}:{hostname}:{device}:{autoconf}

這是在設定embedded自已的IP address, 若沒有這個設定, 將會依照kernel中所設定的autoconf方法取得IP address, 這個設定可以如下各個情形:
  • ip="off"
  • ip="none"
  • ip=dhcp"
  • ip="autoconf"
其各選項說明如下 :
{client-ip} embedded自已本身的IP address
{server-ip} NFS server的IP address
{gw-ip} gateway IP address
{netmask} netmask
{hostname} embedded自已本身的名字
{device} embedded自已本身所使用的網路介面名字
{autoconf} 其可用的選項如下 :
  • off or none
  • on or any
  • dhcp
  • bootp
  • rarp
  • both (使用bootp & rarp)
  • default is any
以上若是使用正確將可以使用NFS來做為開機時的root file system,以下是一個範例:

root=/dev/nfs console=ttySAC0,115200 nfsroot=192.168.2.144:/home/work/embedded/filesystem/rootfs ip=dhcp

以上範例測試板是dhcp取得IP address,以下是另一個範例!為設定固定IP address :

root=/dev/nfs console=ttySAC0,115200 nfsroot=192.168.2.144:/home/work/embedded/filesystem/rootfs ip=192.168.2.90

2010年10月4日 星期一

在Linux一些好用的設定

  • 如何讓在Linux系統下ls指令時所顯示的檔名,會根據檔案屬性不同,顯示不同的顏色,其做法是在使用者的home directory之下編譯.bashrc檔案,加入一些設定如下
    export LS_OPTIONS='--color=auto'
    eval "`dircolors`"
    alias ls='ls $LS_OPTIONS'
    alias ll='ls $LS_OPTIONS -l'
    alias l='ls $LS_OPTIONS -lA'
  • 在debain系統按裝套件的指令
apt-get install package-name
apt-cache search package-name
  • 檢查有多少軟件已按裝
dpkg -l
  • 免設IP/ 免使用public IP / 使用P2P / Window, Ubuntu, iphone支援,可以穿透任何firewall

http://www.teamviewer.com
用過心得:還是VNC的效能較好,比較不會有延遲晝面及解析度較好。但是VNC無法透過internet使用。
  • 如何設定讓debain 6.0的Window可以用root權限登入:
default的debain 6.0是不讓人使用root來登錄,當然這是為了安全著想,但是有時debain我們是將它安裝在virtual machine中,只是自已使用,所以常常需使用su或者做權限的認證才能做事,其實是很麻煩的,所以可以直接用root來登錄是一件很方便的事。其實要讓root可以直接登入是一件很簡單的事,只要修改 /etc/pam.d/gdm3 的檔案,將以下的這行mark掉就好了:

auth required pam_succeed_if.so user != root quiet_success

2010年8月20日 星期五

如何編譯ARM Cross Compiler ToolChain

在Embedded系統中, 通常我們會需要做cross development, 而這時則需要cross compiler, 而有時等待網路上其他編譯好的cross compiler會太慢, 或者他們編譯時所選的選項不是你想要的, 這時則必需自行編譯, 我個人的經驗則是完全自行編譯使用, 而不用其他人所編譯好的toolchain, 所以我將我個人的經驗提供給大家做參考。在編譯之前你必需準備好以下的環境及source code:

下載原始碼
1. 安裝好一個PC Linux, 你可以用virtual machine來建立, 個人不太建議使用cygwin這類的環境來做開發環境。
2. 必需在PC Linux上按裝軟體如下:
  • x86版本的gcc compiler (必須包含g++)
  • 公共程式texi2html
  • 公共程式gawk
  • 函式庫zlib
3. 下載kernel source code (這裏我使用2.6.35當範例) 。
4. 下載gcc source code (這裏我使用4.5.0當範例) 。
5. 下載glibc source code (這裏我使用2.11.2當範例) 。
6. 下載binutil source code (這裏我使用2.20.0當範例) 。
7. 下載glibc ARM patched file (glibc-ports-2.11.tar.bz2)。
8. 下載gmp source code (這裏我使用5.0.1當範例) 。
9. 下載mpfr source code (這裏我使用3.0.0當範例) 。
10. 下載mpc source code (這裏我使用0.8.2當範例, 在GCC 4.5.0之後必需使用mpc)。

編譯流程
以下則是整個編譯過程的主要步驟, 你必須依照以下的順序, 否則你將無法順利編譯成功:
1. 將下載下來的source code全部解壓縮至各別的目錄。
2. 先編譯binutils, 並將其按裝在你的PC Linux上 (這裏假設按裝的目錄為/usr/local/arm-linux-gnueabi), 這個原始碼是ld, as, ranlib等的程式碼。
3. 編譯gmp並將其按裝至以上binutil按裝目錄之下的tools目錄。
4. 宣告一個系統變數 export LD_LIBRARY_PATH=/usr/local/arm-linux-gnueabi/tools/lib。
5. 編譯mpfr並將其按裝至以上binutil按裝目錄之下的tools目錄。
6. 編譯mpc並將其按裝至以上binutil按裝目錄之下的tools目錄。
7. 增加系統search變數 export PATH=$PATH:/usr/local/arm-linux-gnueabi/bin。
8. 編譯gcc為static以及只有 c 語言的支援, 並將其按裝至PC Linux。
9. 編譯glibc為shared library並將其按裝至PC Linux, 若是要編譯多種版本library (little endian, big endian version), 則必須要在這個步驟重覆執行, 使用不同的參數來產生不同版本的library。
10. 再編譯一次gcc, 但是將其編譯為shared library, 並有c++的支援, 至此就大功告成。

編譯gmp步驟如下:
1. 建立一個子目錄為 /usr/local/arm-linux-gnueabi/tools
2. 進入gmp的source code directory
3. 建立一個目錄為build-arm, 並進入該目錄
4. 執行命令如下:
CPPFLAGS=-fexceptions ../configure --prefix=/usr/local/arm-linux-gnueabi/tools --enable-cxx --with-gnu-ld
5. 執行make & make install

編譯mpfr步驟如下:
1. 進入mpfr的source code directory
2. 建立一個目錄為build-arm, 並進入該目錄
3. 執行命令如下:
../configure --prefix=/usr/local/arm-linux-gnueabi/tools --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-gnu-ld
4. 執行make & make install

編譯mpc步驟如下:
1. 進入mpc的source code directory
2. 建立一個目錄為build-arm, 並進入該目錄
3. 執行命令如下:
../configure --prefix=/usr/local/arm-linux-gnueabi/tools --with-gnu-ld --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools
4. 執行make & make install

編譯binutil的步驟如下:
1. 進入binutils的source code directory
2. 建立一個月錄build-arm, 並且進入該目錄
3. 執行命令如下:
../configure --prefix=/usr/local/arm-linux-gnueabi --host=i686-pc-linux --build=i686-pc-linux --target=arm-linux-gnueabi --enable-shared --disable-multilib --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools --with-mpc=/usr/local/arm-linux-gnueabi/tools
3-1. 假如你要支援多種版本的library (little-endian & big-endian), 則所下的參數會是如下 :
../configure --prefix=/usr/local/arm-linux-gnueabi --build=i686-pc-linux --host=i686-pc-linux --target=arm-linux-gnueabi --enable-shared --enable-multilib --with-lib-path=/usr/local/arm-linux-gnueabi/lib:/usr/local/arm-linux-gnueabi/lib/be:/usr/local/arm-linux-gnueabi/lib/soft-float:/usr/local/arm-linux-gnueabi/lib/be/soft-float --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools --with-mpc=/usr/local/arm-linux-gnueabi/tools
主要是多一個--with-lib-path的選項。
4. 之後執行make & make install

設定環境變數
這時需要先編譯一版使用static library的gcc版本, 再使用這版compiler去編譯glibc為shared library, 之後再回頭再編譯一次gcc為使用shared library, 在編譯之前需設定幾個環境變數如下 :
1. export LD_LIBRARY_PATH=/usr/local/arm-linux-gnueabi/tools/lib
2. export PATH=$PATH:/usr/local/arm-linux-gnueabi/bin
3. 若要支援多版本的編繹器則需要修改設定檔gcc/config/arm/t-linux-eabi,增加內容如下:

MULTILIB_OPTIONS = mlittle-endian/mbig-endian
MULTILIB_DIRNAMES = le be
LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib

以下是編譯的流程 :
1. 進入GCC source code目錄並建立一個子目錄build-arm-static
2. 進入build-arm-static目錄, 執行以下指令
../configure --host=i686-pc-linux --build=i686-pc-linux --target=arm-linux-gnueabi --prefix=/usr/local/arm-linux-gnueabi --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools --with-gnu-ld --without-newlib --enable-long-long --disable-libada --without-cvs --disable-libgomp --disable-libmudflap --disable-libssp --disable-threads --enable-__cxa_atexit --disable-shared --enable-languages=c --with-system-zlib --with-mpc=/usr/local/arm-linux-gnueabi/tools --disable-multilib
2-1.若是要支援多版library則設定如下:
./configure --host=i686-pc-linux --build=i686-pc-linux --target=arm-linux-gnueabi --prefix=/usr/local/arm-linux-gnueabi --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools --with-gnu-ld --without-newlib --enable-long-long --disable-libada --without-cvs --disable-libgomp --disable-libmudflap --disable-libssp --disable-threads --enable-__cxa_atexit --disable-shared --enable-languages=c --with-system-zlib --with-mpc=/usr/local/arm-linux-gnueabi/tools --enable-multilib
3. 執行make & make install

編譯glibc
接下來是要編譯glibc是較麻煩的一件事, 在編譯glibc之前, 你必須使用kernel來產生一些header files因為在編譯glibc時需要用到, 以下是產生kernel header file的步驟:
1. 進入kernel source code目錄, 修改Makefile中的ARCH及CROSS_COMPILE變數如下 :
ARCH := arm
CROSS_COMPILE := arm-linux-gnueabi
INSTALL_MOD_PATH := `pwd`/installed_modules
1-1. 你若要產生多種版本的library (little endian & big endian), 那你必須更改兩個檔案 (glibc-source-code/Makeconfig & glibc-source-code/config.make.in), 以便在編譯完要按裝時可以得到適當的按裝, 其修改內容如下:
  • glibc-source-code/Makefconfig 第193行加入以下內容:
slibdir = $(libdir)
  • glibc-source-code/config.make.in mark掉第13行 #slibdir = @libc_cv_slibdir@, 加入一行如下:
slibdir = @libdir@
1-2. 在新版的glibc中gcc_eh並未準備好,所以無法使用,必須修Makeconfig檔以防止在編譯glibc時會連結gcc_eh, 你必須將該檔中的-lgcc_eh移除即。
2. 執行make menuconfig並選擇一種ARM平台, 之後將設定存下
3. 執行make headers_install以取得編譯glibc時要使用的kernel header files, 這時這些的include files將會被產生在kernel source code的目錄之下的usr/include目錄
4. 進入glibc的source code根目錄, 並建立一個目錄為build-arm-le, 並將glibc-ports-2.11.tar.bz2解壓縮的內容拷貝至glibc的目錄並設為ports的目錄
5. 進入build-arm-le的目錄, 並執行以下指令 :
CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ CPP="arm-linux-gnueabi-gcc -E" LD=arm-linux-gnueabi-ld STRIP=arm-linx-gnueabi-strip AR=arm-linux-gnueabi-ar NM=arm-linux-gnueabi-nm AS=arm-linux-gnueabi-as RANLIB=arm-linux-gnueabi-ranlib ARCH=arm ../configure --prefix=/usr/local/arm-linux-gnueabi --target=arm-linux-gnueabi --host=arm-linux-gnueabi --build=i686-pc-linux --disable-profile --enable-add-ons --with-__thread --enable-threads=posix --enable-multilib --without-cvs --enable-shared --without-gd --with-headers=/home/work/kernel/linux-2.6.35/usr/include --libdir=/usr/local/arm-linux-gnueabi/lib libc_cv_forced_unwind=yes libc_cv_c_cleanup=yes
5-1.進入build-arm-be的目錄, 並執行以下指令:
CC="arm-linux-gnueabi-gcc -mbig-endian" CXX="arm-linux-gnueabi-g++ -mbig-endian" CPP="arm-linux-gnueabi-gcc -E -mbig-endian" LD="arm-linux-gnueabi-ld -EB" STRIP=arm-linx-gnueabi-strip AR=arm-linux-gnueabi-ar NM=arm-linux-gnueabi-nm AS="arm-linux-gnueabi-as -EB" RANLIB=arm-linux-gnueabi-ranlib ARCH=arm ../configure --prefix=/usr/local/arm-linux-gnueabi --target=arm-linux-gnueabi --host=arm-linux-gnueabi --build=i686-pc-linux --disable-profile --enable-add-ons --with-__thread --enable-threads=posix --enable-multilib --without-cvs --enable-shared --without-gd --with-headers=/home/work/kernel/linux-2.6.35/usr/include --libdir=/usr/local/arm-linux-gnueabi/lib/be libc_cv_forced_unwind=yes libc_cv_c_cleanup=yes
6. 執行make & make install, 至此即可完成glibc的建立, 你將可以在/usr/local/arm-linux-gnueabi目錄下, 可以發現include & lib的目錄內容已包含glibc的內容
7.  將kernel/usr/include目錄之下的所有檔案拷貝至/usr/local/arm-linux-gnueabi/include目錄之下

編譯gcc for shared library
最後的動作則是要再重新編譯一次gcc使它支援shared library, 在重新編譯之前, 有幾個動作必須要完成, 以下是這些動作的內容:
1. 進入/usr/local/arm-linux-gnueabi/arm-linux-gnueabi的目錄, 特別注意是有兩層的arm-linux-gnueabi, 不可弄錯
2. 執行指令如下:
> mv lib lib.bak
> ln -s ../lib lib
> ln -s ../include include
> cp -a lib.bak/* ../lib
這時可以正式開始來重新編譯gcc, 其步驟如下:
1. 進入gcc source code目錄, 並建立一個目錄為build-arm-shared, 之後進入build-arm-shared目錄
2. 執行以下指令 :
../configure --host=i686-pc-linux --build=i686-pc-linux --target=arm-linux-gnueabi --prefix=/usr/local/arm-linux-gnueabi --with-arch=armv6 --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools --with-gnu-ld --without-newlib --enable-long-long --disable-libada --without-cvs --disable-libgomp --disable-libmudflap --disable-libssp --enable-threads --enable-__cxa_atexit --enable-shared --enable-languages=c,c++ --with-system-zlib --with-mpc=/usr/local/arm-linux-gnueabi/tools --disable-multilib
2-1.若要支援多版library執行指令如下:
../configure --host=i686-pc-linux --build=i686-pc-linux --target=arm-linux-gnueabi --prefix=/usr/local/arm-linux-gnueabi --with-arch=armv6 --with-gmp=/usr/local/arm-linux-gnueabi/tools --with-mpfr=/usr/local/arm-linux-gnueabi/tools --with-gnu-ld --without-newlib --enable-long-long --disable-libada --without-cvs --disable-libgomp --disable-libmudflap --disable-libssp --enable-threads --enable-__cxa_atexit --enable-shared --enable-languages=c,c++ --with-system-zlib --with-mpc=/usr/local/arm-linux-gnueabi/tools --enable-multilib
3. 執行make ; make install

至此就大功告成, 你可以寫一個簡單的hello程式來試試, 記得這是default share library的cross compiler, 所以你執行ap時也要記得將shared library也拷貝至embedded computer

最後有一個公共程式很好用,那就是 ldd 它可將應用程式及 library 所用到的 library 例出來,default 的 ldd 有點問題無法執行,這邊可以自行寫一個 shell script 檔名就是 arm-linux-gnueabi-ldd 其內容如下:

#!/bin/sh
arm-linux-gnueabi-objdump -x $@ | grep NEEDED

並將這執行放至 /usr/local/arm-linux-gnueabi/bin 目錄之下即可。