借鉴CSDN上大佬的Linux系统移植讲解,讲述一下作为小白的学习体会心得,若有不合适、侵权之处,请联系我及时删帖,谢谢!
自学总结Linux的系统移植步骤与过程
为什么要学习Linux嵌入式系统移植?
目前在众多的嵌入式操作系统中,Linux是发展最快的,应用范围广
性能优良、开放源代码的,并且有体积小、内核可裁剪、网络功能
完善,可移植性强的诸多优点。非常适合嵌入式操作系统。
一个基本的Linux操作系统包含:引导程序BootLoader(例如常见的U-boot,
Universal Boot Loader)、内核与根文件系统三部分。
在进行嵌入式的Linux系统移植中只要有四步骤:
一、搭建开发环境,配置交叉工具链。
首先要明白任何一款开发板只是在物理层面的实体,没有程序
是不能开始运行的。程序的运行需要相应的运行环境。开发者
(这里指的是作者本人小白一枚)一般都是在自己的PC机(x86 CPU)上进行程序的编写,那么如何能在ARM的体系架构上运行呢?这时候就需要在PC机上出现一个“工具”,通过这个工具我们可以完成x86环境下的程序编写,并可以实现在arm架构体系中的运行。这个工具就是交叉编译链。
总结两个疑惑:
第一:开发板与PC机的不同:
对于开发板而言,自身的CPU主频相对于PC机而言比较低,例如:市面上可查的开发板的主频有80M、528M、667M、甚至高达1G,但是实际的应用过程中,开发板的主频往往是MHz级别的,而我们(笔者自己的PC机i5-7300HQ)的CPU主频为2.5GHz,相比较而言,在PC机上进行Linux Kernel的编译时间会提升数十倍(比如你一个小时才能完成的活别人6分钟甚至3分钟就搞定了,这样的差距可想而知)。通过PC机可以提高我们的开发效率。
第二:交叉工具链都做了什么:
嵌入式系统MCU体系结构和指令集与主机PC不同,因此安装交叉编译工具链进行编译。这样通过在PC机上编写的程度代码通过交叉编译器进行编译才能具备在目标板(开发板架构)上运行。如ARM。
交叉编译器就是在PC机上编译出能够运行在开发板体系结构上的程序。(延伸的意思则是:在A机上装载交叉编译器,可以将A机上的本地编译的编译程序通过交叉编译器完成目标机B上运行的程序,因此必须要使用交叉编译工具链来实现)(之所以用在A上的交叉编译器对B机上运行的程序进行编译的原因是因为目前B机上还不能运行程序,无法实现程序的编译等,比如开发板)
程序包含了编译、汇编、链接等过程。同时还需要进行调试。
交叉编译器工具链主要是由binutils(包含了汇编程序as,主要是用来编译gcc输出的汇编文件;产生的目标文件由连接器ld连接)、gcc、glibc三部分组成。
As -0 hello.ohello.s生成目标文件
选择使用Crosstool-ng脚本工具来实现一次编译,生成交叉编译工具链。
crosstool-ng是一个脚本工具,可以制作出适合不同平台的交叉编译工具链,在进行制作之前要安装一下软件:
$ sudo apt-get install g++ libncurses5-dev bison flex texinfo automake libtool patch gcj cvs cvsd gawk
crosstool脚本工具可以在http://ymorin.is-a-geek.org/projects/crosstool下载到本地,然后解压,接下来就是进行安装配置了,这个配置优点类似内核的配置。主要的过程有以下几点:
1. 设定源码包路径和交叉编译器的安装路径
2. 修改交叉编译器针对的构架修改交叉编译器针对的构架
3. 增加编译时的并行进程数,以增加运行效率,加快编译,因为这个编译会比较慢。
4. 关闭JAVA编译器 ,减少编译时间
5. 编译
6. 添加环境变量
7. 刷新环境变量。
8. 测试交叉工具链
交叉开发环境的硬件组成:
1、pc
2、开发板
3、常用的链接介质:串口线、USB、网线
对于硬件介质同时需要相对应的软件介质。
串口常用的通常是串口调试助手
USB而言,需要相对应的驱动
网线,则需要网络协议支持。常用的服务主要有两个:
1、tftp服务
主要是用于实现文件的下载,例如在开发调试过程中将测试的bootloader、kernel、文件系统直接下载到内存中运行,而不需要在烧录到Flash芯片中。在测试过程中,需要频繁的下载,如果每次都是下载道Flash中然后在运行也可以。存在的缺点是:过程耗费时间、效率低、但由于flash的擦写次数有限;测试就是将目标文件加载到内存中运行就可以,而tftp刚好具有这样的功能。因此没有必要将这些测试文件再烧写到Flash中。
tftp服务器配置
1.检查是否安装tftp,没用安装,去安装tftp
$sudo dpkg -s tftpd-hpa
$sudo apt-get install tftpd-hpa
2.配置tftp选项及参数
$sudo vi /etc/default/tftpd-hpa
3.建立相应tftp目录。并设立777权限
$sudo mkdir /tftpboot
$sudo chmod a+w /tftpboot
4.重启tftp服务
$sudo service tftpd-hpa restart
5.验证tftp是否成功
$sudo tftp 127.0.0.1
tftp>get
tftp>put
tftp>quit //退出
2、nfs服务
主要是用于实现网络文件的挂载,实际上实现网络文件的共享。在开发过程中,Linux系统移植的最后一步则需要将文件系统放置在我们开发设备PC机的相应位置,开发板通过nfs服务进行挂载,从而测试我们制作的文件系统是否正确。在整个过程中不需要再将文件系统烧写到Flash中,而且挂载是自动进行的,在bootloader启动后,kermel运行起来会根据我们设置的启动参数进行自动挂载。因此对于开发测试来讲,这样的方式很便利。
nfs配置
1.修改 /etc/exports 文件
$sudo vi /etc/exports
2.创建“/source”设置权限777
3.启动nfs服务
$sudo server nfs-kernal-server restart
4.测试
$sudo mount -t nfs localhost:/source/rootfs /mnt
$sudo umount /mnt
3(单补)、samba(SMB协议)
主要是用于文件的共享,区别于nfs的文件共享,samba实现是在开发主机上的windows与linux虚拟机之间进行不同平台的文件传输。nfs的共享是网络文件实现共享。
二、BootLoader的选择和移植
一、BootLoader的概念
在操作系统内核运行之前的一段引导程序。这段程序起到了初始化硬件设备、建立内存空间映射图,将系统的软硬件带到一个合适的环境,为最终调用系统内核准备好正确的环境。英译名称Boot Loader。
二、为什么系统移植前要先进行BootLoader?
BootLoader的任务就是引导操作系统,简单的说就是启动内核,(在程序运行前,所有的文件数据都存放在Disk(Rom)上,Ram断电丢失数据)让内核运行就是将内核加载到内存Ram中运行。
第一个疑问:是谁将内核搬运到内存中去运行的?
第二个疑问:内存是SDRAM。与SRAM有区别。SRAM是上电系统就可以运行,而SDRAM需要软件进行初始化才能运行。也就是说在将内核搬运到SDRAM之前,需要对其进行 初始化,那么是谁来初始化SDRAM的?
以上两个疑问其实均是由BootLoader来完成的。目的就是将内核的运行环境准备好,BootLoader配好相应的环境才能使得系统运行起来。
三、BootLoader的分类
BootLoader作为引导程序,有很多种。大概分类如下:
由上图可以看出,不同的BootLoader具有不同的适用范围。其中最为我们所熟悉认识的就是U-Boot。全称是Universal BootLoader,是一个通用的引导程序。同时支持x86、ARM、PowerPC等架构。
四、U-Boot的目录结构
* board 目标板相关文件,主要包含SDRAM、FLASH驱动;
* common 独立于处理器体系结构的通用代码,如内存大小探测与故障检测;
* cpu 与处理器相关的文件。如mpc8xx子目录下含串口、网口、LCD驱动及中断初始化等文件;
* driver 通用设备驱动,如CFI FLASH驱动(目前对INTEL FLASH支持较好)
* doc U-Boot的说明文档;
* examples可在U-Boot下运行的示例程序;如hello_world.c,timer.c;
* include U-Boot头文件;尤其configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件;
* lib_xxx 处理器体系相关的文件,如lib_ppc, lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件;
* net 与网络功能相关的文件目录,如bootp,nfs,tftp;
* post 上电自检文件目录。尚有待于进一步完善;
* rtc RTC驱动程序;
* tools 用于创建U-Boot S-RECORD和BIN镜像文件的工具;
五、U-Boot的工作模式
U-Boot的工作模式有启动加载模式和下载模式。启动加载模式是Bootloader的正常工作模式,嵌入式产品Bootloader必须工作在这种模式下,Bootloader将嵌入式操作系统从FLASH芯片中加载到SDRAM中运行,整个过程是自动的。下载模式就是BootLoader通过tftp(或其他通信手段)将内核映像或根文件系统映像等从PC机中下载到目标板的SDRAM中运行,用户可以利用BootLoader提供的一些令接口来完成自己想要的操作,这种模式主要用于测试和开发。
六、U-Boot的启动过程
大多数BootLoader都分为stage1和stage2两大部分,U-Boot也不例外。依赖于cpu体系结构的代码(如设备初始化代码等)通常都放在stage1且可以用汇编语言来实现,而stage2则通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。
1、 stage1(start.s代码结构)
U-Boot的stage1代码通常放在start.s文件中,它用汇编语言写成,其主要代码部分如下:
(1) 定义入口。由于一个可执行的image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在rom(Flash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。
(2)设置异常向量(exception vector)。
(3)设置CPU的速度、时钟频率及中断控制寄存器。
(4)初始化内存控制器 。
(5)将rom中的程序复制到ram中。
(6)初始化堆栈 。
(7)转到ram中执行,该工作可使用指令ldrpc来完成。
2、 stage2(C语言代码部分)
lib_arm/board.c中的start armboot是C语言开始的函数,也是整个启动代码中C语言的主函数,同时还是整个U-Boot(armboot)的主函数,该函数主要完成如下操作:
(1)调用一系列的初始化函数。
(2)初始化flash设备。
(3)初始化系统内存分配函数。
(4)如果目标系统拥有nand设备,则初始化nand设备。
(5)如果目标系统有显示设备,则初始化该类设备。
(6)初始化相关网络设备,填写ip,c地址等。
(7)进入命令循环(即整个Boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作。
三、Kernel的配置、编译、移植
一、将下载好的linux-2.6.35.tar.bz2拷贝到主目录下解压
二、修改顶层目录下的Makefile,主要修改平台的体系架构和交叉编译器,代码如下:
ARCH ?= $(SUBARCH)
CROSS_COMPILE ?=
CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:“%”=%)
修改以上代码为:
ARCH ?= arm ----》体系架构是arm架构
CROSS_COMPILE ?= arm-cortex_a8-linux-gnueabi- ----》交叉编译器是arm-cortex_a8平台的
注意:这两个变量值会直接影响顶层Makefile的编译行为,即选择编译哪些代码,用什么编译器进行编译。
三、拷贝标准版配置文件,目的是得到跟我们开发板相关的配置信息。
$ cp arch/arm/configs/s5pc100_defconfig .config
这里拷贝arch/arm/configs/s5pc100_defconfig到 .config文件是选取跟我们开发板相关的代码。因为Linux支持的平台非常非常多,不仅仅是ARM处理器,当然我们编译的时候只需要编译跟我们平台相关的代码就可以了,平台相关的不需要编译,那么就有个问题,Linux系统中的源代码文件有一万多以个,面对这么庞大的文件数量,我们如何去选择呢?
其实我们只需进行很简单的操作,就可以选择出我们要编译的代码,具体的方法就是把相应平台的_deconfig直接拷贝到顶层目录的.config文件中,这样.config文件中就记录了我们要移植平台的平台信息,因为在配置内核时,系统会把所有的配置信息都保存在顶层目录的.config文件中。注意在第一次,进行make menuconfig时,系统会根据我们选取的平台信息自动选取相关的代码和模块,因此我们只需要进入然后再退出,选择保存配置信息就行了,系统会把这些跟我们移植平台相关的所有配置信息全部保存在顶层目录的.config文件中。
四、配置内核
$make menuconfig
注意:第一次进去,不做任何操作,直接推出,在推出时提示是否保存配置信息,一定要保存配置信息,点击“YES”。这样我们的.config中就已经保存了我们开发平台的信息。
在这个环节,我们需要关心一个问题,make menuconfig时,系统到低都做了哪些事情?为什么会出现图形化的界面?图形化的界面中的相关内容是从哪里来的?
图形化的界面当然是由一个特殊的图形库来实现的,还记得第一次make menuconfig时,系统并没有出现图形化的界面,而是报错了,并且提示我们缺少 ncurses-devel ,此时只需要按照要求安装一个libncurses5-dev就行了,sudo apt-get install libncurses5-dev,有了这个图形化库的支持,我们才能够正常显示图形化界面。
好了,图形化界面的问题解决了,那还有另外一个问题就是图形化界面里面的内容是从哪里来的?要回答这个问题,我们就要提一下Linux内核的设计思想了,Linux 内核是以模块的方式来组织这个操作系统的,那么,为什么要用模块的方式来组织呢?模块的概念又是什么呢?在此来一一回答这个问题。
Linux2.6内核的源码树目录下一般都会有两个文件:Kconfig和Makefile。分布在各目录下的Kconfig构成了一个分布式的内核配置数据库,每个Kconfig分别描述了所属目录源文件相关的内核配置菜单。每个目录都会存放功能相对独立的信息,在每个目录中会存放各个不同的模块信息,比如在/dev/char/目录下就存放了所有字符设备的驱动程序,而这些程序代码在内核中是以模块的形式存在的,也就是说当系统需要这个驱动的时候,会把这个驱动以模块的方式编译到系统的内核中,编译分为静态编译和动态编译,静态编译内核体积比动态编译的体积要大,前面已经说了每个目录下面都会有一个Kconfig的文件,我们还会问,这个文件中都存放了什么信息?前面说了,每个目录的Kconfig文件描述了所属目录源文件相关的内核配置菜单,有其特殊的语法格式,图形化界面的文字正是从这个文件中读取出来的,如果把这个文件中的相应目录文件的信息全部删除,那么在图形化界面中将看不到该模块的信息,因此也不能进行模块的配置。
在内核配置make menuconfig(或xconfig等)时,系统会自动从Kconfig中读出配置菜单,用户配置完后保存到.config(在顶层目录下生成)中。在内核编译时,主Makefile调用这个.config,(.config的重要性就体现在,它保存了我们的所有的配置信息,是我们选取源代码并且进行编译源代码的最终依据!!!)就知道了用户对内核的配置情况。上面的内容说明:Kconfig就是对应着内核的配置菜单。假如要想添加新的驱动到内核的源码中,可以通过修改Kconfig来增加对我们驱动的配置菜单,这样就有途径选择我们的驱动,假如想使这个驱动被编译,还要修改该驱动所在目录下的Makefile。因此,一般添加新的驱动时需要修改的文件有两种,即:Kconfig 和相应目录的Makefile(注意不只是两个),系统移植的重要内容就是给内核添加和删除相应的模块,因此主要修改的内核文件就是Kconfig 和相应目录的Makefile这两个文件。
五、编译内核
$make zImage
通过上述操作我们能够在 arch/arm/boot 目录下生成一个 zImage 文件,这就是经过压缩的内核镜像。
内核的编译过程是非常复杂的,注意这里的编译是静态编译,此时会执行顶层目录下的Makefile中的zImage命令,在执行的过程中,会根据当前目录的.config文件去选择编译源代码。编译内核的具体步骤比较复杂,有时间会另写文章详细描述。
六、通过tftp网络服务下载测试内核
setenv bootcmd tftp 20008000(内存地址) zImage\;go 20008000
setenv bootargs nfs nfsroot=192.168.1.199(虚拟机的ip):/source/rootfs ip=192.168.1.200(开发板的ip) init=/linuxrc(第一个要启动的用户进程) ttySAC0,115200(设置中断为串口1,波特率为:115200)
保存环境变量,复位开发板,测试是否能够正常启动(注意:在此之前应设置好需要nfs挂载的文件系统,最后才能看到效果)。内核测试和启动过程也是比较复杂的,在后续的文章中会详细介绍。
四、文件系统
学习到这里,文件系统的制作和移植是系统移植的最后一步了。在这里首先要提几个疑惑:
1.什么是文件系统?
2.如何实现文件系统?
3.常用的文件系统有哪些?为什么需要这些文件系统?
文件系统我们在日常生活不叫文件系统,一般是叫做资料库。资料库里面的文件众多,资料库采用了分类索引的方法来实现快速查找。把这种进行了分类索引的资料库叫文件系统。
对于计算机而言,文件其实就是数据,只能存储在物理介质上面,比如:硬盘,但是我们人不能自己读取到物理介质上的数据资料,或者自己动手把文件写入物理介质这些都是不现实的。物理介质上资料数据的读写只能采用程序来实现,为了方便实现,程序又被分成了物理介质驱动程序、内容存储程序和文件内容存储程序:
物理介质驱动程序专门用于从物理介质上存取数据;
内容存储程序用于把文件内容和文件属性信息打包;
文件内容存储程序用于把用户输入形成文件内容,或者取得文件内容显示出来。
我们可以把一个文件系统(倒树)分解成多个文件系统(倒树)分别存放到存储介质上,比如:一个存储到光盘里,一个存储到硬盘中,在使用时,我们把光盘里的文件系统的根目录挂到硬盘文件系统的一个目录下面,这样访问这个目录就相当于是访问光盘的根目录了,找到了根目录,我们也就可以访问整个光盘上的文件系统了。
“Linux系统中一切皆文件”在我们学习Linux系统的时候经常听到的一句话。它揭示了文件系统对于Linux系统的重要性;实际上文件系统对于所有的操作系统都很重要,因为它们把大部分的硬件设备和软件数据以文件的形式进行管理。
Linux目录结构
/bin Binary的缩写,此目录用来存最经常使用的命令
/sbin SuperUser的意思 这里是存放的是系统管理员使用的系统管理程序
/home 存放普通用户的主目录,在Linux中每个用户都有自己一个目录
/root 该目录是系统管理员,也称超级权限者的用户主目录
/lib 系统开机所需要的最基本的动态链接共享库,作用类似于windows中的dll文件。几乎所有的应用程序都需要用到这些共享库
/lost+found 这个目录一般是空着,当系统非法关机后,这里就存放了一些文件
/etc 所有系统管理所需要的配置文件和子目录
/user 用户的很多应用程序和文件都存在这个目录下
/boot 存放的是启动Linux时使用的一些核心文件,包括一些连接文件和镜像文件
/proc 这个目录是一个虚拟的目录,是系统内存的映射,访问这个目录来获取系统信息
/srv service缩写,该目录存放一些服务启动之后需要提取的数据
/sys 这是Linux2.6内核一个很大的变化,在该目录下安装了2.6内核中新出的文件系统sysfs
/temp 这个目录用来存一些临时文件
/dev 类似于windows的设备管理器,将所有的硬件用文件的形式存储
/media Linux系统自动识别一些设备,例如U盘、光驱等,在识别后,Linux会把识别的设备挂载到这个目录下
/mnt 系统提供了该目录是为了让用户临时挂载别的文件系统,我们可以将外部的存储挂载在/mnt上,然后进入该目录就可以查看里面的内容
/opt 这是给主机额外安装软件所摆放的目录。
/usr/local 这是给另一个主机额外安装软件所安装的目录,一般是通过编译源码的方式安装的程序
/var 这个目录中存放着不断扩充的东西,习惯将被经常修改的目录放在这个目录下面。包括各种日志文件。
/selinux SELinux是一种安全子系统,它能控制程序只访问特定文件。
Linux系统对设备和数据的管理框架图如下:
A. VFS(virtual file system)是虚拟文件系统,它管理特殊文件(虚拟文件)、磁盘文件和设备文件
B. fs_operations结构是由一系列文件操作接口函数组成,由文件系统层来完成,为VFS提供文件操作;
C. 在文件系统层,磁盘文件要实现各种文件系统(如:ext2),设备文件要实现各种抽象的设备驱动
D. 在设备驱动层,磁盘驱动要实现各种磁盘的驱动程序,其他设备驱动要实现具体的设备驱动
E. 物理层就是设备自身
为什么会有不同的文件类型?
由于存储介质有很多种,所以没有办法用一种统一的格式存放文件系统到各种不同的存储介质上,而是需要多种不同的存储格式来适应各种存储介质的特性,以求达到存取效率和空间利用率的最优化,这样就需要对每种存储格式制定一个规范,这写规范就叫文件系统类型。常见的文件系统类型有:
1.Dos
FAT16
2.windows
FAT16、FAT32、NTFS
3.Linux
Minix、ext、ext2 、ext3 、ISO9660 、jffs2, yaffs, yaffs2、cramfs, romfs, ramdisk, rootfs、proc、sysfs、usbfs、devpts、 tmpfs & ramfs、 NFS
由此可见,Linux支持的文件系统最多。以不同的介质来分类,如下所示:
磁盘
FAT16、 FAT16、FAT32、NTFS、ext、ext2 、ext3、Minix
光盘
ISO9660、
Flash
jffs2, yaffs, yaffs2、cramfs, romfs
内存
Ramdisk、tmpfs & ramfs
虚拟
rootfs、proc、sysfs、usbfs、devpts、NFS
常用的存储介质理论上都可以用于存储Linux支持的文件系统;因为我们这里只研究嵌入式系统,而嵌入式系统由于体积和移动特性的限制,不能采用磁盘和光盘,所以只能采用flash类的存储设备、内存和虚拟存储设备作为文件系统的存储介质;
flash芯片的驱动程序是由系统来提供,所以它的存取特点完全是flash自身的特点,这时最好有更加适合flash的文件系统——Jffs、Yaffs、Cramfs和Romfs。这些文件系统都是嵌入式Linux系统中常用的文件系统,可以根据特点来选择使用它们,特点如下:
共同点
基于MTD驱动
Jffs
A.针对NOR Flash的实现
B.基于哈希表的日志型文件系统
C.采取损耗平衡技术,每次写入时都会尽量使写入的位置均匀分布
D.可读写,支持数据压缩
E.崩溃/掉电安全保护
F.当文件系统已满或接近满时,因为垃圾收集的关系,运行速度大大放慢
Yaffs
A.针对Nand Flash的实现
B.日志型文件系统
C.采取损耗平衡技术,每次写入时都会尽量使写入的位置均匀分布
D.可读写,不支持数据压缩
E.挂载时间短,占用内存小
F.自带Nandflash驱动,可以不使用VFS和MTD
Cramfs
A.单页压缩,支持随机访问,压缩比高达2:1
B.速度快,效率高
C.只读,有利于保护文件系统免受破坏,提高了系统的可靠性,但是无法对其内容进行扩充
Romfs
A.简单的、紧凑的、只读的文件系统
B.顺序存放数据,因而支持应用程序以XIP(execute In Place,片内运行)方式运行,在系统运行时,节省RAM空间
特有的文件系统类型:Ramdisk文件系统
在Linux系统中,内存经常用于存储文件系统,这种叫做Ramdisk,Ramdisk有两种,一种是完全把内存看成物理存储介质,利用内存模拟磁盘,运用磁盘的文件系统类型;另一种只是在内存中存储了文件系统逻辑结构,运用tmpfs & ramfs文件系统类型:
tmpfs & ramfs
1. 概述
用物理内存模拟磁盘分区,挂载这种分区后,就可以跟读写磁盘文件一样读写这里面的文件,但是操作速度要比磁盘文件快得多;所以一般应用在下面几个方面:
1)读写速度要求快的文件应该放在这种文件系统中
2)磁盘分区为flash的情况下,把需要经常读写的文件放在这种文件系统中,然后定期写回flash
3)系统中的临时文件,如/tmp、/var目录下的文件应该放在这种文件系统中
4)/dev设备文件(因为设备文件随驱动和设备的加载和卸载而变化),应该放在这种文件系统中
2. 特点
1)由于数据都存放在物理内存中,所以系统重启后,这个文件系统中的数据会全部丢失
2)ramfs在没有指定最大的大小值情况下,会自动增长,直到用掉系统中所有的物理内存为止,这时会导致系统的崩溃,建议挂载时最好限定其最大的大小值
3)tmpfs如果指定了大小值,自动增长至大小值后,系统会限定它的大小;这个文件系统占用的物理内存页可以背置换到swap分区,但是ramfs不行
Linux系统移植到这里就先告一段落,看了好多的blog,推荐的还是CSDN上“九城风雪”的linux系统移植步骤,让我学到了很多,本篇blog很多内容不属于原创,更多的是诸位前辈的心血,仅仅适用于学习交流,如果有侵权,请大家及时联系我,及时删帖!!谢谢