day01

2019-07-12 18:19发布

一.嵌入式linux系统部署
面试题:谈谈对嵌入式linux系统的认识
场景:给一块开发板,给一台电脑,给一周的时间
      在开发板上跑linux系统
实施步骤:
1.上位机搭建嵌入式linux开发环境
  安装纯linux系统(也可以使用虚拟机)
  安装必要的软件
          vim/vimplus/kermit/minicom/tftpd-hpa/nfs-kernel-server
          ctags/cscope/wine+sourceinsight
  安装添加部署交叉编译器
      问:交叉编译器从何而来?
      答:交叉编译器的获取途径有四种
      1.从芯片厂家或者开发板(参考板)的厂家直接获取
      2.从ARM相关交叉编译器的网站下载  
      3.利用自动化脚本工具crosstools-ng自己制作
      4.自己下载gcc,linux内核源码,各种工具源码
        自己编译制作
      总结:切记:交叉编译器一定要从芯片厂家或者开发板的
            厂家获取
    5.有了交叉编译器,只需在上位机的linux系统中的
      PATH环境变量中添加即可!
 
2.掌控下位机的硬件信息
  粗看
       首先看三大件
               CPU:处理器型号,哪家公司的处理器
               内存:容量和内存的起始地址
               闪存: 容量和类型
                     闪存类型:
                     Norflash:容量和起始地址
                     Nandflash:容量和Nand控制器
                     emmc:容量和SDIO控制器   
               切记:容量,型号,起始地址都可以咨询
                     硬件工程师
        然后看外围接口
            哪些接口需要,哪些接口不需要,依赖用户将来
            对产品的需求,举例子:手机和WIFI路由器对LCD
            显示屏的需求
            但是在产品研发阶段,UART接口和网口必须有
            前者用于调试,后者用于软件下载,加快软件的
            调试进度                    
   
  细看:法宝:原理图和芯片手册
        要具体硬件外设具体分析
        例如:现在要研究mma8653三轴加速度传感器
        那就要首先看这个芯片的原理图,本质目的明确
        此芯片和CPU之间采用哪种硬件通信方式,当然
        mma8653采用I2C总线方式,并且画出一个简要的
        硬件连接示意图(CPU核+I2C控制器+mma8653),看图交代
        访问的操作流程;
        然后再看两个手册:
        mma8653本身的芯片手册,而手册中重点关注两点:
        1.mma8653相关的读写操作时序图  
        2.mma8653相关的片内寄存器的特性
         片内寄存器地址
         片内寄存器的bit含义
         片内寄存器的访问不能以地址指针的形式访问
         必须严格按照时序图操作
        
       紧接着最后看处理器手册中I2C控制器相关的说明:
       1.了解I2C控制器的基本操作原理
       2.掌握I2C控制器内部的特殊功能寄存器
         寄存器的地址
         寄存器的bit位含义
         以地址指针的形式访问
        
       如果有必要,可以顺带大谈特谈I2C总线!
 
3.硬件信息掌控完毕,然后向下位机部署添加软件
  3.1.首先要明确下位机部署的软件是裸板程序
      还是基于操作系统的软件
      如果是裸板程序,谈谈裸板程序的开发流程
      此时此刻需要向下位机跑操作系统,linux系统
       
  3.2.然后明确下位机运行linux系统所包含的必要
      至少三部分软件和三个软件各自特点:
      bootloader:boot(启动)+loader(加载)
                  仅仅是一个统称
                  uboot属于bootloader的一种
                  本质就是一个裸板程序
                  类似PC机的BIOS
                  上电CPU首先运行此程序
                  bootloader初始化硬件完毕以后,会从闪存上加载(拷贝)
                  linux内核到内存,并且从内存中启动linux内核
                  启动linux内核之前,还要给linux内核传递
                  启动参数,告诉linux内核将来根文件系统rootfs
                  在哪里,linux内核参数到某个地方去找根文件系统
                  rootfs
                  切记:一旦linux内核启动,bootloader的
                  生命周期到此结束
                  
                  做的硬件初始化工作如下:
                      初始化CPU:关闭cache功能
                      初始化内存
                      关闭中断
                      关闭看门狗
                      初始化时钟:800MHz
                      初始化闪存
                      初始化UART
                      初始化网卡
                      LCD?
                            注意:有些硬件必须初始化(例如内存,闪存)
                            但有些硬件的初始化要根据用户需求来定
                            (例如用户需要在bootloader运行时,在
                            LCD显示屏上显示LOGO,此时bootloader必须
                            做LCD显示屏的初始化,如果没有此需求,
                            一律不初始化,只要做初始化了,势必影响
                            系统的启动时间)
                      
      linux内核:操作系统内核
                          由bootloader启动
                          永远运行内存中
                          掉电它的生命周期才结束
                          包含7大功能:
                              内存管理子系统
                                      负责内存的映射,分配,销毁等
                              进程管理子系统
                                      负责进程的创建,调度,销毁等
                              文件系统
                                      NTFS/FAT32/EXT4/UBIFS/CRAMFS/YAFFS2等
                              系统调用
                                      open/close/read/write/fork/exit等
                              设备驱动
                                      各种硬件的驱动程序
                              网络协议栈
                                      TCP/IP协议栈
                              平台相关代码
                                      linux内核既可以运行在X86架构
                                      也可以运行在ARM架构,也可以运行
                                      POWERPC/MIPS/DSP/FPGA等
                                      说明linux内核势必有相关的架构
                                      支持的软件代码,此类代码检查
                                      平台相关代码
       
      根文件系统rootfs:
                              仅仅是一个代名词而已,什么都不是
                              它里面包含的内容就是linux系统中
                              的"/"下的所有东西,也就是:
                              cd /
                              ls //查看根目录下的内容
                                bin sbin lib etc dev sys proc ...
                              这些内容合在一起形成了根文件系统rootfs
      
     3.3.嵌入式linux系统软件启动流程
     上电->CPU首先运行emmc上的uboot->uboot启动以后
     做大量的硬件初始化工作,然后从EMMC上读取linux内核
     到内存,然后给linux内核传递启动参数,此参数就是
     告诉linux内核将来内核找的根文件系统rootfs在哪里
     ->linux内核正式投入运行(uboot生命结束)->linux
     内核也做大量的硬件和软件的初始化工作,之后根据
     uboot传递的参数到某个地方去找根文件系统rootfs
     ->linux内核一旦找到根文件系统rootfs,linux内核
     就会把控制权交给根文件系统rootfs->此时linux内核
     会运行根文件系统中/sbin/init第一号进程,第一号
     进程一旦运行,会创建一个子进程来执行根文件系统
     /bin/sh,至此启动了一个shell程序,用户即可通过
     shell程序来输入各种命令和linux系统交互
      
4.实战演练:向X6818部署(烧写)嵌入式linux系统
  实施步骤:
  1.先获取部署的软件
    ftp://PORTING/system.rar
    normal_system/
                uboot.bin:uboot二进制镜像文件
                uImage:linux内核的二进制镜像文件
                rootfs_ext4.img:根文件系统rootfs的二进制镜像文件
    
  2.明确:嵌入式开发模式                                            
    上位机下位机通过网线和tftp服务进行下载烧写
    将三部分软件放到上位机的tftp的下载目录
    cp uImage /tftpboot
    cp rootfs_ext4.img /tftpboot
     
  3.烧写三部软件到emmc上,首先进行分区规划
    EMMC的分区规划:
    0----512-------1M-------65M--------819M--------剩余
     预留    uboot    uImage    rootfs      大片       //自定义名称
   隐藏分区 mmcblk0p0 mmcblk0p1 mmcblk0p2  mmcblk0p3   //官方名称
   
  4.正式的下载烧写
    明确:emmc一个sector(块)为512字节
    1.烧写uImage
      重启下位机,进入uboot命令行模式,执行
      tftp  48000000 uImage
      注意:下载完毕以后,最后会提示uImage下载的文件大小
       
      mmc write 48000000 0x800 0x3000
      说明:
      mmc write:向emmc写入数据
      48000000:内存的起始地址,按字节为单位
      0x800:要写的emmc的起始地址,按sector为单位
            0x800=0x100000/512
      0x3000:要向emmc写入的数据大小,按sector为单位
            0x3000=uImage文件大小/512
     
    2.烧写rootfs_ext4.img
      重启下位机,进入uboot命令行模式,执行:
      tftp 480000000 rootfs_ext4.img
      注意观察rootfs_ext4.img的文件大小
      mmc write 48000000 0x20800  0x32000
      说明:
      0x20800=65M/512
      0x32000=rootfs_ext4.img文件大小/512
       
      至此:uboot/uImage/rootfs_ext4.img烧写完毕
    
  5.烧写完毕,一定要记得设置系统的启动参数
    重启下位机,进入uboot的命令行模式,执行:
    setenv bootcmd mmc read 0x48000000 0x800 0x3000 ; bootm 48000000
    saveenv
    说明:
    bootcmd是uboot的一个非常重要的环境变量,此环境变量的
    作用是用于uboot从某个地方加载linux内核到内存并且
    启动内核,上电,uboot运行以后,如果不按任何键,倒计时321
    以后,uboot会自动执行bootcmd的命令
     
    所以bootcmd的参数为:mmc read 0x48000000 0x800 0x3000 ; bootm 48000000
    mmc read 0x48000000 0x800 0x3000:
    此命令是从emmc的0x800地址开始,读0x3000这么多的
    数据到内存的0x48000000,注意:0x800和0x3000都是按sector为单位
      
    bootm 0x48000000:从内存0x48000000启动linux内核
      
    重启下位机,进入uboot命令行模式,还要执行:
    setenv bootargs root=/dev/mmcblk0p2 init=/linuxrc  
      console=ttySAC0,115200 rootfstype=ext4 maxcpus=1 lcd=vs070cxn(或者lcd=wy070ml) tp=gslx680-linux    
    saveenv
    说明:
    bootargs同样属于uboot的非常非常重要的一个环境变量
    此变量就是给linux内核传递的启动参数,将来内核根据
    这个启动参数去到某个地方找根文件系统rootfs
    root=/dev/mmcblk0p2:告诉linux内核根文件系统在emmc的第三分区
    init=/linuxrc:启动运行的第一个脚本linuxrc
                  linuxrc会启动/sbin/init第一号进程
    console=ttySAC0,115200:告诉linux内核,调试串口用第一个串口
                                                 注意一定要符合硬件的调试串口编号
    rootfstype=ext4:文件系统格式为ext4
    maxcpus=1:只启动CPU0
    lcd=vs070cxn:指定LCD显示屏的型号
    或者
    lcd=wy070ml:指定新板子的LCD显示屏型号
    tp=gslx680-linux:指定触摸屏的型号
   
  6.测试下位机的linux系统是否正常启动
    重启下位机,此时看到uboot的倒计时321时,别按键盘
    时间一到,uboot会自动运行bootcmd指定的命令
    支持linux系统正式运行,串口终端上可以看到linux内核
    启动时候的打印信息,启动到最后,会提示输入:
    用户名:root
    密码:123456  
    进入下位机的linux的shell终端,执行命令:
    cd /
    ls
    观察LCD显示屏是否显示一个QT界面,可以尝试玩玩QT界面
    ps //查看当前进程
     ...
     /usr/local/qttest //QT进程
  kill QT进程的PID
  再去操作触摸屏,QT程序还有反应吗?
  手动再次启动QT程序:
  /usr/local/qttest //启动以后,再去操作LCD显示屏
 
  出现的问题:下位机linux系统无限重启问题或者QT界面没有
  问题原因:linux内核启动到最后找根文件系统rootfs失败
            失败原因的两点:
            1.要不rootfs_ext4.img没有烧写
              只需重新烧写
            2.要不分区无效
                          只需重新分区
  解决办法:
  重启进入uboot命令行,执行:
  fdisk 2 2 0x100000:0x4000000 0x4100000:0x2f200000
  然后重新烧写rootfs_ext4.img
  fdisk:uboot的分区工具命令
  第一2:表示对EMMC进行重新分区,如果是SD0写0,如果是SD1写1
  第二2:表示要分2两个分区,注意uboot分区不算
         这两个分区分别表示uImage和rootfs所在的分区
  0x100000:uImage所在分区的起始地址,为1M开始
  0x4000000:uImage所在分区的大小,为64M(65M-1M)
   
  0x4100000:rootfs所在分区的起始地址,65M
  0x2f200000:rootfs所在分区的大小,754M(819M-65M)
   
  问:如何将uboot的环境变量清空呢?
  答:只需将环境变量设置为空即可,例如:
      setenv bootargs  
      saveenv
      print //查看bootargs是否还有信息
       
 
5.案例:
  问:如何在现在的下位机的linux系统运行一个
      自己的应用程序?
  问:如何更新qttest应用程序呢,前提是在不进行
      对emmc频繁的烧写?
  答:只需利用大名鼎鼎的NFS网络服务即可
      NFS网络服务就是在上位机启动,并且指定一个
      共享目录,将来下位机可以从这个目录中获取
      到要运行的文件
  实施步骤:
  1.上位机安装NFS网络服务
    sudo apt-get install nfs-kernel-server
  2.上位机配置NFS网络服务,添加共享目录
    sudo vim /etc/exports 文件最后添加:
    /opt/rootfs *(rw,sync,no_root_squash)
    保存退出
    说明:
    /opt/rootfs:指定一个共享目录
    *:任何一个IP客户端都可以来访问
    rw:客户端可以对共享目录中的文件进行读和写
    sync:客户端如果对文件进行修改了,要同步更新到服务器上
    no_root_squash:普通用户也可以访问
    注意:可以指定多个共享目录
  3.上位机创建共享目录,添加一个应用程序
    mkdir /opt/rootfs   
    cd /opt/rootfs/
    vim pthread.c
    arm-cortex_a9-linux-gnueabi-gcc -o pthread pthread.c -lpthread
  4.上位机重启NFS网络服务
    sudo service nfs-kernel-server restart
  5.下位机的linux系统只需访问上位机的NFS网络服务
    指定的共享目录/opt/rootfs即可:
    首先确保下位机的linux系统已经启动完毕
    只需在下位机执行一下命令即可:
    ifconfig //发现网卡eth0压根就没有IP地址
    ifconfig eth0 192.168.1.110
    ping 192.168.1.8 //下位机ping上位机,测试
    mount -t nfs -o nolock 192.168.1.8:/opt/rootfs /mnt
    说明:
    mount:挂接命令,让下位机去找上位机的NFS共享目录
    -t nfs:采用NFS网络服务
    -o nolock:对共享目录里的文件访问无需锁定
    192.168.1.8:/opt/rootfs:指定下位机将来要找的上位机和上位机指定的共享目录
    /mnt:属于下位机linux系统的一个目录,作为挂接目录
    结果是将上位机的/opt/rootfs共享目录挂接到下位机的
    /mnt目录,将来下位机只需访问/mnt目录本质就是在
    访问上位机的/opt/rootfs目录
     
    cd /mnt //本质就是进入上位机的/opt/rootfs
    ls
        helloworld.c hellworold pthread pthread.c
    ./helloworld //下位机运行
    ./pthread  //下位机运行
    注意:pthread程序在运行之前,下位机的linux系统
          会自动利用网络从共享目录下载到下位机的
          内存上运行(这个过程是偷偷摸摸的)
    
   6.总结:
     利用NFS网络服务将来可以大大提高软件调试测试的
     效率,关键是将来更新软件测试软件无需频繁烧写
     EMMC,大大提高了EMMC的使用寿命!
    
   问:uboot如何擦除EMMC分区的数据呢?
   答:用mmc erase命令
   例如:在uboot命令行执行:
         mmc erase 0x800 0x3000 //把uImage所在的分区擦除
 
6.案例:目前内核启动根据bootargs=/dev/mmcblk0p2到
        EMMC的第三分区找根文件系统rootfs,现在能否
        让内核启动的时候到上位机去找根文件系统呢?
        答:是可以,同样需要利用NFS网络服务
  实施步骤:
  1.首先在上位机添加一个根文件系统rootfs
    ftp://PORTING/system.rar/
    nfs_rootfs/rootfs_qt.tar.bz2(制作好的rootfs)
    cp rootfs_qt.tar.bz2 /opt/
    rm /opt/rootfs -fr //删除上一个实验创建的目录
    tar -xvf rootfs_qt.tar.bz2 //得到rootfs目录
    cd /opt/rootfs //进入根文件系统rootfs,并且还是
                     NFS网络服务的共享目录
    ls //查看根文件系统的内容
      bin sbin lib etc ...
     
   2.修改linux内核启动参数,告诉linux内核将来找根文件
     系统rootfs不要再去emmc的第三分区找了,直接到
     某个上位机去找根文件系统rootfs
     重启下位机,进入uboot命令行模式,执行:
     setenv bootargs root=/dev/nfs nfsroot=192.168.1.8:/opt/rootfs  
         ip=192.168.1.110:192.168.1.8:192.168.1.1:255.255.255.0  
         init=/linuxrc console=ttySAC0,115200  
         maxcpus=1 lcd=vs070cxn tp=gslx680-linux
     savenv
     说明:
     root=/dev/nfs:告诉linux内核,根文件系统rootfs在上位机上
     nfsroot=192.168.1.8:/opt/rootfs:所要找的上位机的ip为192.168.1.8
                                     并且这个上位机的/opt/rootfs就是要
                                     找到的根文件系统
     ip=下位机的ip:上位机的ip:网关:掩码
     重启开发板,验证下位机是否能够启动
      
     如果下位机的linux系统完毕,测试:
     上位机编写一个程序:
       cd /opt/rootfs/
       vim helloworld.c
       arm...gcc -o helloworld helloworld.c
     下位机的linux系统测试:
     cd /
     ls
       helloworld helloworld.c
     ./helloworlda
      
     结论:将来软件的编辑编译在上位机
           软件的运行只需在下位机运行即可
           无需向EMMC烧写根文件系统rootfs和其中的
           应用程序,提高了开发的效率和EMMC的使用寿命!