分类: LINUX
2008-04-13 22:50:39
本文对 uClinux 在 S
嵌入式系统的特点在于:针对特定的应用,使用“量体裁衣”的方式将所需的功能嵌入到各种应用系统当中。其早期主要应用于军事及航空、航天领域,之后逐步被工业控制、汽车电子、通信和消费电子领域广泛使用。与此同时,各种新兴的应用也对嵌入式系统的处理能力、通信能力等方面提出了更高的要求。
S
uClinux 是针对通信和控制领域的嵌入式操作系统,其主要的优势在于开放源代码、稳定、强大的网络通信功能以及其精简性。其内核功能与 Linux 基本相同,只是对内存管理和进程管理进行了改写,主要应用于没有 MMU 的微处理器平台,如 S
本文根据笔者在实际工作中积累的经验,对 uClinux 在S
Uclinux 的内核现已发展至 2.6 版本。然而,考虑到嵌入式应用大多针对特定需求,开发者往往更关注诸如能耗、空间占用、开发速度、向后兼容等问题,因而在实际开发中 2.4 和 2.6 版本的 uClinux 都会根据项目情况被采用。根据笔者在实际工作中的经验,本文下面将对 2.4 和 2.6 版本的 uClinux 移植到 S
的移植
Step 1:下载内核压缩包和交叉编译工具包
笔者使用的是分别是:uClinux-dist-20030522.tar.gz 和 arm-elf-tools-20030314.sh。将内核压缩包解压至开发主机,然后安装 arm-linux 交叉编译工具,为后续的编译做好准备。
Step 2:修改 Makefile 文件
在# normal make targets下面的Section加入:
.PHONY: images images: $(MAKE) -C $(VENDDIR) images all: subdirs romfs modules modules_install image |
上述语句将在make image的时候生成压缩的映像文件(image)。
首先,找到语句
genromfs -v -V "ROMdisk" -f $(ROMFSIMG) -d $(ROMFSDIR) |
在其下方加入:
arm-elf-ld -r -o $(ROOTDIR)/$(LINUXDIR)/romfs.o \ -b binary $(ROMFSIMG) |
此语句的作用是产生 romfs.o 供后续生成整体的映像文件使用。
同时,在此 Makefile 的最后加入:
images: arm-elf-objcopy -O binary -R .note -R .comment -S $(ROOTDIR)/$(LINUXDIR)/linux \ $(IMAGEDIR)/uclinux_ram.bin cp $(ROOTDIR)/$(LINUXDIR)/arch/armnommu/boot/zImage \ $(IMAGEDIR)/uclinux_rom.bin |
上述语句将在交叉编译的最后将所有生成的目标文件 Dump 到可以下载到开发板中的映像文件中(注意,这里提供两种形式:ROM 启动的 uclinux_rom.bin 和 RAM 启动的 uclinux_ram.bin)
将此文件中的下列语句:
$(LD) $(ZLDFLAGS) $(HEAD) $(OBJS) piggy.o -o $(LINUX) |
修改为:
$(LD) $(ZLDFLAGS) $(HEAD) $(OBJS) piggy.o $(LIBGCC) -o $(LINUX) |
此语句将 arm-elf-tools 库的 libgcc 链接入 uClinux 的 kernel 中。
Step 3:配置操作系统地址转换
由于 S
#define __virt_to_phys__is_a_macro #define __virt_to_phys(vpage) ((unsigned long) (vpage)) #define __phys_to_virt__is_a_macro #define __phys_to_virt(ppage) ((void *) (ppage)) #define __virt_to_bus__is_a_macro #define __virt_to_bus(vpage) ((unsigned long) (vpage)) #define __bus_to_virt__is_a_macro #define __bus_to_virt(ppage) ((void *) (ppage)) |
Step 4:配置romfs
由于uClinux缺省使用romfs作为根文件系统,因此需要对此进行配置:
在*(.got) /* Global offset table */语句后加入下列语句,从而将上文Step 2 (2) 中生成的romfs.o包含到.text段中:
romfs_data = .; romfs.o romfs_data_end = .; |
此文件是romfs的配置文件,找到“arena[] = {”语句,在其花括号中加入下列3行:
#ifdef CONFIG_ARCH_SAMSUNG {0, romfs_data, -1}, #endif |
Step 5:其他修改
此文件中有一个错误语句:
mov r0, #0 #ifdef CONFIG_CPU_WITH_CACHE |
将其修改为:
#ifdef CONFIG_CPU_WITH_CACHE mov r0, #0 |
至此,经过修改的uClinux内核已经可以在开发主机上使用make dep, make lib_only, make user_only, make romfs, make image和make 生成 uclinux_rom.bin 和uclinux_ram.bin 映像文件 并在开发板上顺利运行至uClinux欢迎界面和等待命令输入提示符。需要注意的是, uclinux_ram.bin是压缩过的内核映像,可以使用调试器直接烧写至SDRAM中运行,而 uclinux_rom.bin是没有压缩过的内核映像,需要烧写入FLASH中运行。因此,在调试过程中,一般将待调试的内核映像uclinux_ram.bin通过调试器或Bootlaoder(Redboot等)写入SDRAM中进行调试,待完全调试好之后再将最终uclinux_rom.bin版本烧写入FLASH中固化。
的移植
Step 1:下载内核压缩包和交叉编译工具包
对于2.6内核的uClinux,需要下载uClinux-dist-20051014.tar.gz内核压缩包以及Linux 2.6版本内核及补丁:linux-
Step 2:为uClinux更新标准Linux 2.6版本内核
执行下列命令将uClinux-dist-20051014的内核替换为标准Linux的2.6内核:
tar -zxvf uClinux-dist-20051014.tar.gz cp linux- cd uClinux-dist tar –xjvf linux- gzip -dc ../linux- rm -rf linux-2.6.x mv linux- |
Step 3:配置内核中S
由于Step 2 中的补丁并没有包括专门针对S
$cp ../linux-2.6.x/arch/armnommu/configs/espd_4510b_defconfig \ ../linux-2.6.x/vendor/Samsung/4510B/config.linux-2.6.x |
同时,由于没有特别针对Linux 2.6内核的S
$cp ../linux-2.6.x/vendor/Samsung/4510B/config.vendor-2.4.x \ ../linux-2.6.x/vendor/Samsung/4510B/config.vendor-2.6.x |
Step 4:修改Makefile文件
由于现有的Makefile当中并没有包含编译可烧写至嵌入式存储系统中的映像文件的相应命令,因此需要在此Makefile中添加:
image: [ -d $(IMAGEDIR) ] || mkdir -p $(IMAGEDIR) genromfs -v -V "ROMdisk" -f $(ROMFSIMG) -d $(ROMFSDIR) arm-uclinux-ld -r -o $(ROOTDIR)/$(LINUXDIR)/romfs.o -b binary $(ROMFSIMG) $(CROSS_COMPILE)objcopy -O binary --remove-section=.romvec \ --remove-section=.text --remove-section=.ramvec \ --remove-section=.init \ --remove-section=.bss --remove-section=.eram $(ROOTDIR)/$(LINUXDIR)/linux $(IMAGEDIR)/linux.data |
Step 5:配置romfs
与移植uClinux 2.4内核相同,需要修改下列文件以对romfs进行支持:
在*(.got) /* Global offset table */语句后加入下列语句,从而romfs.o包含到.text段中。
romfs_data = .; romfs.o romfs_data_end = .; |
添加变量romfs_start和romfs_end:
extern int _stext, _text, _etext, _edata, _end; extern int romfs_start,romfs_end; |
同时,对内核启动时的default command line进行设置:
char *from = default_command_line; sprintf(default_command_line, "root=/dev/ram0 initrd=0x%08lx", \ (unsigned long)&romfs_start); |
至此,uClinux 2.6版本的内核移植完成,可以通过make dep, make lib_only, make user_only, make romfs, make image和make编译出可以烧写入SDRAM或FLASH的内核映像文件。
在嵌入式开发和调试中,最常用的网络服务是使用Telnet登录嵌入式开发板以及使用NFS文件系统。本文下面将根据笔者的实际经验对uClinux 2.4环境下这些网络服务的配置进行说明。
中Telnet服务器的配置
由于Telnet使用串口建立终端进行通信,因此为了在嵌入式开发板上架设Telnet服务器,首先需要修改uClinux的内核配置。具体来说,需要在make menuconfig时进行下列配置:
由于在上面的内核配置中选择了对虚拟终端的支持,因此需要手动在../linux-2.4-x/.config中加入CONFIG_DUMMY_KEYB=y,否则编译时会出现编译错误。
同时,由于uClinux内核自身的问题,按照上述方式配置的Telnet在连接时总是出现login用户名和密码验证错误的问题。此问题可以通过修改../linux-2.4-x/user/tinylogin/login.c,省略Telnet连接时的用户名/密码验证来解决:
extern int login_main(int argc, char **argv) { char name[32]; char tty[BUFSIZ]; … #ifdef TLG_FEATURE_SHADOWPASSWDS struct spwd *spwd = NULL; #endif /* TLG_FEATURE_SHADOWPASSWDS */ char **envp = environ; initenv(); /* bypass username/password auth process. Directly start Sash Shell */ shell("/bin/sh", (char *) 0); /* exec the shell finally. */ return (0); } |
重新编译按照上述方法进行修改的内核并将编译好的映像文件烧写入FLASH,系统上电启动后完成后运行:
/bin> inetd & |
开发人员便可以通过Telnet访问嵌入式开发板了。
中NFS的配置
为了在S
选中NFS file system support和Provide NFSv3 client support。
选中portmap,提供端口映射功能。
选中mount和mount: support NFS mounts。
完成上述配置之后,重新编译内核,新内核便可提供NFS Server和Client的支持。在通常情况下, 一般会使用S
/bin > portmap & /bin > mount 192.168.0.8:/nfs_mount /var |
uClinux目前并不支持使用insmod动态加载驱动程序,因此S
中字符驱动程序的开发
开发人员在编写完所需的字符类型驱动程序之后,需要将驱动程序源代码置于
../linux-2.4.x/driver/char目录下,同时修改同级目录下的Makefile。例如,笔者完成了一个S
obj_y += lcd.o |
同时,为了能够在uClinux启动时自动初始化此设备,还需要修改../linux-2.4.x/driver/char/mem.c文件,在其中加入:
extern void lcd_init(void); |
lcd_init(); |
函数int __init chr_dev_init(void)中,字符设备的初始化函数将统一被调用,并完成各个字符驱动file_operations数据结构的注册。初始化好之后就可以使用这些字符设备了。
中网络驱动程序的开发
在uClinux中加入网络驱动设备比较简单,只需将编写好的网络驱动程序置于../linux-2.4.x/driver/net,再修改../linux-2.4.x/driver/net/Makefile即可。例如,笔者编写了HDLC的驱动程序
s
obj-y += s |
uClinux执行make时会根据此Makefile产生新的驱动模块,并将其链接入net.o,系统启动时将会统一加载所有的网络驱动程序。
特殊网络设备驱动程序开发
由于嵌入式应用中往往会对网络应用有特别的需求,因而需要在嵌入式开发板中支持一些不太常用的网络设备(如HDLC设备等)。对此感兴趣的读者可以参考笔者的另外一篇论文《基于 S3C4510B 和 uClinux 的 HDLC 接口的设计与实现》。
除了驱动程序,开发人员还需要在S
Step 1:在../linux-2.4.x/user目录中为应用程序建立相应目录树结构
例如,笔者在开发中需要开发一个在S
(../linux-2.4.x/user/lcd/write_lcd.c)。
Step 2:修改2级Makefile文件
由于在../linux-2.4.x/user目录下加入了新的目录树结构,因此需要修改../linux-2.4.x/user/Makefile文件,使运行make时能够进入新目录进行编译。在上面的例子中,需要在
../linux-2.4.x/user/Makefile中加入:
dir_y += lcd |
以指示系统在编译时进入lcd目录。
新建目录中必须提供一个Makefile供make时使用,下面是笔者在实际开发中使用的此处Makefile模板,不同的应用程序只需更改模板中的EXEC和OBJS宏即可:
EXEC = write_lcd OBJS = write_lcd.o CFLAGS += -I. all: $(EXEC) $(EXEC): $(OBJS) $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS) romfs: $(ROMFSINST) /bin/$(EXEC) clean: -rm -f $(EXEC) *.elf *.gdb *.o |
完成上述步骤之后,运行make user_only编译命令时,将会对../linux-2.4.x/user/目录下的用户程序进行编译,并将编译出的可执行文件置于romfs中的bin目录下。
本文根据笔者在实际工作中的经验,对uClinux在S