Linux3.0最小系统移植
先创建FL2440整个项目的目录框架
[wuyujun@wuyujunlocalhost ~]$ mkdir fl2440
[wuyujun@wuyujunlocalhost ~]$ cd fl2440
[wuyujun@wuyujunlocalhost fl2440]$mkdir -p {crosstool,bootloader,linux/{kernel,rootfs},driver,3rdparty,program,images}
[wuyujun@wuyujunlocalhost fl2440]$ tree
.
├── 3rdparty
├── bootloader
├── crosstool
├── driver
├── images
├── linux
│ ├── kernel
│ └── rootfs
└── program
9 directories, 0 files
3rdparty:今后移植到ARM开发板上的第三方应用程序软件包
bootloader:u-boot移植代码
crosstool:交叉编译器
drivers:今后写的驱动文件
images:编译出的image文件,如linux内核,根文件系统等
linux:kernel:Linux内核源码路径
roofts:根文件系统目录树路径
program:我们今后自己写的应用程序
Linux内核下载:https://www.kernel.org/
[wuyujun@wuyujunlocalhost linux]$ wget https://mirrors.edge.kernel.org/pub/linux/kernel/v3.x/linux-3.0.tar.bz2
[wuyujun@wuyujunlocalhost linux]$ tar -xjf linux-3.0.tar.bz2
[wuyujun@wuyujunlocalhost linux]$ ls
linux-3.0 linux-3.0.tar.bz2
[wuyujun@wuyujunlocalhost linux]$ cd linux-3.0
[wuyujun@wuyujunlocalhost linux-3.0]$ ls
arch CREDITS drivers include Kbuild lib mm REPORTING-BUGS security usr
block crypto firmware init Kconfig MAINTAINERS net samples sound virt
COPYING Documentation fs ipc kernel Makefile README scripts tools
内核目录说明
1、documentation:提供文档帮助。关于内核的一些说明信息,在这个目录下会有帮助手册。2、arch:arch是architecture的缩写。所有与体系结构相关的代码都在这个目录3、drivers:设备驱动文件4、fs:文件系统目录5、include:这个目录包含了内核中大部分的头文件。6、init:内核的初始化代码。7、ipc:IPC(进程间通信)。它包含了共享内存、信号量及其他形式的IPC代码。8、kernel:内核中核心的部分,包括进程的调度(sched.c)、进程控制和模块化等。9、mm:内存管理代码。10、net:网络模块代码12、block:部分块设备驱动13、firmware:驱动固件文件。14、securtity:这个目录下包含了不同的Linux安全模型的代码。15、crypto:加密、压缩、crc校验和代码。16、scripts:配置内核、裁剪的相关工具脚本17、sound:声卡驱动以及其他声音相关的源码。18、samples:一些内核编程的范例19、virt和usr:用于实现压缩和打包的CPIO以及包含了虚拟化代码。20、tools这个文件夹中包含了和内核交互的工具。
Linux内核的版本号可以从源代码的顶层目录下的Makefile中看到
[wuyujun@wuyujunlocalhost linux-3.0]$ vim Makefile
VERSION”和“PATCHLEVEL”组成主版本号,稳定版本的主版本号用偶数表示,开发中的版本号用奇数表示,“SUBLEVEL”称为次版本号,它不分奇偶,顺序递增,每隔1~2个月发布一个稳定版本。“EXTRAVERSION”称为扩展版本号,它不分奇偶,顺序递增,每周发布几次扩展本版号。
源码修改
SMDK2440上使用的是16MHz的晶振,而FL2440上使用的是12MHz的晶振,根据fl2440开发板,以mach-smdk2440.c为原型来进行修改
修改一:
[wuyujun@wuyujunlocalhost linux-3.0]$vim arch/arm/mach-s3c2440/mach-smdk2440.c
s3c24xx_init_clocks(16934400);改成s3c24xx_init_clocks(12000000);
修改二:
因为u-boot给Linux内核传的machine ID值为1999,而Linux内核里smdk2440开发板对应的machine ID是362,所以我们要修改内核代码让smdk2440的machine ID与u-boot里的保持一致,这里我们在源码中将两个machine ID值互换
[wuyujun@wuyujunlocalhost linux-3.0]$ vim arch/arm/tools/mach-types
s3c2440 ARCH_S3C2440 S3C2440 362 改成s3c2440 ARCH_S3C2440 S3C2440 1999
mini2440 MACH_MINI2440 MINI2440 1999改成mini2440 MACH_MINI2440 MINI2440 362
修改三:
改串口驱动设备名字
[wuyujun@wuyujunlocalhost linux-3.0]$ vim drivers/tty/serial/samsung.c
#define S3C24XX_SERIAL_NAME "ttySAC"改成#define S3C24XX_SERIAL_NAME "ttyS"
修改四:
修改顶层Makefile,将交叉编译器换成制作好的交叉编译器
ARCH ?= $(SUBARCH)改成ARCH ?= $arm
CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)改成CROSS_COMPILE ?= /opt/xtools/arm920t/bin/arm-linux-
修改五:
驱动dm9000网卡
[wuyujun@wuyujunlocalhost linux-3.0]$ vim arch/arm/mach-s3c2440/mach-smdk2440.c
在mach-smdk2440.c文件中添加如下代码
#include<linux/dm9000.h> //添加网卡头文件
/* add DM9000 ethernet drivers ,whitch is bodify by liuchengdeng */
#define DM9000_BASE (S3C2410_CS4 + 0x300)
static struct resource s3c_dm9000_resource[] = {
[0] = {
.start = DM9000_BASE,
.end = DM9000_BASE + 3,
.flags = IORESOURCE_MEM
},
[1] = {
.start = DM9000_BASE + 4,
.end = DM9000_BASE + 7,
.flags = IORESOURCE_MEM
},
[2] = {
.start = IRQ_EINT7,
.end = IRQ_EINT7,
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
}
};
/*
* The DM9000 has no eeprom, and it's MAC address is set by
* the bootloader before starting the kernel.
*/
static struct dm9000_plat_data s3c_dm9000_pdata = {
.flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),
};
static struct platform_device s3c_device_dm9000 = {
.name = "dm9000",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_dm9000_resource),
.resource = s3c_dm9000_resource,
.dev = {
.platform_data = &s3c_dm9000_pdata,
},
};
添加成员变量
在static struct platform_device *smdk2440_devices[] __initdata 结构体下添加如下成员变量:
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_ohci,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_dm9000,//添加dm9000成员变量
};
linux的源码编译系统中有一个很巧妙的铁三角关系,导致对每一个特定用途的嵌入式Linux系统image的过程显得没那么复杂。这个铁三角分别是Kconfig、.config和Makefile文件,他们三者之间的关系简单来说就是去饭店点菜:Kconfig是菜单,.config就是你点的菜,Makefile是做法。
Kconfig:一个文本形式的文件,存在内核源码中的每一个文件夹下,内核配置命令make menuconfig读取相应的Konfig文件生成菜单界面;.config:隐藏文件存放在内核源码顶层目录中,make menuconfig命令配置的结果,里面的每个选项用来指导Makefile哪些C文件需要编译,哪些不需要编译;
Makefile:一个文本形式的文件,存在内核源码中的每一个文件夹下,用来控制编译该目录下的源码编译;[wuyujun@wuyujunlocalhost linux-3.0]$ make s3c2410_defconfig
[wuyujun@wuyujunlocalhost linux-3.0]$ export TERM=vt100
[wuyujun@wuyujunlocalhost linux-3.0]$ make menuconfig
使用S3C2440做的SMDK2440开发板,所有其他的开发板都不应该选择
System Type --->
S3C2400 ---> 里面全部不选
S3C2410 ---> 里面全部不选
S3C2412 ---> 里面全部不选
S3C2416 Machines ---> 里面全部不选
S3C2440 and S3C2442 Machines --->
[*] SMDK2440
[*] SMDK2440 with S3C2440 CPU module
其它全部不选
S3C2443 Machines ---> 里面全部不选
.......
我们的交叉编译器使用的是EABI接口,所以这里一定要修改配置,否则跑不起来。(我一开始这个地方漏掉了所以导致了后面的错误)
Kernel Features ----->
[*] Use the ARM EABI to compile the kernel
[*] Allow old ABI binaries to run with this kernel (EXPERIMENTAL) (NEW)
为了防止串口驱动冲突,还需 make menuconfig设置
Device Drivers --->
Character devices --->
Serial drivers --->
把<*> 8250/16550 and compatible serial support取消
写个shell脚本
[wuyujun@wuyujunlocalhost linux-3.0]$ vim build.sh
#!/bin/bash
make
mkimage -A arm -O linux -T kernel -C none -a 30008000 -e 30008040 -n "Linux Kernel" -d arch/arm/boot/zImage linuxrom-s3c2440.bin
chmod a+x linuxrom-s3c2440.bin
内核编译(make)之后会生成两个文件,一个Image,一个zImage,其中Image为内核映像文件,而zImage为内核的一种映像压缩文件,Image大约为4M,而zImage不到2M。而uImage是u-boot专用的映像文件,它是在zImage之前加上一个长度为64字节的“头”,说明这个内核的版本、加载位置、生成时间、大小等信息;其0x40之后与zImage没区别。zImage并不能被u-boot的bootm命令启动,在用 bootm 命令引导内核的时候,bootm 需要读取一个 64 字节的文件头,来获取这个内核映象所针对的 CPU 体系结构、OS、加载到内存中的位置、在内存中入口点的位置以及映象名等等信息。这样 bootm 才能为 OS 设置好启动环境,并跳入内核映象的入口点。而mkimage 就是添加这个文件头的专用工具。
mkimage 工具的使用:
参数说明:
-A 指定 CPU 的体系结构,可用值有:alpha、arm 、x86、ia64、mips、mips64、
ppc 、s390、sh、sparc 、sparc64、m68k 等
-O 指定操作系统类型,可用值有:openbsd、netbsd、freebsd、4_4bsd、linux、
svr4、esix、solaris、irix、sco、dell、ncr、lynxos、vxworks、psos、qnx、u-boot、
rtems、artos
-T 指定映象类型,可用值有:standalone、kernel、ramdisk、multi、firmware、script、
filesystem
-C 指定映象压缩方式,可用值有:none 不压缩(一般使用这个,因为 zImage 是已经被 bzip2 压缩过的自解压内核) gzip 用 gzip 的压缩方式bzip2 用 bzip2 的压缩方式
-a 指定映象在内存中的加载地址,映象下载到内存中时,要按照用 mkimage 制作映象
时,这个参数所指定的地址值来下载
-e 指定映象运行的入口点地址,这个地址就是-a 参数指定的值加上 0x40(因为前面有个mkimage 添加的 0x40 个字节的头)
-n 指定映象名
-d 指定制作映象的源文件
mkimage -A arm -O linux -T kernel -C none -a 30008000 -e 30008040 -n "Linux Kernel" -d arch/arm/boot/zImage linuxrom- s3c2440.bin
-A arm 指定ARCH为arm
-O linux 指定操作系统(OS)为Linux
-T kernel 指定类型(Type)为内核
-C none 指定压缩类型为未压缩,zImage里有自解压的代码;
-a 30008000 指定Image加载的地址, u-boot下使用tftp命令下载linux内核到内存的相应地址
-e 30008040 指定Linux内核的入口地址, uImage的地址在30008000,uImage是在zImage前面加了64字节(0x40)头,所以内核zImage入口地址为30008040。
-n "Linux Kernel" 指定Image的名字
-d arch/arm/boot/zImage 指定zImage文件所在位置
linuxrom-s3c2440.bin 指定生成的uImag
[wuyujun@wuyujunlocalhost linux-3.0]$./build.sh
-bash: mkimage: command not found
这是因为之前没做U-boot的移植编译,没有mkimage工具,下面就对着实验室文档傻瓜式安装了
[wuyujun@wuyujunlocalhost u-boot-2010.09]$ wget ftp://master.iot-yun.club/fl2440/src/u-boot-2010.09.tar.bz2
[wuyujun@wuyujunlocalhost u-boot-2010.09]$ wget ftp://master.iot-yun.club/fl2440/src/u-boot-2010.09-fl2440.patch
[wuyujun@wuyujunlocalhost u-boot-2010.09]$ tar -xjf u-boot-2010.09.tar.bz2
[wuyujun@wuyujunlocalhost u-boot-2010.09]$ patch -p0 < u-boot-2010.09-fl2440.patch
[wuyujun@wuyujunlocalhost u-boot-2010.09]$ cd u-boot-2010.09
[wuyujun@wuyujunlocalhost u-boot-2010.09]$ ls u-boot-s3c2440.bin
[wuyujun@wuyujunlocalhost u-boot-2010.09]$ sudo cp tools/mkimage /usr/bin/
Diff和Patch介绍
这里要介绍一下diff和patch的使用
diff 和 patch 是一对工具,使用这对工具可以获取更新文件与历史文件的差异,并将更新应用到历史文件上。在数学上说,diff就是对两个集合的差运算,patch就是对两个集合的和运算。在数学上我们知道: A - B = C, 这样我们知道 A、B、C中任意两个数都可以得到第三个数:
diff的使用
diff [option] <old_file> <new_file> > ***.patch
(1)[option]选项介绍:
-a Treat all files as text and compare them line-by-line, even if they do not seem to be text.
-N,--new-file
In directory comparison, if a file is found in
only one directory, treat it as present but empty
in the other directory.
-r When comparing directories, recursively compare any subdirectories found.
-u Use the unified output format.
一般而言,如果文件夹中只有代码文件,则option选项可以选为-Nur,但如果代码文件夹里面还有媒体文件之类,则最好选择-Nura,这样在使用patch命令打补丁的时候便可生成对应的媒体文件。
当然,-Nur与-Nura生成的patch文件的最大区别是-Nura生成的文件比较大。此外,介绍一些别的option选项:
-N或--new-file:在比较目录时,若文件A仅出现在某个目录中,预设会显示:Only in目录,文件A 若使用-N参数,则diff会将文件A 与一个空白的文件比较;
-u,-U<列数>或--unified=<列数>:以合并的方式来显示文件内容的不同;
-r或——recursive:比较子目录中的文件;
-a或——text:diff预设只会逐行比较文本文件;
patch 命令用于打补丁,补丁文件是使用diff产生的
patch 命令语法
patch [ -b [ -B Prefix ] ] [ -f ] [ -l ] [ -N ] [ -R ] [ -s ] [ -v ] [ -c | -e | -n ] [ -d Directory ] [ -D Define ] [ -F Number ] [ -i PatchFile ] [ -o OutFile ] [ -p Number ] [ -r RejectFile ] [ -x Number ] [ File ]
-p 指定目录级别(从路径全称中除去几层目录),如果补丁文件包含路径名称 ,那么:
-p0 使用完整路径名,选项要从当前目录查找目的文件(夹)
-p1 除去前导斜杠,选项要忽略掉第一层目录,从当前目录开始查找。-p4 除去前导斜杠和前三个目录。
-d Directory 打补丁前,更改当前目录到指定目录
-i PatchFile 从指定文件,而不是从标准输入中读取补丁信息
-R 逆向补丁,可用来在打错补丁后进行文件的恢复
制作补丁
命令:diff -urNa linux-3.0 linux-3.0-my > linux-3.0.patch
说明:
当前路径(./ )下有 linux-3.0 目录是linux-3.0的源码压缩包解压所得(linux-3.0源码未做任何修改的)。
该路径(./ )下有 linux-3.0-my目录是由linux-3.0的源码修改后,符合开发板正常使用的源码(根据linux-3.4.2源码修改后的)。
生成 linux-3.0.patch 补丁文件。
打补丁
命令:patch -p1 < ../linux-3.0.patch
说明:
当前路径为:./linux-3.0,进入到未做任何修改的linux-3.0的目录下执行patch命令
[wuyujun@wuyujunlocalhost linux-3.0]$./build.sh
[fl2440@lingyun]# tftp 30008000 linuxrom-s3c2440.bin
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
could not establish link
Using dm9000 device
TFTP from server 192.168.137.1; our IP address is 192.168.137.3
Filename 'linuxrom-s3c2440.bin'.
Load address: 0x30008000
Loading: T #################################################################
#################################################################
##########################
done
Bytes transferred = 2278304 (22c3a0 hex)
[fl2440@lingyun]# bootm 30008000
不能挂载根文件系统,因为没烧录根系统所以不能正常启动
根系统制作
根文件系统首先是内核启动时所mount的第一个文件系统,内核代码映像文件保存在根文件系统中,而系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行。尽管内核是 Linux 的核心,但文件却是用户与操作系统交互所采用的主要工具。这对 Linux 来说尤其如此,这是因为在 UNIX 传统中,它使用文件 I/O 机制管理硬件设备和数据文件
Linux根文件系统中一般有如下图的几个目录:
1./bin目录
该目录下的命令可以被root与一般账号所使用,由于这些命令在挂接其它文件系统之前就可以使用,所以/bin目录必须和根文件系统在同一个分区中。/bin目录下常用的命令有:cat、chgrp、chmod、cp、ls、sh、kill、mount、umount、mkdir、[、test等。其中“[”命令就是test命令,我们在利用Busybox制作根文件系统时,在生成的bin目录下,可以看到一些可执行的文件,也就是可用的一些命令。
2./sbin 目录
该目录下存放系统命令,即只有系统管理员(俗称最高权限的root)能够使用的命令,系统命令还可以存放在/usr/sbin,/usr/local/sbin目录下,/sbin目录中存放的是基本的系统命令,它们用于启动系统和修复系统等,与/bin目录相似,在挂接其他文件系统之前就可以使用/sbin,所以/sbin目录必须和根文件系统在同一个分区中。/sbin目录下常用的命令有:shutdown、reboot、fdisk、fsck、init等,本地用户自己安装的系统命令放在/usr/local/sbin目录下。
3、/dev目录
该目录下存放的是设备与设备接口的文件,设备文件是Linux中特有的文件类型,在Linux系统下,以文件的方式访问各种设备,即通过读写某个设备文件操作某个具体硬件。比如通过"dev/ttySAC0"文件可以操作串口0,通过"/dev/mtdblock1"可以访问MTD设备的第2个分区。比较重要的文件有/dev/null, /dev/zero, /dev/tty, /dev/lp*等。
4./etc目录
该目录下存放着系统主要的配置文件,例如人员的账号密码文件、各种服务的其实文件等。一般来说,此目录的各文件属性是可以让一般用户查阅的,但是只有root有权限修改。对于PC上的Linux系统,/etc目录下的文件和目录非常多,这些目录文件是可选的,它们依赖于系统中所拥有的应用程序,依赖于这些程序是否需要配置文件。在嵌入式系统中,这些内容可以大为精减。
5./lib目录
该目录下存放共享库和可加载(驱动程序),共享库用于启动系统。运行根文件系统中的可执行程序,比如:/bin /sbin 目录下的程序。
6./home目录
系统默认的用户文件夹,它是可选的,对于每个普通用户,在/home目录下都有一个以用户名命名的子目录,里面存放用户相关的配置文件。
7./root目录
系统管理员(root)的主文件夹,即是根用户的目录,与此对应,普通用户的目录是/home下的某个子目录。
8./usr目录
/usr目录的内容可以存在另一个分区中,在系统启动后再挂接到根文件系统中的/usr目录下。里面存放的是共享、只读的程序和数据,这表明/usr目录下的内容可以在多个主机间共享,这些主要也符合FHS标准的。/usr中的文件应该是只读的,其他主机相关的,可变的文件应该保存在其他目录下,比如/var。/usr目录在嵌入式中可以精减。
9./var目录
与/usr目录相反,/var目录中存放可变的数据,比如spool目录(mail,news),log文件,临时文件。
10./proc目录
这是一个空目录,常作为proc文件系统的挂接点,proc文件系统是个虚拟的文件系统,它没有实际的存储设备,里面的目录,文件都是由内核临时生成的,用来表示系统的运行状态,也可以操作其中的文件控制系统。
11./mnt目录
用于临时挂载某个文件系统的挂接点,通常是空目录,也可以在里面创建一引起空的子目录,比如/mnt/cdram /mnt/hda1 。用来临时挂载光盘、移动存储设备等。
12. /tmp目录
用于存放临时文件,通常是空目录,一些需要生成临时文件的程序用到的/tmp目录下,所以/tmp目录必须存在并可以访问。那我们利用Busybox制作根文件系统就是创建这上面的这些目录,和这些目录下面的各种文件。对于嵌入式Linux系统的根文件系统来说,一般可能没有上面所列出的那么复杂,比如嵌入式系统通常都不是针对多用户的,所以/home这个目录在一般嵌入式Linux中可能就很少用到,而/boot这个目录则取决于你所使用的BootLoader是否能够重新获得内核映象从你的根文件系统在内核启动之前。一般说来,只有/bin,/dev,/etc,/lib,/proc,/var,/usr这些需要的,而其他都是可选的。
根文件系统一直以来都是所有类Unix操作系统的一个重要组成部分,也可以认为是嵌入式Linux系统区别于其他一些传统嵌入式操作系统的重要特征,它给 Linux带来了许多强大和灵活的功能,同时也带来了一些复杂性。我们需要清楚的了解根文件系统的基本结构,以及细心的选择所需要的系统库、内核模块和应用程序等,并配置好各种初始化脚本文件,以及选择合适的文件系统类型并把它放到实际的存储设备的合适位置,下面是几中比较常用的文件系统。
[wuyujun@wuyujunlocalhost ~]$ cd fl2440/linux/
[wuyujun@wuyujunlocalhost linux]$ mkdir rootfs
[wuyujun@wuyujunlocalhost linux]$ ls
linux-3.0 linux-3.0.tar.bz2 rootfs u-boot-2010.09 u-boot-2010.09-fl2440.patch u-boot-2010.09.tar.bz2
[wuyujun@wuyujunlocalhost rootfs]$ mkdir -p {apps,bin,data,dev,info,proc,root,sbin,sys,tmp,var,etc/init.d,mnt/{usb,mmc},usr/{,bin,sbin,lib},lib/{,modules/{,3.0.0}}}
[wuyujun@wuyujunlocalhost rootfs]$ tree
.
|-- bin 系统基本命令bin
|-- sbin 系统管理命令
|-- dev 系统设备节点所在路径
|-- root root用户home
|-- etc 系统配置文件所在路径
| `-- init.d 系统初始化、启动程序脚本
|-- lib 系统动态库路径
| `-- modules
| `-- 3.0.0 linux内核驱动模块需要该路径
|-- usr
| |-- bin 用户基本命令路径
| |-- lib 用户动态库文件
| `-- sbin 用户管理命令路径
|-- apps nandflash apps分区挂载点,用来存放应用程序
|-- data nandflash data分区挂载点,用来存放数据
|-- info nandflash info分区挂载点,用来存放设备信息
|-- mnt 系统挂载点
| |-- mmc SD卡挂载点
| `-- usb U盘挂载点
|-- proc linux proc文件系统挂载点
|-- sys linux sys文件系统挂载点
|-- tmp linux tmp文件系统挂载点 `-- var 系统日志等文件存放路径
23 directories, 0 files
[wuyujun@wuyujunlocalhost rootfs]$ cd ~/fl2440/linux/rootfs/
[wuyujun@wuyujunlocalhost rootfs]$ ls
apps bin data dev etc info lib mnt proc root sbin sys tmp usr var
[wuyujun@wuyujunlocalhost rootfs]$ cd lib
将/opt/xtools/arm920t路径下除gconv相关的所有动态库(.so)文件拷贝
[wuyujun@wuyujunlocalhost lib]$ find /opt/xtools/arm920t/ -name "*.so*" | grep -v gconv | xargs -i cp -af {} ./
列出失效的链接
[wuyujun@wuyujunlocalhost lib]$ ls -l `find -L -type l`
将失效的符号链接链接到本地路径下,可以创建并使用下面脚本来重新制作符号链接到本地路径下:
[wuyujun@wuyujunlocalhost lib]$ vim symlink.sh
#!/bin/bash
# find all the invalid symlink file and remove "./" before it
for sf in `find -L -type l | cut -d'/' -f2` ; do
# parser and get symlink target file name
file=`ls -l $sf | awk -F '/' '{ print $NF }'`
# remove invalid symlink file and generate new one
rm -f $sf && ln -s $file $sf
Done
[wuyujun@wuyujunlocalhost lib]$ sh symlink.sh && rm -f symlink.sh
再次使用下面命令发现没有红色的失效链接
[wuyujun@wuyujunlocalhost lib]$ ls -l `find -L -type l`
现在根文件系统树下这些路径里并没有Linux系统相关命令。所以需要安装BusyBox。BusyBox 是一个集成了三百多个最常用Linux命令和工具的软件。BusyBox 包含了一些简单的工具,例如ls、cat和echo等等,还包含了一些更大、更复杂的工具,例grep、find、mount以及telnet。有些人将 BusyBox 称为 Linux 工具里的瑞士军刀。简单的说BusyBox就好像是个大工具箱,它集成压缩了 Linux 的许多工具和命令。BusyBox 将许多具有共性的小版本的UNIX工具结合到一个单一的可执行文件。这样的集合可以替代大部分常用工具比如的GNU fileutils , shellutils等工具,BusyBox提供了一个比较完善的环境,可以适用于任何小的嵌入式系统。
而Linux系统的命令默认会到/bin、/sbin、/usr/bin、/usr/sbin这些路径下找,如果找不到则提示command not found,当然我们也可以通过PATH环境变量告诉系统其它命令存放的路径。下载busybox源码、配置编译并安装它就会提供这些Linux的基本命令的实现,它们将会被安装到根文件系统树的 bin、sbin、usr/bin、usr/sbin等路径下。
[wuyujun@wuyujunlocalhost lib]$ cd ~/fl2440/linux/rootfs/
[wuyujun@wuyujunlocalhost rootfs]$ ls
apps bin data dev etc info lib mnt proc root sbin sys tmp usr var
将Busybox安装到第三方应用程序软件包目录下面
[wuyujun@wuyujunlocalhost rootfs]$ cd ~/fl2440/
[wuyujun@wuyujunlocalhost fl2440]$ ls
3rdparty bootloader crosstool driver images linux program
[wuyujun@wuyujunlocalhost fl2440]$ cd 3rdparty/
下载并解压Busybox
[wuyujun@wuyujunlocalhost 3rdparty]$ wget https://busybox.net/downloads/busybox-1.27.1.tar.bz2
[wuyujun@wuyujunlocalhost 3rdparty]$ tar -xjf busybox-1.27.1.tar.bz2
[wuyujun@wuyujunlocalhost 3rdparty]$ cd busybox-1.27.1
[wuyujun@wuyujunlocalhost busybox-1.27.1]$ ls
applets configs editors libbb Makefile.custom networking scripts TODO_unicode
applets_sh console-tools examples libpwdgrp Makefile.flags printutils selinux util-linux
arch coreutils findutils LICENSE Makefile.help procps shell
archival debianutils include loginutils make_single_applets.sh qemu_multiarch_testing sysklogd
AUTHORS docs init mailutils miscutils README testsuite
Config.in e2fsprogs INSTALL Makefile modutils runit TODO
与移植Linux内核类似,对busybox进行配置,选择哪些Linux命令需要编译,哪些不需要
[wuyujun@wuyujunlocalhost busybox-1.27.1]$ export TERM=vt100
[wuyujun@wuyujunlocalhost busybox-1.27.1]$ make menuconfig
Busybox Settings --->
Linux System Utilities --->
Coreutils --->
Print Utilities ---> 打印机我们不用的话,里面的全部不要选,可以节约空间
Mail Utilities ---> 邮件收发我们不用的话,里面的全部不要选,可以节约空间
[wuyujun@wuyujunlocalhost busybox-1.27.1]$ make && make install
切换到根文件系统树路径下,创建符号链接指向busybox程序
[wuyujun@wuyujunlocalhost busybox-1.27.1]$ cd ../../linux/rootfs/
[wuyujun@wuyujunlocalhost rootfs]$ ln -s bin/busybox init
创建/dev路径下的设备节点,Linux下所有的东西都是文件,其中设备也是当作文件来处理。/dev路径下存放所有的Linux的设备文件,我们需要使用root权限执行mknod命令在这里,创建系统启动必须的设备文件节点,其他的设备节点将有mdev动态创建。
[wuyujun@wuyujunlocalhost rootfs]$ cd dev/
[wuyujun@wuyujunlocalhost dev]$ sudo mknod -m666 null c 1 3
[wuyujun@wuyujunlocalhost dev]$ sudo mknod -m666 console c 5 1
[wuyujun@wuyujunlocalhost dev]$ sudo mknod -m666 ttyS0 c 4 6
[wuyujun@wuyujunlocalhost dev]$ cd ..
[wuyujun@wuyujunlocalhost rootfs]$ ls
apps bin data dev etc info init lib linuxrc mnt proc root sbin sys tmp usr var
/var路径会存放系统运行时的一些文件(如系统日志文件/var/log/messages),我们在该路径下创建相应的符号链接到/tmp路径下
[wuyujun@wuyujunlocalhost rootfs]$ ln -s /tmp var/lock
[wuyujun@wuyujunlocalhost rootfs]$ ln -s /tmp var/log
[wuyujun@wuyujunlocalhost rootfs]$ ln -s /tmp var/run
[wuyujun@wuyujunlocalhost rootfs]$ ln -s /tmp var/tmp
创建 /etc/inittab 文件,Linux内核自举启动完成之后,最后将创建init进程并执行根文件系统中的/init或/initrc程序,该程序解析并执行/etc/inittab配置文件,在此文件中配置了init进程在系统启动要启动哪些应用程序
[wuyujun@wuyujunlocalhost busybox-1.27.1]$ cd ~/fl2440/linux/rootfs/
[wuyujun@wuyujunlocalhost rootfs]$ vim etc/inittab
# Format for each entry: <id>:<runlevels>:<action>:<process>
#
# id == tty to run on, or empty for /dev/console.
# If specified, then /dev/$id device must exist
# runlevels == ignored, busybox doesn't support it
# action == one of sysinit, respawn, askfirst, wait, and once
# process == program to run
# Startup the system
# mount all the file systems specified in /etc/fstab
::sysinit:/bin/mount -a
# Use mdev to auto generate device nod and auto mount SD card and USB storage
::sysinit:/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug
::sysinit:/sbin/mdev -s
#make shm, pts support
::sysinit:/bin/mkdir -p /dev/pts
::sysinit:/bin/mkdir -p /dev/shm
::sysinit:/bin/mount -t devpts devpts /dev/pts
#Set hostname
null::sysinit:/bin/hostname -F /etc/hostname
#Enable console logon
null::respawn:/sbin/getty -L ttyS0 115200 vt100
# now run any rc scripts, which used to start other application
null::wait:/etc/init.d/rcS
# system daemon
null::respawn:/sbin/syslogd -n
null::respawn:/sbin/klogd -n
# Stuff to do before rebooting
null::shutdown:/bin/killall klogd
null::shutdown:/bin/killall syslogd
null::shutdown:/bin/umount -a -r
创建/etc/fstab文件,Linux下的mount -a命令将自动挂载/etc/fstab文件中指定的所有需要系统自动挂载的文件。在/etc/inittab中会使用该命令在上电时自动挂载相应的文件系统,这里面主要是挂载一些伪文件系统,这些伪文件系统主要是导出Linux内核运行的相关信息,如比较重要的tmpfs、sysfs、proc文件系统等。
[wuyujun@wuyujunlocalhost rootfs]$ vim etc/fstab
/dev/root / ext2 rw,noauto 0 1
proc /proc proc defaults 0 0
usbfs /proc/bus/usb usbfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
ramfs /tmp ramfs defaults 0 0
sysfs /sys sysfs defaults 0 0
创建hostname文件,/etc/hostname文件指定主机名,/etc/inittab文件中通过 hostname -F /etc/hostname 命令设置主机名
[wuyujun@wuyujunlocalhost rootfs]$ vim etc/hostname
LingYunFL2440_WYJ
创建系统启动脚本,系统所有的启动脚本我们都将放在/etc/init.d路径下,接下来我们将创建这些系统启动脚本:
[wuyujun@wuyujunlocalhost rootfs]$ ls etc/
fstab hostname init.d inittab
[wuyujun@wuyujunlocalhost rootfs]$ cd etc/init.d/
[wuyujun@wuyujunlocalhost init.d]$ ls
启动脚本一、 /etc/init.d/rcS
/etc/inittab脚本会使用命令/etc/init.d/rcS启动所有其它的应用程序,下面创建etc/rcS脚本,该脚本将执行/etc/init.d/下所有文件名以S开头且后面紧
跟至少包括2个字符的文件。
[wuyujun@wuyujunlocalhost init.d]$ vim rcS
# Start all scripts in /etc/init.d, executing them in numerical order.
for i in /etc/init.d/S??* ; do
$i
Done
需要将给脚本执行的权限才能执行
[wuyujun@wuyujunlocalhost init.d]$ chmod a+x rcS
启动脚本二、 /etc/init.d/S10_network,假设我们希望系统启动时自动设置网卡的IP地址,则可以创建一个文件名以S开头且后面紧跟至少包括2个字符的启动脚本,并给执行权限。
[wuyujun@wuyujunlocalhost init.d]$ vim S10_network
#!/bin/sh
ifconfig eth0 192.168.137.9 netmask 255.255.255.0 up
[wuyujun@wuyujunlocalhost init.d]$ chmod a+x S10_network
启动脚本三、 /etc/init.d/S99_rcsApp,今后我们很多应用程序将会存放在/apps分区下,有时我们希望这些应用程序的启动脚本也存放在该分区下以便于管理。这时我们可以参考rcS脚本的实现机制,让它也执行在/apps/etc下的所有启动脚本
[wuyujun@wuyujunlocalhost init.d]$ vim S99_rcsApp
for i in /apps/etc/init.d/S??* ; do
$i
Done
[wuyujun@wuyujunlocalhost init.d]$ chmod a+x S99_rcsApp
[wuyujun@wuyujunlocalhost init.d]$ ls
rcS S10_network S99_rcsApp
创建shell的配置文件,在Linux服务器上,我们可以通过修改~/.bash_profile或~/.bashrc文件来修改Shell的配置文件,如修改PATH、LD_LIBRARY_PATH环境变量等,也可以在里面定义命令别名。在嵌入式环境下,我们这些Shell的配置保存在/etc/profile文件中。这时我们可以创建该文件配置嵌入式环境busybox里的shell(ash)。切换到根文件系统树/etc路径下
[wuyujun@wuyujunlocalhost init.d]$ cd ~/fl2440/linux/rootfs/etc/
[wuyujun@wuyujunlocalhost etc]$ ls
fstab hostname init.d inittab
[wuyujun@wuyujunlocalhost etc]$ vim profile
# Busybox Shell(ash) configure file
export PATH=/bin:sbin:/usr/bin:/usr/sbin:/apps/bin:/apps/tools
export PS1='\w >: '
export USER=`id -un`
export LOGNAME=$USER
export HOSTNAME=`/bin/hostname`
export HISTSIZE=500
export HISTFILESIZE=500
export PAGER='/bin/more'
export EDITOR='/bin/vi'
export INPUTRC=/etc/inputrc
export LD_LIBRARY_PATH=/lib:/usr/lib:/apps/lib
export network_cfg_dir=/apps/etc/network
### Some alias command
alias vim='vi'
alias ll='ls -l'
alias l.='ls -d .*'
alias df='df -h'
创建Linux登录帐号文件,Linux系统中的登录帐号信息保存在/etc/group、/etc/passwd和/etc/shadow文件中。
/etc/group文件保存了用户组信息;
/etc/passwd文件保存了用户信息;
/etc/shadow文件保存来相应用户的密码;
下面我们需要在根文件系统中创建这些系统帐号文件,在这里我们只创建root帐号,其他帐号的过程类似。当然也可以在系统启动后以root帐号权限来添加。
[wuyujun@wuyujunlocalhost etc]$ vim group
root:x:0:root
该文件的格式为:
groupname:password:gid:members 第一个字段为用户组名称;
第二个字段为用户组密码,当为x时密码是映射到/etc/gshadow中的,是非逆的;
第三个字段为GID,及组号,为正整数或0,0被付于了root用户组;系统通常会预留一些较靠前的GID给系统虚拟用户之用,每个系统预留的GID都不同,一般普通用户的GID从500开始;
第四个字段为用户列表,每个用户间用逗号分隔;
[wuyujun@wuyujunlocalhost etc]$ vim passwd
root:x:0:0:root:/:/bin/sh
该文件的格式为:
username:password:uid:gid:fullname:homedir:shell
第一个字段为登录用户名
第二个字段为密码,这里设置为x表示密码被映射到/etc/shadow文件中
第三个字段为用户ID(UID)
第四个字段为用户所属组ID(GID)
第五个字段为用户名全称
第六个字段为用户根目录
第七个字段为用户所用shell的类型
Unix系统最初是用明文保存密码的,后来由于安全的考虑,采用crypt()算法加密密码并存放在/etc/passwd文件。现在,由于计算机处理能力的提高,使密码破解变得越来越容易。/etc/passwd文件是所有合法用户都可访问的,大家都可互相看到密码的加密字符串,这给系统带来很大的安全威胁。现代的Unix系统使用影子密码系统,它把密码从/etc/passwd文件中分离出来,真正的密码保存在/etc/shadow文件中,shadow文件只能由超级用户访问。这样入侵者就不能获得加密密码串,用于破解。使用shadow密码文件后,/etc/passwd文件中所有帐户的password域的内容为"x",如果password域的内容
为"*",则该帐号被停用。
/etc/shadow文件/etc/shadow文件保存了相应帐号的密文,这个文件内容不能直接创建。如果我们想在嵌入式Linux系统中设置root的登录密码为123456,则我们先在自己的Linux服务器或虚拟机上修改root密码为123456,然后再将Linux服务器或虚拟机上的/etc/shadow文件里root帐号的密码密文部分拷贝出来,创建根文件系统树里的etc/shadow时将密文部分用这个密文替换。这个过程做完后记得将自己的Linux服务器或虚拟机的root密码还原回去。
[wuyujun@wuyujunlocalhost etc]$ sudo passwd root
[sudo] password for wuyujun:
更改用户 root 的密码 。
新的 密码:
无效的密码: 过于简单化/系统化
无效的密码: 过于简单
重新输入新的 密码:
passwd: 所有的身份验证令牌已经成功更新。
[wuyujun@wuyujunlocalhost etc]$ sudo cat /etc/shadow | grep root
root:$6$7e62u5ko$lWO0E4vSB05pY93nZN3LBW7iSZ1YqVVIx.uOQ.p8ED41t5BPwtyie8iCIZOAxOprkZ6laK5u0107WIog0nFBO0:18009:0:99999:7:::
[wuyujun@wuyujunlocalhost etc]$ sudo passwd root
更改用户 root 的密码 。
新的 密码:
重新输入新的 密码:
passwd: 所有的身份验证令牌已经成功更新。
[wuyujun@wuyujunlocalhost etc]$
[wuyujun@wuyujunlocalhost etc]$ vim shadow
root:$6$7e62u5ko$lWO0E4vSB05pY93nZN3LBW7iSZ1YqVVIx.uOQ.p8ED41t5BPwtyie8iCIZOAxOprkZ6laK5u0107WIog0nFBO0:0:0:99999:7:::
该文件的格式为:
username:password:last_change:min_change:max_change:warm:failed_expire:expiration:reserved
第一字段:用户名(也被称为登录名),在/etc/shadow中,用户名和/etc/passwd 是相同的,这样就把passwd 和shadow中用的用户记录联系在一起;这个字段是非空的;
第二字段:密码加密后的密文,这个字段是非空的;
第三字段:上次修改密码的时间;这个时间是从1970年01月01日算起到最近一次修改口令的时间间隔(天数);
第四字段:两次修改密码间隔最少的天数;如果这个字段的值为空,帐号永久可用;
第五字段:两次修改密码间隔最多的天数;如果这个字段的值为空,帐号永久可用;
第六字段:提前多少天警告用户密码将过期;如果这个字段的值为空,帐号永久可用;
第七字段:在密码过期之后多少天禁用此用户;如果这个字段的值为空,帐号永久可用;
第八字段:用户过期日期;此字段指定了用户作废的天数(从1970年的1月1日开始的天数),如果这个字段的值为空,帐号永久可用;
第九字段:保留字段,目前为空,以备将来发展之用;
创建Linux其他文件切换到根文件系统树etc路径下:
[wuyujun@wuyujunlocalhost etc]$ ls
fstab group hostname init.d inittab passwd profile shadow
/etc/resolv.conf文件该文件用来设置Linux系统的DNS服务器,DNS服务器用来将域名转换成IP地址
[wuyujun@wuyujunlocalhost etc]$ vim resolv.conf
nameserver 8.8.8.8
nameserver 114.114.114.114
/etc/hosts文件,/etc/hosts文件存放本机静态绑定的域名,如下面在本机上域名localhost就会被解析成127.0.0.1,不需要DNS来做解析。
[wuyujun@wuyujunlocalhost etc]$ vim hosts
127.0.0.1 localhost
/etc/TZ文件,/etc/TZ文件配置了系统的时区
[wuyujun@wuyujunlocalhost etc]$ vim TZ
CUT0GDT
时区代码 | 时区 | CUT 时差 |
CUT0GDT | 全球标准时间 | CUT |
EST5EDT | 美国东部 | CUT-5 |
CST6CDT | 美国中部 | CUT-6 |
MST7MDT | 美国山区 | CUT-7 |
PST8PDT | 美国太平洋沿岸地区 | CUT-8 |
AST9ADT | 阿拉斯加州 | CUT-9 |
HST10HDT | 夏威夷 | CUT-10 |
/etc/issue文件,/etc/issue文件里存放在console上登录系统时提示的警告信息
[wuyujun@wuyujunlocalhost etc]$ vim issue
Copyright (C) 2012 LingYun I.o.T Studio< iot-yun.com >
2019/4/23 WuYujun Demo
Default Logon Username: root Password: 123456
至此Linux根文件系统树所需要的基本文件都已经有了,接下来我们可以使用Linux内核启动这个根文件系统。
Linux内核使用initramfs文件系统启动,initramfs是Linus开发的一种基于内存的根文件系统,在编译Linux内核的时候,它会直接把根文件系统树打包进内核的镜像文件中(zImage),这也意味着该镜像文件同时包含了Linux内核和根文件系统。因为根文件系统是内核打包进去的,所以内核自己知道根文件系统的位置,这样u-boot也就不需要通过
bootargs参数告诉内核根文件系统的信息,此外也不需要额外烧录根文件系统镜像文件,如ubifs、yaffs2、jffs2等。在今后做其他根文件系统的时候,请务必在内核中禁用initramfs,否则Linux内核将优先使用initramfs启动了。因为initramfs是基于内存的根文件系统,所以在开发板上对根文件系统里的任何文件的操作,包括创建、删除、修改在重启后都会丢失。因此如果想要更改根文件系统里的文件,必须修改根文件系统树,然后重新编译Linux内核并使用u-boot重新烧录。
Linux内核使用initramfs启动,只需要在make menuconfig作下面修改,然后重新编译即可
[wuyujun@wuyujunlocalhost etc]$ cd ~/fl2440/linux/linux-3.0/
[wuyujun@wuyujunlocalhost linux-3.0]$ make menuconfig
General setup --->
Device Drivers --->
[*] Block devices --->
[wuyujun@wuyujunlocalhost linux-3.0]$ ls
[wuyujun@wuyujunlocalhost linux-3.0]$ ./build.sh
启动开发板,在u-boot下使用tftp命令把Linux内核下载到0x30008000地址上后,直接使用bootm命令启动
[fl2440@lingyun]# tftp 30008000 linuxrom-s3c2440.bin
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
could not establish link
Using dm9000 device
TFTP from server 192.168.137.1; our IP address is 192.168.137.3
Filename 'linuxrom-s3c2440.bin'.
Load address: 0x30008000
Loading: T #################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#########################
done
Bytes transferred = 7040012 (6b6c0c hex)
[fl2440@lingyun]# bootm 30008000
然后报错了。。。
drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
Freeing init memory: 11556K
Kernel panic - not syncing: Attempted to kill init!
根文件已经挂载了但是init进程启动不起来
Kernel Features --->
[*] Use the ARM EABI to compile the kernel
[*] Allow old ABI binaries to run with this kernel (EXPERIMENTAL)
将这两个选上 ABI,application binary interface (ABI),应用程序二进制接口。EABI中的E,表示“Embedded”,即嵌入式应用二进制接口,是一种新的ABI。ABI是描述可连接目标代码,库目标代码,可执行文件映像,如何连接,执行和调试,以及目标代码生成过程,和c, c++语言接口的规范,是编译连接工具的基础规范,也是研究它们工作原理的基础,用的交叉编译器是EABI,编译的程序就是是EABI接口,Linux内核要支持他的话就要EABI去编译内核。
[fl2440@lingyun]# set tb 'tftp 30008000 linuxrom-s3c2440.bin;bootm 30008000'
[fl2440@lingyun]# save
[fl2440@lingyun]# set bootargs 'console=tty0 console=ttyS0,115200 mem=64M rw loglevel=7'
[fl2440@lingyun]# run tb
到这里Linux3.0最小系统移植完毕