构建嵌入式 linux 开发环境 v0.1,我也来学做嵌入式Linux系统V0.1(完整版)

Build/install c++ compiler and libstdc++?

[ ]   Build/install java compiler and libgcj?                支持的语言,我没有选择java

[ ] Enable ccache support?                启用ccache的支持,它用于编译时头文件的缓存处理,用它来编译程序,第一次会有点慢,但是以后的速度可就很理想了,呵呵……

--- Gdb Options                根据你的需要,选择gdb的支持

Package Selection for the target  --->;

这一项我没有选择任意一项,因为我打算根文件系统及busybox  等工具链创建成工,手工来做。

Target Options  --->;  文件系统类型,根据实际需要选,我用的ext2;

配置完成后,编译它:

[root@skynet build-tools]#make

这一项工作是非常花时间的,我的工具包全部在本地,也花去我一小时十三分的时间,如果全要下载,我估计网速正常也要多花一两个钟头。

经过漫长的等待(事实上并不漫长,去打了几把游戏,很快过去了):

……

make[1]: Leaving directory `/home/skynet/build-tools/buildroot/build_i386/genext2fs-1.3'

touch -c /home/skynet/build-tools/buildroot/build_i386/genext2fs-1.3/genext2fs

#-@find /home/skynet/build-tools/buildroot/build_i386/root/lib -type f -name \*.so\* | xargs /home/skynet/tools/bin/i386-linux-uclibc-strip --remove-section=.comment --remove-section=.note --strip-unneeded 2>;/dev/null || true;

/home/skynet/build-tools/buildroot/build_i386/genext2fs-1.3/genext2fs -i 503 -b 1056 \

-d /home/skynet/build-tools/buildroot/build_i386/root -q -D target/default/device_table.txt /home/skynet/build-tools/buildroot/root_fs_i386.ext2

大功告成!!!

清点战利品

让我来看看它究竟做了哪些事情吧:

[root@skynet skynet]# cd tools

[root@skynet tools]# ls

bin  bin-ccache  i386-linux  i386-linux-uclibc  include  info  lib  libexec  man  usr

bin:所有的编译工具,如gcc,都在这儿了,只是加了些指定的前缀;

bin-ccache:如果在Toolchain optaion中没有选择对ccache的支持,就没有这一项了;

i386-linux:链接文件;实际指向include

i386-linux-uclibc:uclibc的相关工具;

include:供交叉开发工具使用的头文件;

info:gcc 的info文件;

lib:供交叉开发工具使用的链接库文件;

……

现在可以把编译工具所在目录XXX/bin添加至PATH了

测试工具链

如果你现在写一个程序,用i386-linux-gcc来编译,运行的程序会告诉你:

./test: linked against GNU libc

因为程序运行库会寻到默认的/lib:/usr/lib上面去,而我们目前的uclibc的库并不在那里(虽然对于目标机来讲,这是没有错的),所以,也只能暂时静态编译,试试它能否工作了。当然,你也可以在建好根文件系统后,试试用chroot……

第三章        编译内核

本章的工作,是为目标机建立一个合适的内核,对于建立内核,我想有两点值得考虑的:

1、功能上的选择,应该能够满足需要的情况下,尽量地小;

2、小不是最终目的,稳定才是;

所以,最好编译内核前有一份目标机硬件平台清单以及所需功能清单,这样,才能更合理地裁减内核。

准备工具

Linux内核源码,我选用的是Linux-2.4.27.tar.bz2

编译内核

将Linux-2.4.27.tar.bz2拷贝至${PRJROOT}/kernel,解压

#cd linux-2.4.27

//配置

# make ARCH=i386 CROSS_COMPILE=i386-linux- menuconfig

//建立源码的依存关系

# make ARCH=i386 CROSS_COMPILE=i386-linux- clean dep

//建立内核映像

# make ARCH=i386 CROSS_COMPILE=i386-linux- bzImage

ARCH指明了硬件平台,CROSS_COMPILE指明了这是交叉编译,且编译器的名称为i386-linux-XXX,这里没有为编译器指明路径,是因为我前面已将其加入至环境变量PATH。

又是一个漫长的等待……

OK,编译完成,673K,稍微大了点,要移到其它平台,或许得想办法做到512以下才好,回头来想办法做这个工作。

安装内核

内核编译好后,将内核及配置文件拷贝至${PRJROOT}/images下。

# cp arch/i386/boot/bzImage ${PRJROOT}/images/bzImage-2.4.27-rmk5

# cp vmlinux ${PRJROOT}/images/vmlinux-2.4.27-rmk5

# cp System.map ${PRJROOT}/images/System-2.4.27-rmk5

# cp .config ${PRJROOT}/images/2.4.27-rmk5

我采用了后缀名的方式重命名,以便管理多个不同版本的内核,当然,你也可以不用这样,单独为每个版本的内核在images下新建对应文件夹也是可行的。

安装内核模块

完整内核的编译后,剩下的工作就是建立及安装模块了,因为我的内核并没有选择模块的支持(这样扩展性差了一点,但是对于我的系统来说,功能基本上定死了,这样影响也不太大),所以,剩下的步骤也省去了,如果你还需要模块的支持,应该:

//建立模块

#make ARCH=i386 CROSS_COMPILE=i386-linux- modules

//安装内核模块至${PRJROOT}/images

#make ARCH=i386 CROSS_COMPILE= i386-linux- \

>;INSTALL_MOD_PATH=${PRJROOT}/images/modules-2.4.18-rmk5 \

>;modules_install

最后一步是为模块建立依存关系,不能使用原生的depmod来建立,而需要使用交叉编译工具。需要用到busybox中的depmod.pl脚本,很可 惜,我在busybox1.0.0中,并没有找到这个脚本,所以,还是借用了busybox0.63中scripts中的depmod.pl。

将depmod.pl拷贝至${PREFIX}/bin目录中,也就是交叉编译工具链的bin目录。

#depmod.pl \

>;-k ./vmlinux –F ./System.map \

>;-b ${PRJROOT}/images/modules-2.4.27-rmk5/lib/modules >; \

>;${PRJROOT}/images/modules-2.4.27-rmk5/lib/modules/2.4.27-rmk5/modules.dep

注:后面讨论移植内核和模块内容时,我只会提到内核的拷贝,因为我的系统并没有模块的支持。如果你需要使用模块,只需按相同方法将其拷贝至相应目录即可。

附,内核编译清单

附,内核选择:

内核编译记录:

Code maturity level options                不选

Loadable module support                不选

Processor type and features                根据实际,选择处理器类型

General setup  --->;

Networking support

PCI support

(Any)   PCI access mode

PCI device name database

System V IPC

Sysctl support

(ELF) Kernel core (/proc/kcore) format

Kernel support for ELF binaries

Power Management support

Memory Technology Devices (MTD)  --->;                MTD设备,我用CF卡,不选

Parallel port support  --->;                                        不选

Plug and Play configuration  --->;                                我的系统用不着即插即用,不选

Block devices  --->;

Loopback device support

RAM disk support

(4096)   Default RAM disk size (NEW)

Initial RAM disk (initrd) support

Multi-device support (RAID and LVM)  --->;  不选

Networking options  --->;                 基本上都选了

ATA/IDE/MFM/RLL support  --->;                  用了默认的

Telephony Support  --->;                不选

SCSI support  --->;                 不选

Fusion MPT device support  --->;         不选

I2O device support  --->;                   不选

Network device support  --->;                  根据实际情况选择

Amateur Radio support  --->;                 不选

IrDA (infrared) support  --->;                不选

ISDN subsystem  --->;                        不选

Old CD-ROM drivers (not SCSI, not IDE)  --->;        不选

Input core support  --->;                        不选

Character devices  --->;

Virtual terminal

Support for console on virtual terminal

Standard/generic (8250/16550 and compatible UARTs) serial support

Support for console on serial port

Multimedia devices  --->;                 不选

File systems  --->;

Kernel automounter version 4 support (also supports v3)

Virtual memory file system support (former shm fs)

/proc file system support

Second extended fs support

Console drivers  --->;

VGA text console          调试时接显示器用

剩下三个都不要

Sound  --->;

USB support  --->;

Kernel hacking  --->;

第四章        建立根文件系统

1、建立目录

构建工作空间时,rootfs文件夹用来存放根文件系统,

#cd rootfs

根据根文件系统的基本结构,建立各个对应的目录:

# mkdir bin dev etc lib proc sbin tmp usr var root home

# chmod 1777 tmp

# mkdir usr/bin usr/lib usr/sbin

# ls

dev  etc  lib  proc  sbin  tmp  usr  var

# mkdir var/lib var/lock var/log var/run var/tmp

# chmod 1777 var/tmp

对于单用户系统来说,root和home并不是必须的。

准备好根文件系统的骨架后,把前面建立的文件安装到对应的目录中去。

2、拷贝链接库

把uclibc的库文件拷贝到刚才建立的lib文件夹中:

# cd ${PREFIX}/lib

[root@skynet lib]# cp *-*.so ${PRJROOT}/rootfs/lib

[root@skynet lib]# cp -d *.so.[*0-9] ${PRJROOT}/rootfs/lib

3、        拷贝内核映像和内核模块

因为没有模块,所以拷贝模块就省了,

新建boot目录,把刚才建立好的内核拷贝过来

# cd /home/kendo/control-project/daq-module/rootfs/

# mkdir boot

# cd ${PRJROOT}/images

# cp bzImages-2.4.18-rmk5 /home/kendo/control-project/daq-module/rootfs/boot

4、        建立/dev下边的设备文件

在linux中,所有的的设备文件都存放在/dev中,使用mknod命令创建基本的设备文件。

mknod命令需要root权限,不过偶本身就是用的root用户,本来是新建了一个用户专门用于嵌入式制作的,不过后来忘记用了……

# mknod -m 600 mem c 1 1

# mknod -m 666 null c 1 3

# mknod -m 666 zero c 1 5

# mknod -m 644 random c 1 8

# mknod -m 600 tty0 c 4 0

# mknod -m 600 tty1 c 4 1

# mknod -m 600 ttyS0 c 4 64

# mknod -m 666 tty c 5 0

# mknod -m 600 console c 5 1

基本的设备文件建立好后,再创建必要的符号链接:

# ln -s /proc/self/fd fd

# ln -s fd/0 stdin

# ln -s fd/1 stdout

# ln -s fd/2 stderr

# ls

console  fd  mem  null  random  stderr  stdin  stdout  tty  tty0  tty1  ttyS0  zero

设备文件也可以不用手动创建,听说RedHat /dev下的脚本MAKEDEV 可以实现这一功能,不过没有试过……

基本上差不多了,不过打算用硬盘/CF卡来做存储设备,还需要为它们建立相关文件,因为我的CF在目标机器上是CF-to-IDE,可以把它们等同来对待,先看看Redhat 下边had的相关属性:

# ls -l /dev/hda

brw-rw----    1 root     disk       3,   0 Jan 30  2003 /dev/hda

# ls -l /dev/hda1

brw-rw----    1 root     disk       3,   1 Jan 30  2003 /dev/hda1

对比一下,可以看出,had类型是b,即块设备,主编号为3,次编号从0递增,根限位是

rw-rw----,即660,所以:

# mknod -m 660 hda b 3 0

# mknod -m 660 hda1 b 3 1

# mknod -m 660 hda2 b 3 2

# mknod -m 660 hda3 b 3 3

5、添加基本的应用程序

未来系统的应用程序,基本上可以分为三类:

        基本系统工具,如ls、ifconfig这些……

        一些服务程序,管理工具,如WEB、Telnet……

        自己开发的应用程序

这里先添加基本的系统工具,有想过把这些工具的代码下载下来交叉编译,不过实在是麻烦,用BusyBox,又精简又好用……

将busybox-1.00.tar.gz下载至sysapps目录下,解压:

#tar zxvf busybox-1.00.tar.gz

#cd busybox-1.00

//进入配置菜单

#make TARGET_ARCH=i386 CROSS=i386-linux- PREFIX=${PRJROOT}/rootfs menuconfig

//建立依存关系

#make TARGET_ARCH=i386 CROSS= i386-linux- PREFIX=${PRJROOT}/rootfs dep

//编译

#make TARGET_ARCH=i386 CROSS= i386-linux- PREFIX=${PRJROOT}/rootfs

//安装

#make TARGET_ARCH=i386 CROSS= i386-linux- PREFIX=${PRJROOT}/rootfs install

# cd ${PRJROOT}/rootfs/bin

# ls

addgroup  busybox  chown  delgroup  echo      kill   ls     mv       ping  rm     sleep

adduser   chgrp    cp     deluser   grep      ln     mkdir  netstat  ps    rmdir  umount

ash       chmod    date   dmesg     hostname  login  mount  pidof    pwd   sh     vi

一下子多了这么多命令……

配置busybox的说明:

A、如果编译时选择了:

Runtime SUID/SGID configuration via /etc/busybox.conf

系统每次运行命令时,都会出现“Using fallback suid method ”

可以将它去掉,不过我还是在/etc为其建了一个文件busybox.conf搞定;

B、

Do you want to build BusyBox with a Cross Compiler?                                      (i386-linux-gcc) Cross Compiler prefix

这个指明交叉编译器名称(其实在编译时的命令行已指定过了……)

C、安装选项下的(${PRJROOT}/rootfs) BusyBox installation prefix,这个指明了编译好后的工具的安装目录。

D、静态编译好还是动态编译好?即是否选择

[ ] Build BusyBox as a static binary (no shared libs)

动态编译的最大好处是节省了宝贵空间,一般来说都是用动态编译,不过我以前动态编译出过问题(其实是库的问题,不关busybox的事),出于惯性,我选择了静态编译,为此多付出了107KB的空间。

E、其它命令,根据需要,自行权衡。

6、系统初始化文件

内核启动时,最后一个初始化动作就是启动init程序,当然,大多数发行套件的Linux都使用了与System V init相仿的init,可以在网上下载System V init套件,下载下来交叉编译。另外,我也找到一篇写得非常不错的讲解如何编写初始化文件的文件,bsd-init,回头附在后面。不过,对于嵌入式系 统来讲,BusyBox init可能更为合适,在第6步中选择命令的时候,应该把init编译进去。

#cd ${PRJROOT}/rootfs/etc

#vi inittab

我的inittal文件如下:

#指定初始化文件

::sysinit:/etc/init.d/rcS

#打开一个串口,波特率为9600

::respawn:/sbin/getty 9600 ttyS0

#启动时执行的shell

::respawn:/bin/sh

#重启时动作

::restart:/sbin/init

#关机时动作,卸载所有文件系统

::shutdown:/bin/umount -a –r

保存退出;

再来编写rcS脚本:

#mkdir  ${PRJROOT}/rootfs/etc/init.d

#cd ${PRJROOT}/rootfs/etc/init.d

#vi rcS

我的脚本如下:

#!/bin/sh

#Set Path

PATH=/sbin:/bin

export PATH

syslogd -m 60

klogd

#install /proc

mount -n -t proc none /proc

#reinstall root file system by read/write mode(need: /etc/fstab)

mount -n -o remount,rw /

#reinstall /proc

mount -n -o remount,rw -t proc none /proc

#set lo ip address

ifconfig lo 127.0.0.1

#set eth0 ip address

#当然,这样子做只是权宜之计,最后做的应该是在这一步引导网络启动脚本,像RedHat

#那样,自动读取所有指定的配置文件来启动

ifconfig eth0 192.168.0.68 netmask 255.255.255.0

#set route

#同样的,最终这里应该是运行启动路由的脚本,读取路由配置文件

route add default gw 192.168.0.1

#还差一个运行服务程序的脚本,哪位有现成的么?

#网卡/路由/服务这三步,事实上可以合在一步,在rcS这一步中,做一个循环,运行指定启动目录下的所有脚,先将就着这么做吧,确保系统能够正常启动了,再来写这个脚本。

#set hostname

hostname MyLinux

保存退出。

编写fstab文件

#vi fstab

我的fstab很简单:

/dev/hda1 / ext2 defaults 1 1

none /proc proc defaults 0 0

第五章        让MyLinux能够启动

前一章,我们把编译好的内核、应用程序、配置文件都拷贝至rootfs目录对应的子目录中去了,这一步,就是把这些文件移植至目标机的存储器。这里,我是 先另外拿一块硬盘,挂在我的开发机上做的测试,因为我的本本用来写文档,PC机用来做开发机,已经没有另外的机器了……但是本章只是讲述一个一般性的过 程,并不影响你直接在目标主机上的工作。

因为以后目标机识别硬盘序号都是hda,而我现在直接挂上去,则会是hdb、hdc……这样,安装lilo时有点麻烦(虽然也可以实现)。所以我想了另一个办法:

        把新硬盘挂在IDE0的primary上,进入linux后,会被认为是had;

        原来主机的装Redhat的硬盘,我将它从IDE0的primary上变到了IDE1 的primary,因为它的lilo早已装好,基本上不影响系统的使用;

分区和格式化

BIOS中改为从第二个硬盘启动;也就是从我原来开发机启动,新的硬盘被识别成了had。

#fdisk /dev/hda

用d参数删除已存在的所有分区

用n参数新建一个分区,也是就/dev/hda1

格式化

#mkfs.ext2 /dev/hda1

安装bootloader

因为我是X86平台,所以直接用了lilo,如果你是其这平台,当然,有许多优秀的bootloader供你选择,你只需查看其相应的说明就可以了。

编译lilo配置文件,我的配置文件名为target.lilo.conf,置于${PRJROOT}/rootfs/etc目录。内容如下所示:

boot=/dev/hda

disk=/dev/hda

bios=0x80

image=/boot/bzImage-2.4.18-rmk5

label=Linux

root=/dev/hda1

append="root=/dev/hda1"

read-only

//新建文件夹,为mount做新准备

#mkdir /mnt/cf

//把目标硬盘mount上来

#mount –t ext2 /dev/hdc1 /mnt/cf

回到rootfs

#cd ${PRJROOT}/rootfs

拷贝所有文件至目标硬盘

#cp –r * /mnt/cf

这样,我们所有的文件都被安装至目标硬盘了,当然,它还不能引导,因为没有bootloader。使用如下命令:

# lilo -r /mnt/cf -C etc/target.lilo.conf

Warning: LBA32 addressing assumed

Added Linux *

-r :改变根目标为/mnt/cf ,这样配置文件其实就是/mnt/cf/etc/target.lilo.conf,也就是我们先前建立的文件。

当然,完成这一步,需要lilo22.3及以后版本,如果你的版本太旧,比如Redhat9.0自带的,就会出现下面的信息:

#lilo –r /mnt/cf –C etc/target.lilo.conf

Fatal: open /boot/boot.b: No such file or directory

这时,你需要升级你的lilo,或者重新安装一个。

启动系统

#umount /mnt/cf

#reboot

将BIOS改为从IDE0启动,也就是目标硬盘。如果一切顺利,你将顺利进入一个属于你的系统。

回头再来看看我们的工作空间吧

[root@skynet lib]# df /dev/hda1

Filesystem           1K-blocks      Used Available Use% Mounted on

/dev/hda1              3953036      1628   3750600   1% /mnt/cf

总共花去了我1628KB的空间,看来是没有办法放到软盘里边去了^o^,不过一味求小,并不是我的目标。

[root@skynet skynet]# ls ${PRJROOT}

bootloader build-tools  debug  doc  images  kernel  rootfs  sysapps  tmp  tools

这几个目录中的文件,呵呵,与本文一开头规划的一样

[root@skynet skynet]# ls build-tools/

buildroot  buildroot-0.9.27.tar.tar

包含了buildroot源码及压缩包,事实上buildroot下边还包括了GNU其它工具的源码、编译文件等诸多内容,是我们最重要的一个文件夹,不过到现在它已经没有多大用处了,如果你喜欢,可以将它删除掉(不建议)。

[root@skynet skynet]# ls images

2.4.18-rmk5  bzImage-2.4.18-rmk5  System-2.4.18-rmk5  vmlinux-2.4.18-rmk5

内核映像及配置文件等,如果你有模块,因为还有相应的目录

[root@skynet skynet]# ls kernel/

linux-2.4.27  linux-2.4.27.tar.bz2

内核源码及压缩包

[root@skynet skynet]# ls rootfs/

bin  boot  dev  etc  home  lib  linuxrc  proc  root  sbin  tmp  usr  var

制作好的根文件系统,重中之重,注意备份……

[root@skynet skynet]# ls sysapps/

busybox-1.00  busybox-1.00.tar.gz

busybox-1.00源码包,或许你还要继续添加/删除一些命令……

[root@skynet skynet]# ls tools

bin  i386-linux  i386-linux-uclibc  include  info  lib  man

这个也很重要,我们制作好的交叉开发工具链。如果你要继续开发程序,这个目录重要性就很高了。

其它目录暂时是空的。

第六章        完善MyLinux

关于进一步的调试,你可以在开发机上使用chroot /mnt/cf /bin/sh这样的命令,以使我们在目标根文件系统上工作。

支持多用户

因为我在编译busybox时,已经将它的多用户那一大堆命令编译了进来。现在关键是的要为其建立相应的文件;

进入原来的开发机,进入rootfs目录,切换根目录

#chroot rootfs/ /bin/sh

A、        建立/etc/passwd文件,我的文件内容如下:

root:x:0:0:root:/root:/bin/bash

B、        建立/etc/group文件,我的文件内容如下:

root:x:0:

bin:x:1:

sys:x:2:

kmem:x:3:

tty:x:4:

tape:x:5:

daemon:x:6:

disk:x:7:

C、        为root建立密码

#passwd root

试试用addgroup/addusr……这堆命令。然后重启,从目标硬盘上启动;从console口,9600登陆试试(因为我在inittab中启用了ttyS0,我未来的目标机,是没有显卡的,需要从console口或SSH进去管理)

MyLinux login: root

Password:

BusyBox v1.00 (2004.10.10-04:43+0000) Built-in shell (ash)

Enter 'help' for a list of built-in commands.

~ #

成功了……

增加WEB Server

Busybox里边有httpd选项,不过我编译时并没有选择,所以还是自己来安装。我使用的软件是thttpd-2.25b.tar.gz,将它移至sysapps目录下。

[root@skynet sysapps]# tar zxvf thttpd-2.25b.tar.gz

[root@skynet sysapps]# cd thttpd-2.25b

//配置

[root@skynet thttpd-2.25b]# CC=i386-linux-gcc ./configure --host=$TARGET

……

i386-linux-gcc  -static htpasswd.o -o htpasswd -lcrypt

make[1]: warning:  Clock skew detected.  Your build may be incomplete.

make[1]: Leaving directory `/home/skynet/sysapps/thttpd-2.25b/extras'

//拷贝至根文件目录

[root@skynet thttpd-2.25b]# cp thttpd ${PRJROOT}/rootfs/usr/sbin

//trip处理

[root@skynet thttpd-2.25b]# i386-linux-strip ${PRJROOT}/rootfs/usr/sbin/thttpd

剩下的,就发挥各人的想像吧……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值