嵌入式Linux系统移植

嵌入式Linux系统移植是进行Linux驱动开发的基础,如果连Linux系统移植都完成不了,那就更不用说驱动开发了。因此,Linux系统移植至关重要。

嵌入式Linux系统的移植要点

  1. 搭建交叉开发环境
  2. bootloader的选择与移植
  3. kernel的设置、编译、移植和调试
  4. 根文件系统的制作

嵌入式Linux系统的移植基本步骤

  1. 安装交叉编译器
  2. 确定目标机、主机的连接方式
  3. 搭建主机-目标机数据传输通道
  4. 编译三大子系统(bootloader子系统、内核核心子系统、文件系统子系统)
  5. 烧写测试

一般而言,目标机、主机的连接方式有:

  • UART异步串行通信接口(速率低、实用性强)
  • USB串行通信接口(速率快、驱动要移植修改)
  • TCP/IP网络通信接口(速率快、驱动要移植)
  • Debug Jtag调试接口(方便快捷、价格昂贵)

相比较来说,UART、USB、TCP/IP连接方式的选择较多。

一般而言,交叉编译器可以选择芯片公司提供的已经编译好的工具链,通常前缀名有:

  • arm-none-linux-gnueabi- (简称为:arm-linux-
  • arm-none-eabi- (不针对linux操作系统)
  • arm-elf- (不针对linux操作系统)

当然,也可以选择自己构建新的交叉编译器。

注意注意:在【嵌入式Linux的移植】博文中,并不涉及制作交叉编译器、编译U-Boot、编译内核、编译根文件系统的内容,而是将已经制作/编译完成的成品进行试验并测试。至于它们的制作过程,会在后面的博文中讲解。

交叉编译器

为什么要有交叉编译?

一般情况下,需要在开发主机上(通常是PC机)开发出能够在目标机(通常是开发板)上运行的程序。

为什么需要交叉开发环境?主要原因有以下两点:

  1. 目标机的硬件资源有很多限制,比如cpu主频相对较低,内存容量较小等,想想让几百MHZ主频的目标机去编译一个Linux kernel会让我们等的不耐烦,相对来说,主机的速度更快,硬件资源更加丰富,因此利用主机进行开发会提高开发效率;
  2. 目标机的MCU体系结构和指令集与主机不同,因此需要安装交叉编译工具进行编译,这样编译的目标程序才能够在目标机上运行。

安装交叉编译器

本文使用的是友善之臂提供的arm-linux-gcc-4.5.1-v6-vfp-20120301版交叉编译工具:

sudo tar -zxvf arm-linux-gcc-4.5.1-v6-vfp-20120301.tgz -C /

这样arm-linux-的相关文件就解压到了根目录下的opt目录中。

解压完成了,如何来使用呢?

使用简单方法:

arm-linux-gcc hello.c -o build

使用绝对路径方法:

/opt/FriendlyARM/toolschain/4.5.1/bin/arm-linux-gcc hello.c -o build

使用简单方法需要在环境变量的PATH中添加路径,这样的话,有一个弊端,最好保证整个系统只有一个交叉编译器,不然会存在PATH的优先级等问题。

怎么在环境变量的PATH中添加路径?

sudo vi /etc/environment

在该文件中添加路径/opt/FriendlyARM/toolschain/4.5.1/bin,各个路径之间以分隔。添加完成后,需要更新一下,使环境变量生效:

source /etc/environment

可以通过以下命令查看是否添加成功:

echo $PATH

需要注意的是:

如果在64位Linux安装arm-linux-gcc-4.5.1交叉编译器,要先安装32位的库(arm-linux-gcc-4.5.1-v6-vfp-20120301是在32位的Linux下编译的)。

安装32位的库:

sudo apt-get install libc6:i386
sudo apt-get install lib32z1

本部分内容,可以参考博文安装arm-linux-gcc交叉编译器。

工具集介绍

readelf

顾名思义,就是读取ELF头信息的工具。ELF(Executable and Linkable Format)是一种对象文件的格式,用于定义不同类型的对象文件(Object files)中都放了什么东西、以及都以什么样的格式去放这些东西。

ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。实际上,一个文件中不一定包含全部内容,而且他们的位置也未必如同所示这样安排,只有ELF头的位置是固定的,其余各部分的位置、大小等信息由ELF头中的各项值来决定。例如:

gcc hello.c -o hello
readelf -h hello

不添加arm-linux-前缀,就表示在x86上进行编译和查取ELF头信息。结果为:

ELF 头:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  入口点地址:               			 0x400750
  程序头地址:                         64 (bytes into file)
  Start of section headers:          65016 (bytes into file)
  标志:                          	 0x0
  本头的大小:                         64 (bytes)
  程序头大小:                         56 (bytes)
  Number of program headers:         9
  节头大小:           				 64 (bytes)
  节头数量:         					 37
  字符串表索引节头: 					 34

需要注意的是,运行一个程序首先进入入口点地址,然后再各种调用,最终进入main地址,运行main函数的内容。

可通过file命令查看文件属性

file hello

在运行结果中,最开始是ELF头,第二个是编译后可运行的平台。实际上,运行平台在ELF中的Machine字段中也标明出来了。如果通过arm-linux-gcc进行编译,那么ELF头和file命令中的结果,都会显示为ARM。

本部分内容,可以参考博文ELF格式文件详细分析。

其他工具集

  • size:查看编译后的文件各个段(text段、data段、bss段、dec段等)的大小信息
  • nm:查看符号信息(说白了就是一些函数和全局变量等,比如main地址,_start入口点地址等)
  • strip:删除符号表信息(减小文件的大小)
  • objcopy:将目标文件的一部分或者全部内容拷贝到另外一个目标文件中(用于将ELF头去掉,因为ARM裸机是不识别ELF头信息的)
  • objdump:反汇编(添加-d选项)

目标机、主机的连接方式

一般而言,目标机和主机之间采用TCP/IP网络通信的方式居多,常用的服务主要两个:TFTP服务NFS服务

TFTP服务

TFTP服务主要用于实现文件的下载,比如开发调试的过程中,主要用tftp把要测试的bootloader、kernel和文件系统直接下载到内存中运行,而不需要预先烧录到Flash芯片中。

一方面,在测试的过程中,往往需要频繁的下载,如果每次把这些要测试的文件都烧录到Flash中然后再运行也可以,但是缺点是:过程比较麻烦,而且Flash的擦写次数是由限的;另外一方面:测试的目的就是把这些目标文件加载到内存中直接运行就可以了,而tftp就刚好能够实现这样的功能,因此,更没有必要把这些文件都烧录到Flash中去。

nfs服务:

NFS服务主要用于实现网络文件的挂载,实际上是实现网络文件的共享

在开发的过程中,通常在系统移植的最后一步会制作文件系统,那么这是可以把制作好的文件系统放置在我们开发主机PC的相应位置,开发板通过nfs服务进行挂载,从而测试我们制作的文件系统是否正确,在整个过程中并不需要把文件系统烧录到Flash中去,而且挂载是自动进行挂载的,bootload启动后,kernel运行起来后会根据我们设置的启动参数进行自动挂载,因此,对于开发测试来讲,这种方式非常的方便,能够提高开发效率。

一般而言,U-Boot采用USB线或者SD卡烧写到Flash中,而内核、根文件系统都采用TFTP的方式传输到目标板,然后通过U-Boot的命令进行启动。

搭建主机-目标机数据传输通道

以笔记本为例,整个搭建示意图:



嵌入式linux增加一个EMMC分区 嵌入式linux移植过程_交叉编译器


首先笔记本有两个网卡,分别为WiFi网卡和有线网卡。虚拟机同样也配置成双网卡模式,一个通过NAT模式与WiFi网卡相连,连接无线路由器的LAN口,再通过WAN口进行上网功能;另一个通过桥接模式与有线网卡相连,最终连接到目标板进行数据传输功能。

桥接模式

在这种模式下,使用VMnet0虚拟交换机,虚拟机就像是局域网中的一台独立的主机,与宿主计算机一样,它可以访问网内任何一台机器。

在桥接模式下,可以手工配置它的TCP/IP配置信息(IP、子网掩码等,而且还要和宿主机器处于同一网段),以实现通过局域网的网关或路由器访问互联网;还可以将IP地址和DNS设置成“自动获取”。

在桥接模式中,使用VMnet0虚拟交换机,此时虚拟机相当与网络上的一台独立计算机与主机一样,拥有一个独立的IP地址。

NAT模式

使用NAT模式,就是让虚拟机借助NAT(网络地址转换)功能,通过宿主机器所在的网络来访问公网。也就是说,使用NAT模式可以实现在虚拟系统里访问互联网

NAT模式下的虚拟机的TCP/IP配置信息是由VMnet8虚拟网络的DHCP服务器提供的,因此IP和DNS一般设置为“自动获取”,因此虚拟系统也就无法和本局域网中的其他真实主机进行通讯。

采用NAT模式最大的优势是虚拟系统接入互联网非常简单,你不需要进行任何其他的配置,只需要宿主机器能访问互联网即可。

也就是说,桥接模式是为了加入主机的局域网,完成与主机之间的数据访问;NAT模式是为了通过主机,访问互联网。两者的目的不同!