精华内容
参与话题
问答
  • 1.1 系统移植简介 在正式开始一直前,首先要明白什么系统移植,知其然还要知其所以然,对于嵌入式的学习方法,我认为都是大同小异的:从宏观上把握(解决为什么的问题),微观上研究(解决正在做什么的问题)。 ...

    1.1 系统移植简介

    在正式开始一直前,首先要明白什么系统移植,知其然还要知其所以然,对于嵌入式的学习方法,我认为都是大同小异的:从宏观上把握(解决为什么的问题),微观上研究(解决正在做什么的问题)。

    嵌入式Linux系统移植主要由四大部分组成:
     搭建交叉开发环境;
     bootloader的选择和移植;
     kernel的配置、编译、和移植;
     根文件系统的制作;
    关于具体的移植详情请看后文。

    1.2 Binutils工具集 解析

    对于嵌入式系统开发,掌握相应的工具至关重要,它能使我们解决问题的效率大大提高。目前,可以说嵌入式系统的开发工具是GNU的天下,因为来自GNU的GCC编译器支持大量的目标处理器。除了GCC,还有一个非常重要的、同样来自于GNU的工具集(toolchain) —— binutils toolchain。这一工具集中存在的一些工具,可以说是我们开发和调试不可缺少的利器。 Binutils中的工具不少和GCC相类似,也是针对特定的处理器的。

    在binutils中以下的工具是我们在做嵌入式系统开发时需要掌握的:

    表 1

    在这里插入图片描述
    在这里插入图片描述
    下面是各命令的详细使用方法:
     addr2line
    addr2line是用来将程序地址转换成其所对应的程序源文件及所对应的代码行,当然,也可以得到所对应的函数。为了说明addr2line是如何使用的,我们需要有一个练习用的程序。先采用编辑工具编辑一个test.c源文件,其内容如图所示。

    在这里插入图片描述

    运行如下的命令将test.c编译成可执行文件,
    在这里插入图片描述

    注意:必须加 -g 这个选项 并运行之。在运行test程序后,我们可以在其终端上看到它打印出的fun()函数的地址 —— 0x80483e4。
    现在,我们可以用这一地址来看一看addr2line是如何使用的。在终端中运行如下的命令,从命令的运行结果来看,addr2line工具正确的指出了地址0x80483e4 所对于应的程序的具体位置是在哪以及所对应的函数名是什么。

    在这里插入图片描述
    可能有人会问了:这个0x80483e4地址是我们打印出来,即然有打印,我们一般情况下也会打印出其具体的函数位置,而不是只打印地址,我为何要这么绕一下通过addr2line去找到地址所对应的函数呢?其实,这里打印出地址只是为了得到一个地址以便用于练习。在现实中,地址往往是在调试过程中或是当程序崩溃时通过某种方式获得的。此外,采用nm工具(后面会讲到)可以得到如下的函数地址信息。

    farsight@ubuntu:~/kernel$ nm -n test
             w _Jv_RegisterClasses
             w __gmon_start__
             U __libc_start_main@@GLIBC_2.0
             U printf@@GLIBC_2.0
    080482b4 T _init
    08048330 T _start
    08048360 t __do_global_dtors_aux
    080483c0 t frame_dummy
    080483e4 T fun
    08048401 T main
    08048420 T __libc_csu_init
    08048490 T __libc_csu_fini
    08048492 T __i686.get_pc_thunk.bx
    080484a0 t __do_global_ctors_aux
    080484cc T _fini
    080484e8 R _fp_hw
    080484ec R _IO_stdin_used
    0804862c r __FRAME_END__
    08049f14 d __CTOR_LIST__
    08049f14 d __init_array_end
    08049f14 d __init_array_start
    08049f18 d __CTOR_END__
    08049f1c d __DTOR_LIST__
    08049f20 D __DTOR_END__
    08049f24 d __JCR_END__
    08049f24 d __JCR_LIST__
    08049f28 d _DYNAMIC
    08049ff4 d _GLOBAL_OFFSET_TABLE_
    0804a00c D __data_start
    0804a00c W data_start
    0804a010 D __dso_handle
    0804a014 A __bss_start
    0804a014 A _edata
    0804a014 b completed.6159
    0804a018 b dtor_idx.6161
    0804a01c A _end
    

     as
    as汇编器,将汇编代码汇编成目标文件
    ** size **
    size工具,就是列程序文件中各段的大小。在后面的章节中,我们会使用objdump查看段信息,除了这三个段还有.rdata和.idata两个段,其中.rdata段被归类到.text段中,而.idata段被归类到.data段中。下面是采用size工具所显示出的test中的段大小信息。

    在这里插入图片描述
     nm
    nm用于列出程序文件中的符号,符号是指函数或是变量名什么的。

    farsight@ubuntu:~/kernel$ nm -n test
             w _Jv_RegisterClasses
             w __gmon_start__
             U __libc_start_main@@GLIBC_2.0
             U printf@@GLIBC_2.0
    080482b4 T _init
    08048330 T _start
    08048360 t __do_global_dtors_aux
    080483c0 t frame_dummy
    080483e4 T fun
    08048401 T main
    08048420 T __libc_csu_init
    08048490 T __libc_csu_fini
    08048492 T __i686.get_pc_thunk.bx
    080484a0 t __do_global_ctors_aux
    080484cc T _fini
    080484e8 R _fp_hw
    080484ec R _IO_stdin_used
    0804862c r __FRAME_END__
    08049f14 d __CTOR_LIST__
    08049f14 d __init_array_end
    08049f14 d __init_array_start
    08049f18 d __CTOR_END__
    08049f1c d __DTOR_LIST__
    08049f20 D __DTOR_END__
    08049f24 d __JCR_END__
    08049f24 d __JCR_LIST__
    08049f28 d _DYNAMIC
    08049ff4 d _GLOBAL_OFFSET_TABLE_
    0804a00c D __data_start
    0804a00c W data_start
    0804a010 D __dso_handle
    0804a014 A __bss_start
    0804a014 A _edata
    0804a014 b completed.6159
    0804a018 b dtor_idx.6161
    0804a01c A _end
    

    nm所列出的每一行有三部分组成:
    第一列是指程序运行时的符号所对应的地址,对于函数则地址表示的是函数的开始地址,对于变量则表示的是变量的存储地址;
    第二列是指相应符号是放在哪一个段的;
    第三列则是指符号的名称。
    在前面我们讲解addr2line时,我们提到addr2line是将程序地址转换成这一地址所对应的具体函数是什么,而nm则是全面的列出这些信息。但是,nm不具备列出符号所在的源文件及其行号这一功能,因此,我们说每一个工具有其特定的功能,在嵌入式系统的开发过程中我们需要灵活的运用它们。
    对于nm列出的第二列信息,非常的有用,其意义在于可以了解我们在程序中所定义的一个符号(比如变量等等)是被放在程序的哪一个段的,下表列出了第二列将会出现的部分字母的含义,要参看所有字母的意思,请在你的开发环境中运行“man nm”。

    表2

    在这里插入图片描述
     strip
    strip的功能也相对的简单,主要用于去除程序文件中的调试信息以便减小文件的大小。对于strip的功能,其与objcopy带–strip-debug参数时的功能是一样的,这我们前面也有提及。strip所具有的功能,objcopy也都有。

    在这里插入图片描述
    可以看到test小了几KB,strip在大文件中有更好的体现。
     objdump
    objdump可以用来查看目标程序中的段信息和调试信息,也可以用来对目标程序进行反汇编。我们知道程序是由多个段组成的,比如.text是用来放代码的、.data是用来放初始化好的数据的、.bss是用来放未初始化好的数据的,等等。在嵌入式系统的开发过程中,我们有时需要知道所生成的程序中的段信息来分析问题。比如,我们需要知道其中的某个段在程序运行时,共起始地址是什么,或者,我们需要知道正在运行的程序中是否存在调试信息等等。
    【1】下面是使用objdump的–h选项来查看程序中的段信息,练习用的程序如前面的图,这里假设你已将其编译成了可执行文件test。
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述
    【2】反汇编

    在这里插入图片描述
     objcopy
    objcopy的功能非常的强大,它可以对最后生成的程序文件进行一定的编辑。
    作用:格式转换
    例:objcopy -O binary xx xx.bin
     readelf
    readelf可以显示elf格式可执行文件的信息。ELF格式是UNIX系统实验室作为应用程序二进制接口开发的。ELF格式是Unix/Linux平台上应用最广泛的二进制工业标准之一。

    在这里插入图片描述

    1.3关于gcc、glibc和binutils模块之间的关系

    1.3.1 gcc、glibc和binutils模块之间的关系

    1、gcc(gnu collect compiler)是一组编译工具的总称。它主要完成的工作任务是“预处理”和“编译”,以及提供了与编译器紧密相关的运行库的支持,如libgcc_s.so、libstdc++.so等。
    2、binutils提供了一系列用来创建、管理和维护二进制目标文件的工具程序,如汇编(as)、连接(ld)、静态库归档(ar)、反汇编(objdump)、elf结构分析工具(readelf)、无效调试信息和符号的工具(strip)等。通常,binutils与gcc是紧密相集成的,没有binutils的话,gcc是不能正常工作的。
    3、glibc是gnu发布的libc库,也即c运行库。glibc是linux系统中最底层的api(应用程序开发接口),几乎其它任何的运行库都会倚赖于glibc。glibc除了封装linux操作系统所提供的系统服务外,它本身也提供了许多其它一些必要功能服务的实现,主要的如下:
     string,字符串处理
     signal,信号处理
     dlfcn,管理共享库的动态加载
     direct,文件目录操作
     elf,共享库的动态加载器,也即interpreter
     iconv,不同字符集的编码转换
     inet,socket接口的实现
     intl,国际化,也即gettext的实现
     io
     linuxthreads
     locale,本地化
     login,虚拟终端设备的管理,及系统的安全访问
     malloc,动态内存的分配与管理
     nis
     stdlib,其它基本功能

    1.3.2在现有系统上如何升级(redhat9上实践的)

    1、升级这些库时,最好不要覆盖系统中缺省的;因为这些库,尤其是glibc库,是系统中最核心的共享库和工具,如果盲目覆盖,很可能导致整个系统瘫痪,因为一般更新glibc库时,其它所有以来libc库的共享库都需要重新被编译一遍。因此,为了调试某个程序进入glibc时,最好把glibc安装到/usr/local/lib下。

    2、首先编译glibc库。注意最好令建立一个glibc-build的目录,configure时加上–enable-add-ons=linuxthreads选项。make install安装到/usr/local下。

    3、修改gcc的spec文件(/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/specs),更改ld-linux.so.2为/usr/local/lib下的新的共享库装载器。

    4、编译binutils库,此时被编译出的程序会连接到/usr/local/lib下的新的libc库。注意,在configure前,需要设置ld缺省连接的路径(LIBRARY_PATH=/usr/local/lib:/lib:/usr/lib),否则binutils会configure出错,找不到libc中的一些符号。具体步骤如下:
    (1)export LIBRARY_PATH=/usr/local/lib:/lib:/usr/lib
    (2)mkdir binutils-build && cd binutils-build
    (3)…/binutils-2.13.90.0.18/configure
    (4)make
    (5)make -C ld clean
    (6)make -C ld LIB_PATH=/usr/lib:/lib:/usr/local/bin(设置编译后的ld的缺省库搜索路径,后面的比前面的优先级高)
    (7)make install

    1.3.3总结

    【1】运行时,动态库的装载依赖于ld-linux.so.6的实现,它查找共享库的顺序如下:
    (1)ld-linux.so.6在可执行的目标文件中被指定,可用readelf命令查看;
    (2)ld-linux.so.6缺省在/usr/lib和lib中搜索;当glibc安装到/usr/local下时,它查找/usr/local/lib;
    (3)LD_LIBRARY_PATH环境变量中所设定的路径;
    (4)/etc/ld.so.conf(或/usr/local/etc/ld.so.conf)中所指定的路径,由ldconfig生成二进制的ld.so.cache中。

    【2】编译时,搜索库的路径顺序如下:
    (1)ld-linux.so.6由gcc的spec文件中所设定;
    (2)gcc --print-search-dirs所打印出的路径,主要是libgcc_s.so等库。可以通过GCC_EXEC_PREFIX来设定;
    (3)LIBRARY_PATH环境变量中所设定的路径,或编译的命令行中指定的-L/usr/local/lib ;
    (2)binutils中的ld所设定的缺省搜索路径顺序,编译binutils时指定。(可以通过“ld --verbose | grep SEARCH”来查看)。

    【3】二进制程序的搜索路径顺序为PATH环境变量中所设定。一般/usr/local/bin高于/usr/bin。

    【4】编译时的头文件的搜索路径顺序,与library的查找顺序类似。一般/usr/local/include高于/usr/include。

    展开全文
  • linux系统移植步骤

    万次阅读 多人点赞 2018-07-13 22:52:08
    在众多嵌入式操作系统中,Linux目前发展最快、应用最为广泛。性能优良、源码开放的... 嵌入式Linux系统移植主要由四大部分组成: 一、搭建交叉开发环境 二、bootloader的选择和移植 三、kernel的配置、编译、...

    在众多嵌入式操作系统中,Linux目前发展最快、应用最为广泛。性能优良、源码开放的Linux具有体积小、内核可裁减、网络功能完善、可移植性强等诸多优点,非常适合作为嵌入式操作系统。一个最基本的Linux操作系统应该包括:引导程序、内核与根文件系统三部分。

      嵌入式Linux系统移植主要由四大部分组成:

      一、搭建交叉开发环境

      二、bootloader的选择和移植

      三、kernel的配置、编译、和移植

      四、根文件系统的制作

    linux系统移植步骤

      第一部分:搭建交叉开发环境

      先介绍第一分部的内容:搭建交叉开发环境,首先必须得思考两个问题,什么是交叉环境? 为什么需要搭建交叉环境?

      先回答第一个问题,在嵌入式开发中,交叉开发是很重要的一个概念,开发的第一个环节就是搭建环境,第一步不能完成,后面的步骤从无谈起,这里所说的交叉开发环境主要指的是:在开发主机上(通常是我的pc机)开发出能够在目标机(通常是我们的开发板)上运行的程序。嵌入式比较特殊的是不能在目标机上开发程序(狭义上来说),因为对于一个原始的开发板,在没有任何程序的情况下它根本都跑不起来,为了让它能够跑起来,我们还必须要借助pc机进行烧录程序等相关工作,开发板才能跑起来,这里的pc机就是我们说的开发主机,想想如果没有开发主机,我们的目标机基本上就是无法开发,这也就是电子行业的一句名言:搞电子,说白了,就是玩电脑!

      然后回答第二个问题,为什么需要交叉开发环境?主要原因有以下几点:

      原因1:嵌入式系统的硬件资源有很多限制,比如cpu主频相对较低,内存容量较小等,想想让几百MHZ主频的MCU去编译一个Linux kernel会让我们等的不耐烦,相对来说,pc机的速度更快,硬件资源更加丰富,因此利用pc机进行开发会提高开发效率。

      原因2:嵌入式系统MCU体系结构和指令集不同,因此需要安装交叉编译工具进行编译,这样编译的目标程序才能够在相应的平台上比如:ARM、MIPS、 POWEPC上正常运行。

      交叉开发环境的硬件组成主要由以下几大部分:

      1.开发主机

      2.目标机(开发板)

      3.二者的链接介质,常用的主要有3中方式:(1)串口线 (2)USB线 (3)网线

      对应的硬件介质,还必须要有相应的软件“介质”支持:

      1.对于串口,通常用的有串口调试助手,putty工具等,工具很多,功能都差不多,会用一两款就可以;

      2.对于USB线,当然必须要有USB的驱动才可以,一般芯片公司会提供,比如对于三星的芯片,USB下载主要由DNW软件来完成;

      3.对于网线,则必须要有网络协议支持才可以,常用的服务主要两个

      第一:tftp服务:

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

      第二:nfs服务:

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

      另外,还有一个名字叫samba的服务也比较重要,主要用于文件的共享,这里说的共享和nfs的文件共享不是同一个概念,nfs的共享是实现网络文件的共享,而samba实现的是开发主机上Windows主机和Linux虚拟机之间的文件共享,是一种跨平台的文件共享,方便的实现文件的传输。

      以上这几种开发的工具在嵌入式开发中是必备的工具,对于嵌入式开发的效率提高做出了伟大的贡献,因此,要对这几个工具熟练使用,这样你的开发效率会提高很多。等测试完成以后,就会把相应的目标文件烧录到Flash中去,也就是等发布产品的时候才做的事情,因此对于开发人员来说,所有的工作永远是测试。

      通过前面的工作,我们已经准备好了交叉开发环境的硬件部分和一部分软件,最后还缺少交叉编译器,读者可能会有疑问,为什么要用交叉编译器?前面已经讲过,交叉开发环境必然会用到交叉编译工具,通俗地讲就是在一种平台上编译出能运行在体系结构不同的另一种平台上的程序,开发主机PC平台(X86 CPU)上编译出能运行在以ARM为内核的CPU平台上的程序,编译得到的程序在X86 CPU平台上是不能运行的,必须放到ARM CPU平台上才能运行,虽然两个平台用的都是Linux系统。相对于交叉编译,平常做的编译叫本地编译,也就是在当前平台编译,编译得到的程序也是在本地执行。用来编译这种跨平台程序的编译器就叫交叉编译器,相对来说,用来做本地编译的工具就叫本地编译器。所以要生成在目标机上运行的程序,必须要用交叉编译工具链来完成。

      这里又有一个问题,不就是一个交叉编译工具吗?为什么又叫交叉工具链呢?原因很简单,程序不能光编译一下就可以运行,还得进行汇编和链接等过程,同时还需要进行调试,对于一个很大工程,还需要进行工程管理等等,所以,这里 说的交叉编译工具是一个由编译器、连接器和解释器组成的综合开发环境,交叉编译工具链主要由binutils(主要包括汇编程序as和链接程序ld)、gcc(为GNU系统提供C编译器)和glibc(一些基本的C函数和其他函数的定义) 3个部分组成。有时为了减小libc库的大小,也可以用别的 c 库来代替 glibc,例如 uClibc、dietlibc 和 newlib。

      那么,如何得到一个交叉工具链呢?是从网上下载一个“程序”然后安装就可以使用了吗?回答这个问题之前先思考这样一个问题,我们的交叉工具链顾名思义就是在PC机上编译出能够在我们目标开发平台比如ARM上运行的程序,这里就又有一个问题了,我们的ARM处理器型号非常多,难道有专门针对我们某一款的交叉工具链吗?若果有的话,可以想一想,这么多处理器平台,每个平台专门定制一个交叉工具链放在网络上,然后供大家去下载,想想可能需要找很久才能找到适合你的编译器,显然这种做法不太合理,且浪费资源!因此,要得到一个交叉工具链,就像我们移植一个Linux内核一样,我们只关心我们需要的东西,编译我们需要的东西在我们的平台上运行,不需要的东西我们不选择不编译,所以,交叉工具链的制作方法和系统移植有着很多相似的地方,也就是说,交叉开发工具是一个支持很多平台的工具集的集合(类似于Linux源码),然后我们只需从这些工具集中找出跟我们平台相关的工具就行了,那么如何才能找到跟我们的平台相关的工具,这就是涉及到一个如何制作交叉工具链的问题了。

      通常构建交叉工具链有如下三种方法:

      方法一 :分步编译和安装交叉编译工具链所需要的库和源代码,最终生成交叉编译工具链。该方法相对比较困难,适合想深入学习构建交叉工具链的读者。如果只是想使用交叉工具链,建议使用下列的方法二构建交叉工具链。

      方法二: 通过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. 测试交叉工具链

      到此,嵌入式Linux系统移植四大部分的第一部分工作全部完成,接下来可以进行后续的开发了。

      第二部分:bootloader的选择和移植

      一、Boot Loader 概念

      就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境,他就是所谓的引导加载程序(Boot Loader)。

      linux系统移植步骤

      二、为什么系统移植之前要先移植BootLoader?

      BootLoader的任务是引导操作系统,所谓引导操作系统,就是启动内核,让内核运行就是把内核加载到内存RAM中去运行,那先问两个问题:第一个问题,是谁把内核搬到内存中去运行?第二个问题:我们说的内存是SDRAM,大家都知道,这种内存和SRAM不同,最大的不同就是SRAM只要系统上电就可以运行,而SDRAM需要软件进行初始化才能运行,那么在把内核搬运到内存运行之前必须要先初始化内存吧,那么内存是由谁来初始化的呢?其实这两件事情都是由bootloader来干的,目的是为内核的运行准备好软硬件环境,没有bootloadr我们的系统当然不能跑起来。

      三、bootloader的分类。

      首先更正一个错误的说法,很多人说bootloader就是U-boot,这种说法是错误的,确切来说是u-boot是bootloader的一种。也就是说bootloader具有很多种类,大概的分类如下图所示:

      linux系统移植步骤

      由上图可以看出,不同的bootloader具有不同的使用范围,其中最令人瞩目的就是有一个叫U-Boot的bootloader,是一个通用的引导程序,而且同时支持X86、ARM和PowerPC等多种处理器架构。U-Boot,全称 Universal Boot Loader,是遵循GPL条款的开放源码项目,是由德国DENX小组开发的用于多种嵌入式CPU的bootloader程序,对于Linux的开发,德国的u-boot做出了巨大的贡献,而且是开源的。

      u-boot具有以下特点:

      ① 开放源码;

      ② 支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS;

      ③ 支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale;

      ④ 较高的可靠性和稳定性;

      ⑤ 高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等;

      ⑥ 丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等;

      ⑦ 较为丰富的开发调试文档与强大的网络技术支持;

      其实,把u-boot可以理解为是一个小型的操作系统。

      四、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通过某些通信手段将内核映像或根文件系统映像等从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的工作循环),接受用户从串口输入的命令,然后进行相应的工作。

      七、基于cortex-a8的s5pc100bootloader启动过程分析

      s5pc100支持两种启动方式,分别为USB启动方式和NandFlash启动方式:

      1. S5PC100 USB启动过程

      [1] A8 reset, 执行iROM中的程序

      [2] iROM中的程序根据S5PC100的配置管脚(SW1开关4,拨到4对面),判断从哪里启动(USB)

      [3] iROM中的程序会初始化USB,然后等待PC机下载程序

      [4] 利用DNW程序,从PC机下载SDRAM的初始化程序到iRAM中运行,初始化SDRAM

      [5] SDRAM初始化完毕,iROM中的程序继续接管A8, 然后等待PC下载程序(BootLoader)

      [6] PC利用DNW下载BootLoader到SDRAM

      [7] 在SDRAM中运行BootLoader

      2. S5PC100 Nandflash启动过程

      [1] A8 reset, 执行IROM中的程序

      [2] iROM中的程序根据S5PC100的配置管脚(SW1开关4,拨到靠4那边),判断从哪里启动(Nandflash)

      [3] iROM中的程序驱动Nandflash

      [4] iROM中的程序会拷贝Nandflash前16k到iRAM

      [5] 前16k的程序(BootLoader前半部分)初始化SDRAM,然后拷贝完整的BootLoader到SDRAM并运行

      [6] BootLoader拷贝内核到SDRAM,并运行它

      [7] 内核运行起来后,挂载rootfs,并且运行系统初始化脚本

      八、u-boot移植(基于cortex_a8的s5pc100为例)

      1.建立自己的平台

      (1)下载源码包2010.03版本,比较稳定

      (2)解压后添加我们自己的平台信息,以smdkc100为参考版,移植自己s5pc100的开发板

      (3)修改相应目录的文件名,和相应目录的Makefile,指定交叉工具链。

      (4)编译

      (5)针对我们的平台进行相应的移植,主要包括修改SDRAM的运行地址,从0x20000000

      (6)“开关”相应的宏定义

      (7)添加Nand和网卡的驱动代码

      (8)优化go命令

      (9)重新编译 make distclean(彻底删除中间文件和配置文件) make s5pc100_config(配置我们的开发板) make(编译出我们的u-boot.bin镜像文件)

      (10)设置环境变量,即启动参数,把编译好的u-boot下载到内存中运行,过程如下:

      1. 配置开发板网络

      ip地址配置:

      $setenv ipaddr 192.168.0.6 配置ip地址到内存的环境变量

      $saveenv 保存环境变量的值到nandflash的参数区

      网络测试:

      在开发开发板上ping虚拟机:

      $ ping 192.168.0.157(虚拟机的ip地址)

      如果网络测试失败,从下面几个方面检查网络:

      1. 网线连接好

      2. 开发板和虚拟机的ip地址是否配置在同一个网段

      3. 虚拟机网络一定要采用桥接(VM--Setting--》option)

      4. 连接开发板时,虚拟机需要设置成静态ip地址

      2. 在开发板上,配置tftp服务器(虚拟机)的ip地址

      $setenv serverip 192.168.0.157(虚拟机的ip地址)

      $saveenv

      3. 拷贝u-boot.bin到/tftpboot(虚拟机上的目录)

      4. 通过tftp下载u-boot.bin到开发板内存

      $ tftp 20008000(内存地址即可) u-boot.bin(要下载的文件名)

      如果上面的命令无法正常下载:

      1. serverip配置是否正确

      2. tftp服务启动失败,重启tftp服务

      #sudo service tftpd-hpa restart

      5. 烧写u-boot.bin到nandflash的0地址

      $nand erase 0(起始地址) 40000(大小) 擦出nandflash 0 - 256k的区域

      $nand write 20008000((缓存u-boot.bin的内存地址) 0(nandflash上u-boot的位置) 40000(烧写大小)

      6. 切换开发板的启动方式到nandflash

      1. 关闭开发板

      2. 把SW1的开关4拨到4的那边

      3. 启动开发板,它就从nandflash启动

      第三部分: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挂载的文件系统,最后才能看到效果)。内核测试和启动过程也是比较复杂的,在后续的文章中会详细介绍。

      第四部分:根文件系统的介绍

      由本文的第一张图:Flash存储中存放文件的分布图可知,文件系统的制作和移植是系统移植的最后一道工序了,在这里首先要提几个问题:

      1.什么是文件系统?

      2.如何实现文件系统?

      3.常用的文件系统有哪些?为什么需要这些文件系统?

      下面来一一回答这些问题:

      文件系统我们在日常生活中则很少听说,但是它确实存在,只是名字不叫文件系统罢了,一般叫资料库。资料库里面的文件众多,我们如何快速准确的找到我们要的那份文件呢?资料库采用了分类索引的方法来实现快速查找。类似于我们学校图书馆的管理方式,一楼可能是哲学类,二楼是社科类的,三楼是电子类的,四楼是计算机类的…………等等,我们把这种进行了分类索引的资料库叫文件系统。

      对于计算机而言,文件其实就是资料数据,只能存储在物理介质上面,比如:硬盘,但是我们人不可能自己读取物理介质上的文件,或者自己把文件写入物理介质,物理介质上文件的读写只能采用程序来完成,为了方便实现,程序又被分成了物理介质驱动程序、内容存储程序和文件内容存储程序。物理介质驱动程序专门用于从物理介质上存取数据;内容存储程序用于把文件内容和文件属性信息打包;文件内容存储程序用于把用户输入形成文件内容,或者取得文件内容显示出来。

      我们可以把一个文件系统(倒树)分解成多个文件系统(倒树)分别存放到存储介质上,比如:一个存储到光盘里,一个存储到硬盘中,在使用时,我们把光盘里的文件系统的根目录挂到硬盘文件系统的一个目录下面,这样访问这个目录就相当于是访问光盘的根目录了,找到了根目录,我们也就可以访问整个光盘上的文件系统了。

      “在Linux系统中一切皆是文件”这句话是我们学习Linux系统的时候常常听到的一句话。虽然有些夸张,但是它揭示了文件系统对于Linux系统的重要性;实际上文件系统对于所有的操作系统都很重要,因为它们把大部分的硬件设备和软件数据以文件的形式进行管理。Linux系统对设备和数据的管理框架图如下:

      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不行

      不同的文件系统具有不同的制作方法,有的比较复杂,有的比较简单,在此由于篇幅限制,先不做介绍,在后续的文章中会单独介绍文件系统的制作。

    展开全文
  • 系统移植详解过程

    2020-08-25 21:27:41
    系统移植 linux系统移植分为四块 1、环境搭建 2、BootLoader移植 3、linux内核的移植 4、根文件系统移植 系统移植具体的部署方式分为两种: 开发阶段系统的部署 uboot镜像(ubootpak.bin) : 下载到flash/SD linux内核...

    系统移植

    linux系统移植分为四块

    1、环境搭建
    2、BootLoader移植
    3、linux内核的移植
    4、根文件系统移植

    为了移植部署需要完成以下准备工作

    一、安装tftp服务

    介绍:tftp服务是基于TCP/IP简单文本传输协议

    作用:在系统移植中用于通过网络的方式下载文件到开发板

    具体操作步骤如下(前提Ubuntu必须可以上网)

    1、安装命令

    sudo apt-get install tftpd-hpa tftp-hpa
    

    2、在家目录下创建tftpboot文件夹

    cd ~ 
    mkdir tftpboot 
    chmod 777 tftpboot 
    

    注,tftpboot文件夹的作用:
    将需要下载的文件,放到tftpboot目录下,
    当使用tftp命令下载时,tftp命令会自动的到ubuntu的
    tftpboot目录下找对应的要下载的文件。

    3、修改tftp的环境变量

    打开tftp的配置文件:

    sudo vi /etc/default/tftpd-hpa 
    

    修改配置文件中的内容,修改为以下:

    # /etc/default/tftpd-hpa
    TFTP_USERNAME="tftp"	# tftp服务
    TFTP_DIRECTORY="/var/lib/tftpboot"	# 指定自己的tftpboot的路径
    TFTP_ADDRESS=":69"	# 端口号默认为69
    TFTP_OPTIONS="--secure"	# 权限相关参数
    

    4、重启tftp服务

    sudo service tftpd-hpa restart 
    

    二、安装nfs服务

    作用:开发板通过nfs服务远程从ubuntu系统中挂载文件系统

    1、安装nfs服务

    sudo apt-get install nfs-kernel-server
    

    2、配置nfs服务的环境变量

    打开 sudo vi /etc/exports

    在最后一行添加一下内容:

    /home/hqyj/nfs/rootfs/  *(rw,sync,no_subtree_check,no_root_squash) 
    

    内容解释:

    /home/hqyj/nfs/rootfs/:网络文件系统所在的路径
    *:所有的用户
    rw:可读可写权限
    sync:同步
    no_subtree_check:不检查子目录的权限
    no_root_squash:非root用户具有相同的权限

    3、创建根文件系统的路径

    在家目录下创建nfs文件夹,并修改权限最大

    cd ~
    mkdir nfs 
    chmod 777 nfs 
    

    4、如果有根文件系统的压缩包放在nfs目录下并进行解压(如果没有后面会讲到制作)

    三、编译uboot(BootLoader)

    1、获取u-boot源码
    2、u-boot版本的选择
    3、移植的准备工作
    4、移植u-boot源码
    5、分析make <board_name>_config命令执行过程详解
    6、制作windows下的win_ubootpak.bin
    7、将制作好的ubootpak.bin烧写到EMMC中,进行测试

    详细编译uboot过程可以参照这篇文章

    四、编译内核uImage

    1、内核的配置和编译
    2、自己添加图形化界面菜单选项
    3、弄懂Makefile .config Kconfig 三个文件之间的关系
    4、了解内核启动过程中主要干了那些事?
    5、模块化加载驱动的命令
    insmod rmmod lsmod mknod

    详细编译内核过程可以参照这篇文章

    五、编译根文件系统

    1、移植根文件系统的工具 —> busybox
    2、如何获取busybox
    3、根文件系统中目录的介绍
    4、使用busybox工具制作根文件系统
    5、完善根文件系统
    6、生成ramdisk.img的镜像文件

    详细编译根文件系统过程可以参照这篇文章

    至此就完成了uboot镜像,内核(uImage)镜像和根文件系统(ramdisk.img)镜像的制作
    拿到这三个镜像文件之后就可以进行系统移植了

    六、系统移植具体的部署方式分为两种:

    开发阶段系统的部署
    uboot镜像(ubootpak.bin) : 下载到flash/SD
    linux内核镜像(uImage) : 通过tftp下载
    根文件系统(rootfs) : 通过nfs挂载

    特点:下载调试方便,适合做开发

    产品阶段系统的部署
    uboot镜像(ubootpak.bin) : 下载到flash/SD
    linux内核镜像(uImage) : 下载到flash/SD
    根文件系统镜像(ramdisk.img) : 下载到flash/SD

    特点:独立性强,不用依赖于Ubuntu,适合产品阶段

    展开全文
  • 系统移植的四大步骤 详解

    千次阅读 2017-05-23 21:28:50
    最近在学习系统移植的相关知识,在学习和调试过程中,发现了很多问题,也解决了很多问题,但总是对于我们的开发结果有一种莫名其妙的感觉,纠其原因,主要对于我们的开发环境没有一个深刻的认识,有时候几个简单的...
    最近在学习系统移植的相关知识,在学习和调试过程中,发现了很多问题,也解决了很多问题,但总是对于我们的开发结果有一种莫名其妙的感觉,纠其原因,主要对于我们的开发环境没有一个深刻的认识,有时候几个简单的命令就可以完成非常复杂的功能,可是我们有没有想过,为什么会有这样的效果?如果没有去追问,只是机械地完成,并且看到实验效果,这样做其实并没有真正的掌握系统移植的本质。
    在做每一个步骤的时候,首先问问自己,为什么要这样做,然后再问问自己正在做什么?搞明白这几个问题,我觉得就差不多了,以后不管更换什么平台,什么芯片,什么开发环境,你都不会迷糊,很快就会上手。对于嵌入式的学习方法,我个人方法就是:从宏观上把握(解决为什么的问题),微观上研究(解决正在做什么的问题),下面以自己学习的arm-cortex_a8开发板为目标,介绍下自己的学习方法和经验。
    嵌入式Linux系统移植主要由四大部分组成:
    一、搭建交叉开发环境
    二、bootloader的选择和移植
    三、kernel的配置、编译、和移植
    四、根文件系统的制作

    第一部分:搭建交叉开发环境
    先介绍第一分部的内容:搭建交叉开发环境,首先必须得思考两个问题,什么是交叉环境? 为什么需要搭建交叉环境?
    先回答第一个问题,在嵌入式开发中,交叉开发是很重要的一个概念,开发的第一个环节就是搭建环境,第一步不能完成,后面的步骤从无谈起,这里所说的交叉开发环境主要指的是:在开发主机上(通常是我的pc机)开发出能够在目标机(通常是我们的开发板)上运行的程序。嵌入式比较特殊的是不能在目标机上开发程序(狭义上来说),因为对于一个原始的开发板,在没有任何程序的情况下它根本都跑不起来,为了让它能够跑起来,我们还必须要借助pc机进行烧录程序等相关工作,开发板才能跑起来,这里的pc机就是我们说的开发主机,想想如果没有开发主机,我们的目标机基本上就是无法开发,这也就是电子行业的一句名言:搞电子,说白了,就是玩电脑!
    然后回答第二个问题,为什么需要交叉开发环境?主要原因有以下几点:
    原因1:嵌入式系统的硬件资源有很多限制,比如cpu主频相对较低,内存容量较小等,想想让几百MHZ主频的MCU去编译一个Linux kernel会让我们等的不耐烦,相对来说,pc机的速度更快,硬件资源更加丰富,因此利用pc机进行开发会提高开发效率。
    原因2:嵌入式系统MCU体系结构和指令集不同,因此需要安装交叉编译工具进行编译,这样编译的目标程序才能够在相应的平台上比如:ARM、MIPS、               POWEPC上正常运行。
    交叉开发环境的硬件组成主要由以下几大部分
    1.开发主机
    2.目标机(开发板)
    3.二者的链接介质,常用的主要有3中方式:(1)串口线 (2)USB线 (3)网线
    对应的硬件介质,还必须要有相应的软件“介质”支持:
    1.对于串口,通常用的有串口调试助手,putty工具等,工具很多,功能都差不多,会用一两款就可以;
    2.对于USB线,当然必须要有USB的驱动才可以,一般芯片公司会提供,比如对于三星的芯片,USB下载主要由DNW软件来完成;
    3.对于网线,则必须要有网络协议支持才可以,常用的服务主要两个
    第一:tftp服务:
          
    主要用于实现文件的下载,比如开发调试的过程中,主要用tftp把要测试的bootloader、kernel和文件系统直接下载到内存中运行,而不需要预先烧录到Flash芯片中,一方面,在测试的过程中,往往需要频繁的下载,如果每次把这些要测试的文件都烧录到Flash中然后再运行也可以,但是缺点是:过程比较麻烦,而且Flash的擦写次数是由限的;另外一方面:测试的目的就是把这些目标文件加载到内存中直接运行就可以了,而tftp就刚好能够实现这样的功能,因此,更没有必要把这些文件都烧录到Flash中去
    第二:nfs服务:
          
    主要用于实现网络文件的挂载,实际上是实现网络文件的共享,在开发的过程中,通常在系统移植的最后一步会制作文件系统,那么这是可以把制作好的文件系统放置在我们开发主机PC的相应位置,开发板通过nfs服务进行挂载,从而测试我们制作的文件系统是否正确,在整个过程中并不需
    要把文件系统烧录到Flash中去,而且挂载是自动进行挂载的,bootload启动后,kernel运行起来后会根据我们设置的启动参数进行自动挂载,因此,对于开发测试来讲,这种方式非常的方便,能够提高开发效率。
          另外,还有一个名字叫samba的服务也比较重要,主要用于文件的共享,这里说的共享和nfs的文件共享不是同一个概念,nfs的共享是实现网络文件的共享,而samba实现的是开发主机上Windows主机和Linux虚拟机之间的文件共享,是一种跨平台的文件共享,方便的实现文件的传输。
    以上这几种开发的工具在嵌入式开发中是必备的工具,对于嵌入式开发的效率提高做出了伟大的贡献,因此,要对这几个工具熟练使用,这样你的开发效率会提高很多。等测试完成以后,就会把相应的目标文件烧录到Flash中去,也就是等发布产品的时候才做的事情,因此对于开发人员来说,所有的工作永远是测试。
          通过前面的工作,我们已经准备好了交叉开发环境的硬件部分和一部分软件,最后还缺少交叉编译器,读者可能会有疑问,为什么要用交叉编译器?前面已经讲过,交叉开发环境必然会用到交叉编译工具,通俗地讲就是在一种平台上编译出能运行在体系结构不同的另一种平台上的程序,开发主机PC平台(X86 CPU)上编译出能运行在以ARM为内核的CPU平台上的程序,编译得到的程序在X86 CPU平台上是不能运行的,必须放到ARM CPU平台上才能运行,虽然两个平台用的都是Linux系统。相对于交叉编译,平常做的编译叫本地编译,也就是在当前平台编译,编译得到的程序也是在本地执行。用来编译这种跨平台程序的编译器就叫交叉编译器,相对来说,用来做本地编译的工具就叫本地编译器。所以要生成在目标机上运行的程序,必须要用交叉编译工具链来完成。
          这里又有一个问题,不就是一个交叉编译工具吗?为什么又叫交叉工具链呢?原因很简单,程序不能光编译一下就可以运行,还得进行汇编和链接等过程,同时还需要进行调试,对于一个很大工程,还需要进行工程管理等等,所以,这里 说的交叉编译工具是一个由编译器、连接器和解释器组成的综合开发环境,交叉编译工具链主要由binutils(主要包括汇编程序as和链接程序ld)、gcc(为GNU系统提供C编译器)和glibc(一些基本的C函数和其他函数的定义) 3个部分组成。有时为了减小libc库的大小,也可以用别的 c 库来代替 glibc,例如 uClibc、dietlibc 和 newlib。
          那么,如何得到一个交叉工具链呢?是从网上下载一个“程序”然后安装就可以使用了吗?回答这个问题之前先思考这样一个问题,我们的交叉工具链顾名思义就是在PC机上编译出能够在我们目标开发平台比如ARM上运行的程序,这里就又有一个问题了,我们的ARM处理器型号非常多,难道有专门针对我们某一款的交叉工具链吗?若果有的话,可以想一想,这么多处理器平台,每个平台专门定制一个交叉工具链放在网络上,然后供大家去下载,想想可能需要找很久才能找到适合你的编译器,显然这种做法不太合理,且浪费资源!因此,要得到一个交叉工具链,就像我们移植一个Linux内核一样,我们只关心我们需要的东西,编译我们需要的东西在我们的平台上运行,不需要的东西我们不选择不编译,所以,交叉工具链的制作方法和系统移植有着很多相似的地方,也就是说,交叉开发工具是一个支持很多平台的工具集的集合(类似于Linux源码),然后我们只需从这些工具集中找出跟我们平台相关的工具就行了,那么如何才能找到跟我们的平台相关的工具,这就是涉及到一个如何制作交叉工具链的问题了。
    通常构建交叉工具链有如下三种方法:
    方法一 :分步编译和安装交叉编译工具链所需要的库和源代码,最终生成交叉编译工具链。该方法相对比较困难,适合想深入学习构建交叉工具链的读者。如果只是想使用交叉工具链,建议使用下列的方法二构建交叉工具链。
    方法二: 通过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. 测试交叉工具链
         到此,嵌入式Linux系统移植四大部分的第一部分工作全部完成,接下来可以进行后续的开发了。

    第二部分:bootloader的选择和移植
    一、Boot Loader 概念
           就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境,他就是所谓的引导加载程序(Boot Loader)。

    【图1Flash存储中存放文件的分布图
     二、为什么系统移植之前要先移植BootLoader?
          BootLoader的任务是引导操作系统,所谓引导操作系统,就是启动内核,让内核运行就是把内核加载到内存RAM中去运行,那先问两个问题:第一个问题,是谁把内核搬到内存中去运行?第二个问题:我们说的内存是SDRAM,大家都知道,这种内存和SRAM不同,最大的不同就是SRAM只要系统上电就可以运行,而SDRAM需要软件进行初始化才能运行,那么在把内核搬运到内存运行之前必须要先初始化内存吧,那么内存是由谁来初始化的呢?其实这两件事情都是由bootloader来干的,目的是为内核的运行准备好软硬件环境,没有bootloadr我们的系统当然不能跑起来。

    三、bootloader的分类。
          首先更正一个错误的说法,很多人说bootloader就是U-boot,这种说法是错误的,确切来说是u-boot是bootloader的一种。也就是说bootloader具有很多种类,大概的分类如下图所示:
    【图2bootloader分类图
          由上图可以看出,不同的bootloader具有不同的使用范围,其中最令人瞩目的就是有一个叫U-Boot的bootloader,是一个通用的引导程序,而且同时支持X86、ARM和PowerPC等多种处理器架构。U-Boot,全称 Universal Boot Loader,是遵循GPL条款的开放源码项目,是由德国DENX小组开发的用于多种嵌入式CPU的bootloader程序,对于Linux的开发,德国的u-boot做出了巨大的贡献,而且是开源的。
          u-boot具有以下特点:
          ① 开放源码;
          ② 支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS;
          ③ 支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale;
          ④ 较高的可靠性和稳定性;
          ⑤ 高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等;
          ⑥ 丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等;
          
    ⑦ 较为丰富的开发调试文档与强大的网络技术支持;
          其实,把u-boot可以理解为是一个小型的操作系统。

    四、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通过某些通信手段将内核映像或根文件系统映像等从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的工作循环),接受用户从串口输入的命令,然后进行相应的工作。

    七、基于cortex-a8的s5pc100bootloader启动过程分析
    s5pc100支持两种启动方式,分别为USB启动方式和NandFlash启动方式:
    1. S5PC100 USB启动过程
    [1] A8 reset, 执行iROM中的程序
    [2] iROM中的程序根据S5PC100的配置管脚(SW1开关4,拨到4对面),判断从哪里启动(USB)
    [3] iROM中的程序会初始化USB,然后等待PC机下载程序
    [4] 利用DNW程序,从PC机下载SDRAM的初始化程序到iRAM中运行,初始化SDRAM
    [5] SDRAM初始化完毕,iROM中的程序继续接管A8, 然后等待PC下载程序(BootLoader)
    [6] PC利用DNW下载BootLoader到SDRAM
    [7] 在SDRAM中运行BootLoader

    2. S5PC100 Nandflash启动过程

    [1] A8 reset, 执行IROM中的程序
    [2] iROM中的程序根据S5PC100的配置管脚(SW1开关4,拨到靠4那边),判断从哪里启动(Nandflash)
    [3] iROM中的程序驱动Nandflash
    [4] iROM中的程序会拷贝Nandflash前16k到iRAM
    [5] 前16k的程序(BootLoader前半部分)初始化SDRAM,然后拷贝完整的BootLoader到SDRAM并运行
    [6] BootLoader拷贝内核到SDRAM,并运行它
    [7] 内核运行起来后,挂载rootfs,并且运行系统初始化脚本

    八、u-boot移植(基于cortex_a8s5pc100为例)
    1.建立自己的平台
    (1).下载源码包2010.03版本,比较稳定
    (2).解压后添加我们自己的平台信息,以smdkc100为参考版,移植自己s5pc100的开发板
    (3).修改相应目录的文件名,和相应目录的Makefile,指定交叉工具链。
    (4).编译
    (5).针对我们的平台进行相应的移植,主要包括修改SDRAM的运行地址,从0x20000000
    (6).“开关”相应的宏定义
    (7).添加Nand和网卡的驱动代码
    (8).优化go命令
    (9).重新编译 make distclean(彻底删除中间文件和配置文件) make s5pc100_config(配置我们的开发板)   make(编译出我们的u-boot.bin镜像文件)
    (10).设置环境变量,即启动参数,把编译好的u-boot下载到内存中运行,过程如下:
    1. 配置开发板网络
           ip地址配置:
           $setenv ipaddr 192.168.0.6               配置ip地址到内存的环境变量
           $saveenv                                 保存环境变量的值到nandflash的参数区

           网络测试:
           在开发开发板上ping虚拟机:
           $ ping 192.168.0.157(虚拟机的ip地址)
     
           如果网络测试失败,从下面几个方面检查网络:
           1. 网线连接好
           2. 开发板和虚拟机的ip地址是否配置在同一个网段
           3. 虚拟机网络一定要采用桥接(VM--Setting-->option)
           4. 连接开发板时,虚拟机需要设置成静态ip地址

        2. 在开发板上,配置tftp服务器(虚拟机)的ip地址
           $setenv serverip 192.168.0.157(虚拟机的ip地址)
           $saveenv
        3. 拷贝u-boot.bin到/tftpboot(虚拟机上的目录)
        4. 通过tftp下载u-boot.bin到开发板内存
           $ tftp 20008000(内存地址即可) u-boot.bin(要下载的文件名)

           如果上面的命令无法正常下载:
           1. serverip配置是否正确
           2. tftp服务启动失败,重启tftp服务
              #sudo service tftpd-hpa restart

        5. 烧写u-boot.bin到nandflash的0地址
           $nand erase 0(起始地址)  40000(大小)                    擦出nandflash 0 - 256k的区域
           $nand write 20008000((缓存u-boot.bin的内存地址) 0(nandflash上u-boot的位置) 40000(烧写大小)
        
        6. 切换开发板的启动方式到nandflash
           1. 关闭开发板
           2. 把SW1的开关4拨到4的那边
           3. 启动开发板,它就从nandflash启动


    第三部分: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挂载的文件系统,最后才能看到效果).内核测试和启动过程也是比较复杂的,在后续的文章中会详细介绍。

    第四部分:根文件系统的介绍
        由本文的第一张图:Flash存储中存放文件的分布图可知,文件系统的制作和移植是系统移植的最后一道工序了,在这里首先要提几个问题:
        1.什么是文件系统?
        2.
    如何实现文件系统?
        3.
    常用的文件系统有哪些?为什么需要这些文件系统?
        下面来一一回答这些问题:

    文件系统我们在日常生活中则很少听说,但是它确实存在,只是名字不叫文件系统罢了,一般叫资料库。资料库里面的文件众多,我们如何快速准确的找到我们要的那份文件呢?资料库采用了分类索引的方法来实现快速查找。类似于我们学校图书馆的管理方式,一楼可能是哲学类,二楼是社科类的,三楼是电子类的,四楼是计算机类的…………等等,我们把这种进行了分类索引的资料库叫文件系统。
        对于计算机而言,文件其实就是资料数据,只能存储在物理介质上面,比如:硬盘,但是我们人不可能自己读取物理介质上的文件,或者自己把文件写入物理介质,物理介质上文件的读写只能采用程序来完成,为了方便实现,程序又被分成了物理介质驱动程序、内容存储程序和文件内容存储程序。物理介质驱动程序专门用于从物理介质上存取数据;内容存储程序用于把文件内容和文件属性信息打包;文件内容存储程序用于把用户输入形成文件内容,或者取得文件内容显示出来。

    我们可以把一个文件系统(倒树)分解成多个文件系统(倒树)分别存放到存储介质上,比如:一个存储到光盘里,一个存储到硬盘中,在使用时,我们把光盘里的文件系统的根目录挂到硬盘文件系统的一个目录下面,这样访问这个目录就相当于是访问光盘的根目录了,找到了根目录,我们也就可以访问整个光盘上的文件系统了。

    “在Linux系统中一切皆是文件”这句话是我们学习Linux系统的时候常常听到的一句话。虽然有些夸张,但是它揭示了文件系统对于Linux系统的重要性;实际上文件系统对于所有的操作系统都很重要,因为它们把大部分的硬件设备和软件数据以文件的形式进行管理。Linux系统对设备和数据的管理框架图如下:


    【图3】文件系统实现

    [说明]

    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的文件系统——JffsYaffs、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系统中,内存经常用于存储文件系统,这种叫做RamdiskRamdisk有两种,一种是完全把内存看成物理存储介质,利用内存模拟磁盘,运用磁盘的文件系统类型;另一种只是在内存中存储了文件系统逻辑结构,运用tmpfs & ramfs文件系统类型:
        tmpfs & ramfs
        1. 概述
        用物理内存模拟磁盘分区,挂载这种分区后,就可以跟读写磁盘文件一样读写这里面的文件,但是操作速度要比磁盘文件快得多;所以一般应用在下面几个方面:
        1)读写速度要求快的文件应该放在这种文件系统中
        2)磁盘分区为flash的情况下,把需要经常读写的文件放在这种文件系统中,然后定期写回flash
        3)系统中的临时文件,如/tmp、/var目录下的文件应该放在这种文件系统中
        4)/dev设备文件(因为设备文件随驱动和设备的加载和卸载而变化),应该放在这种文件系统中
        2. 特点 
        1)由于数据都存放在物理内存中,所以系统重启后,这个文件系统中的数据会全部丢失
        2)ramfs在没有指定最大的大小值情况下,会自动增长,直到用掉系统中所有的物理内存为止,这时会导致系统的崩溃,建议挂载时最好限定其最大的大小值
        3)tmpfs如果指定了大小值,自动增长至大小值后,系统会限定它的大小;这个文件系统占用的物理内存页可以背置换到swap分区,但是ramfs不行


        不同的文件系统具有不同的制作方法,有的比较复杂,有的比较简单,在此由于篇幅限制,先不做介绍,在后续的文章中会单独介绍文件系统的制作。
    展开全文
  • 操作系统移植(一)--启动分析

    千次阅读 2017-12-25 19:31:35
    前言从本节开始,开始讲述一些关于OS移植的内容;与Linux移植不同,本次讲的是嵌入式实时操作系统移植,类似于ucos-ii这种,所以后面在说到任务抢占时,可能和熟知的Linux有所区别。
  • 系统移植

    2020-08-14 11:06:20
    实验一交叉工具链的安装 【实验目的】 了解交叉工具链的编译过程及其使用。 说明:在实验中命令行提示符为“$”表示在主机上运行,“#”表示在目标板上运行 【实验环境】 ...1、 ubuntu 12.04 发行版 ...
  • Android系统移植(一)

    千次阅读 2016-01-27 13:27:16
    摘要:移植Android4.2.2到TINY4412上
  • 嵌入式Linux系统移植的四大步骤

    千次阅读 多人点赞 2018-08-25 20:34:28
    最近在学习系统移植的相关知识,在学习和调试过程中,发现了很多问题,也解决了很多问题,但总是对于我们的开发结果有一种莫名其妙的感觉,纠其原因,主要对于我们的开发环境没有一个深刻的认识,有时候几个简单的...
  • 嵌入式系统移植步骤详解

    千次阅读 2016-12-28 18:22:37
     下面我们就来看下一个内容叫做移植的基本步骤,也就是说我们要现有一个大体的思路,如果说我作为产品开发者,或者说是作为一个系统的整体架构来说,我们拿到一款板子过后我们是如何一步一步把我们的系统用起来呢?...
  • 嵌入式Linux系统移植的四大步骤

    万次阅读 多人点赞 2017-06-05 10:30:25
    最近在学习系统移植的相关知识,在学习和调试过程中,发现了很多问题,也解决了很多问题,但总是对于我们的开发结果有一种莫名其妙的感觉,纠其原因,主要对于我们的开发环境没有一个深刻的认识,有时候几个简单的...
  • 系统移植的四大步骤

    千次阅读 2016-11-14 10:06:37
    最近在学习系统移植的相关知识,在学习和调试过程中,发现了很多问题,也解决了很多问题,但总是对于我们的开发结果有一种莫名其妙的感觉,纠其原因,主要对于我们的开发环境没有一个深刻的认识,有时候几个简单的...
  • 嵌入式Linux系统移植

    千次阅读 2019-02-13 15:49:19
    嵌入式Linux系统移植主要由四大部分组成:一、搭建交叉开发环境二、bootloader的选择和移植三、kernel的配置、编译、和移植四、根文件系统的制作第一部分:搭建交叉开发环境 先介绍第一分部的内容:搭建交叉开发...
  • Android系统移植:驱动篇

    万次阅读 多人点赞 2017-05-24 15:05:23
    【导语】在Android系统移植中,有很重要的一个部分工作,就是为新平台上的硬件设备移植驱动程序。因为Android系统是基于Linux kernel内核构建,所以这里说的移植驱动程序,其实就是基于Android系统平台开发适应移动...
  • 嵌入式Linux系统移植的四大步骤

    千次阅读 多人点赞 2018-02-06 08:32:13
    最近在学习系统移植的相关知识,在学习和调试过程中,发现了很多问题,也解决了很多问题,但总是对于我们的开发结果有一种莫名其妙的感觉,纠其原因,主要对于我们的开发环境没有一个深刻的认识,有时候几个简单的...
  • 从零开始学习UCOSII操作系统13--系统移植理论篇 1、什么是系统移植? (1)UCOSII移植到不同的处理器上,所谓的移植就是将一个实时的内核能在其他的微处理器或者微控制器上运行。 为了方便移植,UCOSII的大部分...
  • LwIP在stm32上的无操作系统移植

    万次阅读 2015-11-12 23:30:19
    这里的移植是无操作系统移植。 LwIP虽然是一个轻型的IP协议,但是TCP/IP基本功能都有。而且占用的资源不多,非常适合用于嵌入式系统。 移植的平台:STM32F103VE+MDK 4.7+ LWIP-1.4.1 下载LwIP-1.4.1源码以及...
  • elm FatFs文件系统移植总结

    千次阅读 2017-01-13 20:10:02
    本文将根据我的一些理解,针对elm FatFs文件系统做一个初步总结。
  • FreeRTOS系统移植教程

    2018-12-23 17:40:57
     本文主要讲解如何将FreeRTOS实时操作系统移植到STM32单片机中,在本文之前已经基于MDK集成开发环境移植了stm32标准库并新建工程模板,如果您阅读本文之前没有该工程或者相关stm32工程,您可以参考...
  • 找一个本机型的底包system,然后
  • 基于ARM9的嵌入式Linux系统移植

    千次阅读 2013-11-22 15:03:30
    这篇文章是我6个月前在老师的指导下写的论文,留在博客里,做个备份,那时我的系统环境是window 7虚拟机下的RedHat Enterprise Linux6.0 ,现在我的系统环境是Ubuntu13.04。讲的比较粗糙,有不正确的地方希望大家指出...

空空如也

1 2 3 4 5 ... 20
收藏数 344,286
精华内容 137,714
关键字:

系统移植