day05

来源:互联网 发布:每天读书 知乎 编辑:程序博客网 时间:2024/05/16 19:27
回顾:
1.linux内核菜单配置
  作用:便于用户向内核添加和去除某个内核程序(例如驱动)
  依赖:Kconfig和Makefile
  Kconfig:产生菜单,生成一个选项:CONFIG_XXX
                  此选项最终给Makefile使用
                  *:CONFIG_XXX=y:在一起
                  M: CONFIG_XXX=m: 分家
                          生成的二进制文件都是以.ko
                          安装内核程序到内核uImage:insmod
                          从内核uImage卸载内核程序:rmmod
                  N: CONFIG_XXX=空:不编译
  Makefile:根据CONFIG_XXX条件编译内核程序
                  obj-$(CONFIG_XXX) += xxx.o
 
2.嵌入式linux系统软件之根文件系统rootfs
  2.1.根文件系统rootfs特点
      仅仅代名词
      包含内容:cd /; ls看到的所有内容组成根文件系统rootfs
   
  2.2.根文件系统rootfs包含的必要目录和可选目录
      必要目录:
              bin/sbin/usr/dev/proc/sys/lib/etc
      可选目录:
              home/mnt/var/tmp/opt等
   
  2.3.利用busybox制作根文件系统
  1.大谈特谈busybox特点
    著名的开源软件
    "瑞士军刀"
    仅仅提供各种命令:ls,cd,insmod,rmmod等
    www.busybox.net
    跟硬件信息没有关系,只要CPU一样,即使硬件外设
    不一样,利用busybox制作的根文件系统无需修改
     
  2.busybox源码操作
    获取源码
    修改源码的Makefile:
        ARCH=arm
        CROSS_COMPILE=arm-cortex_a9-linux-gnueabi-
    配置源码:make menuconfig
        就是用来添加(*)和删除(N)某个命令
            仅仅去除insmod/rmmod等命令的精简版本添加完整版
            其余部分不要动
    编译make
    安装make install  
        "安装":把编译busybox生成的二进制文件统一拷贝到某个目录下
        默认安装到_install目录中
    _install目录的结果:
                linuxrc bin sbin usr/bin usr/sbin
    这些生成的各种命令仅仅是bin/busybox二进制可执行文件的一个软连接而已
   
  3.添加根文件系统rootfs的必要目录和可选目录
    必要:mkdir etc lib proc sys dev  
    可选:mkdir home mnt var tmp opt
   
  4.案例:编译busybox,得到最初的根文件系统rootfs模型
    1.获取busybox源码
       从www.busybox.net直接下载获取即可
       ftp://porting/busybox-1.21.1.tar.bz2
      
     2.获取正确的交叉编译器
       确保交叉编译器的版本和busybox的版本门当户对
        
     3.busybox源码的交叉编译
       3.1.源码解压缩
       cp busybox-1.21.1.tar.bz2 /opt/
       cd /opt
       tar -xvf busybox-1.21.1.tar.bz2 //得到busybox源码目录:busybox-1.21.1
       mv busybox-1.21.1 busybox //重命名
        
       3.2.修改Makefile
       cd /opt/busybox //进入busybox源码根目录
       vim Makefile +190
       将
       ARCH ?= $(SUBARCH)
       修改为:
       #指定将来根文件系统运行在ARM架构
       ARCH=arm
       保存退出
        
       vim Makefile +164
       将
       CROSS_COMPILE ?=  
       修改为
       #指定交叉编译器
       CROSS_COMPILE=arm-cortex_a9-linux-gnueabi-
       保存退出
        
       3.3.配置busybox源码
       cd /opt/busybox
       make menuconfig
                Linux Module Utilities  --->  
                             //按N键去除选项(insmod/lsmod/rmmod精简版命令)
                             [*] Simplified modutils (NEW)   
                             去除以上选项,立马出现完整版的命令选项:
                             [*]   insmod (NEW)                                                           │ │   
                               [*]   rmmod (NEW)                                                            │ │   
                             [*]   lsmod (NEW)  
                             [*]   Pretty output (NEW)  
                             [*]   Blacklist support                                                               │ │   
                               [*]   modprobe (NEW)                                                         │ │   
                           [*]   depmod (NEW)
            保存退出
              注意:目前busybox提供的命令已经足够使用
          
          3.4.正式交叉编译源码
            cd /opt/busybox
            time make -j2/j4/j8
            make install //安装编译busybox生成的各种二进制文件
                         //也就是将编译生成的各种二进制文件统一
                           拷贝到某个目录下
            ls _install/  //查看编译生成的内容
               linuxrc bin sbin usr //验证了busybox仅仅提高各种命令
            ls _install/bin/* -lh
            ls _install/sbin/* -lh
            ls _install/usr/bin/* -lh
            ls _install/usr/sbin/* -lh
            结论:所有的命令本质就是一个软连接文件
                  最终都连接到_install/bin/busybox
                  也就是编译busybox本质就是生成了一个
                  可执行文件bin/busybox
            
           3.5.创建必要目录和可选目录
               mv /opt/rootfs /opt/rootfs_bak //备份原先的根文件系统
               cp /opt/busybox/_install /opt/rootfs -frd   
               cd /opt/rootfs //进入自己制作的根文件系统的根目录
                
               //创建必要目录
               mkdir dev lib etc proc sys
                     
               //创建可选目录
               mkdir home tmp var mnt opt
            
           3.6.添加部署必要的动态库
               明确:
               1.动态库添加到根文件系统的lib必要目录
               2.要拷贝的动态库来自于交叉编译器
               3.添加动态的基本原则是应用程序所需哪些
                 动态库,就拷贝需要的动态库即可,
                 获取一个应用程序运行时所需的动态库:
                 cd /opt/rootfs
                 //获取busybox可执行程序的所需的动态库
                 arm-cortex_a9-linux-gnueabi-readelf -d bin/busybox 内容如下:
                 共享库:[libm.so.6] //标准数学运算库
             共享库:[libc.so.6] //标准C库
             结论:busybox可执行程序所需的动态库为:libm.so.6和libc.so.6
           4.从交叉编译器获取要拷贝的动态库到根文件系统rootfs的lib目录下
             //获取到编译busybox的交叉编译器的路径
             which is arm-cortex_a9-linux-gnueabi-gcc
             //进入交叉编译器的根目录
             cd /opt/toolchains
              
             //添加libc.so.6动态库
             find . -name libc.so.6 得到:
                ./arm-cortex_a9-linux-gnueabi/sysroot/lib/libc.so.6
             ls ./arm-cortex_a9-linux-gnueabi/sysroot/lib/libc.so.6 -lh 得到
                     ./arm-cortex_a9-linux-gnueabi/sysroot/lib/libc.so.6 -> libc-2.18-2013.10.so
                                说明libc.so.6也仅仅是一个软连接文件,所以拷贝时务必将实体文件一块拷走
                         cp ./arm-cortex_a9-linux-gnueabi/sysroot/lib/libc.so.6 /opt/rootfs/lib/ -d
                         cp ./arm-cortex_a9-linux-gnueabi/sysroot/lib/libc-2.18-2013.10.so /opt/rootfs/lib/ -d
                          
             //添加libm.so.6动态库
             cd /opt/toolchains
             find . -name libm.so.6 得到:
                ./arm-cortex_a9-linux-gnueabi/sysroot/lib/libm.so.6
             ls ./arm-cortex_a9-linux-gnueabi/sysroot/lib/libm.so.6 -lh 得到
                     ./arm-cortex_a9-linux-gnueabi/sysroot/lib/libm.so.6 -> libm-2.18-2013.10.so
                                说明libc.so.6也仅仅是一个软连接文件,所以拷贝时务必将实体文件一块拷走
                         cp ./arm-cortex_a9-linux-gnueabi/sysroot/lib/libm.so.6 /opt/rootfs/lib/ -d
                         cp ./arm-cortex_a9-linux-gnueabi/sysroot/lib/libm-2.18-2013.10.so /opt/rootfs/lib/ -d
            
                 //切记切记切记:最后还要添加动态库使用时所需的加载器
                 cd /opt/toolchains
                 find . -name ld-*  得到加载器
                    ./arm-cortex_a9-linux-gnueabi/sysroot/lib/ld-2.18-2013.10.so
                ./arm-cortex_a9-linux-gnueabi/sysroot/lib/ld-linux.so.3
                         cp ./arm-cortex_a9-linux-gnueabi/sysroot/lib/ld-* /opt/rootfs/lib/ -d
                          
             切记:根文件系统rootfs的lib目录下仅仅存放从交叉编译器中拷贝的动态库
                   也就是仅仅存放标准的动态库(系统库),自己移植其他开源软件获取的
                   动态库或者自己制作的动态库一律不允许放到根文件系统rootfs的lib目录下
                   如果是自己移植开源软件获取的动态库和自己制作的动态库可以放置在
                   根文件系统的home目录下(例如home/lib)将来只需添加一个环境变量指定
                   动态库的路径即可,例如:
                   export LD_LIBRARY_PATH=/home/lib:$LD_LIBRARY_PATH
          
         3.7.添加系统启动的必要配置文件和脚本文件
             所需的配置文件和脚本文件一律在根文件系统的etc必要目录
             1.inittab配置文件
               cd /opt/rootfs/
               vim etc/inittab 添加如下内容
               ::sysinit:/etc/init.d/rcS
               ::respawn:-/bin/sh
               保存退出
               说明:系统启动流程
               上电CPU从EMMC的512字节运行uboot
               ->uboot运行首先进行硬件初始化
                 然后根据bootcmd从某个地方加载
                 内核到内存并且启动内核
                 在启动内核之前给内核传递参数通过bootargs
               ->内核uImage启动,首先做7大子系统的初始化
                 最后根据bootargs到某个地方找根文件系统rootfs
               ->内核找到rootfs以后,内核启动rootfs中的/sbin/init
                 第一号进程,第一号进程init首先打开rootfs目录下的
                 etc/inittab文件,第一号进程init会解析inittab文件
                 首先找到sysinit关键字,一旦找到此关键字
                 第一号进程会创建一个子进程执行sysinit关键字指定的
                 脚本程序etc/init.d/rcS,父进程第一号进程会等待子进程
                 执行完rcS脚本程序
               ->子进程执行完rcS脚本程序以后,父进程init继续执行
                 继续解析inittab文件,找到respawn关键字,一旦找到
                 这个关键字,父进程init继续创建一个子进程,子进程就会
                 执行respawn对应的程序/bin/sh,父进程继续等待子进程
                 至此启动了一个shell程序,用户可以输入各种命令
              
             2.添加系统启动脚本文件rcS
               存于根文件系统rootfs的etc/init.d/目录下
               cd /opt/rootfs
               mkdir etc/init.d/
               vim etc/init.d/rcS 添加如下内容
               mount -a  
               mkdir /dev/pts
               mount -t devpts devpts /dev/pts
               echo /sbin/mdev > /proc/sys/kernel/hotplug
               mdev -s
               保存退出即可
               说明:
               mount -a:系统会自动解析fstab配置文件,系统根据
                        此配置文件进行一系列的挂接动作
               mount -t devpts devpts /dev/pts:将虚拟文件系统devpts
                                                挂接到/dev/pts目录下
                                                将来/dev/pts目录就可以作为
                                                devpts虚拟文件系统的入口
                                                此命令用于将来可以远程登录开发板,
                                                例如:telnet          
               echo /sbin/mdev > /proc/sys/kernel/hotplug:
               向文件/proc/sys/kernel/hotplug写入字符串"/sbin/mdev"  
               起始就是告诉内核驱动将来创建设备文件的程序(人)是/sbin/mdev
                
               mdev -s:系统启动,将内核驱动对应的设备文件进行自动创建
              
             3.添加系统启动的配置文件fstab
               存于根文件系统rootfs的etc目录下  
               cd /opt/rootfs
               vim etc/fstab 添加如下内容
                proc   /proc   proc    defaults   0   0
                sysfs  /sys    sysfs   defaults   0   0
                tmpfs  /dev    tmpfs   defaults   0   0
               保存退出
               说明:
               第一列:表示要挂接的设备
               第二列:表示挂接点,将来的入口
               第三列:表示指定的文件系统格式
               第四,五,六:分别指定访问权限
               结论:
               将来/proc目录,/sys目录,/dev/目录分别作为
               procfs,sysfs,tmpfs三种虚拟文件系统的入口
               并且以上三种虚拟文件系统将来创建的内容都是
               内核来创建,并且分别创建到/proc,/sys,/dev/三个
               目录中,关键的关键,这三个目录下将来内核创建的
               目录也好,文件也好,都是存在于内存中,掉电就会丢失!
           
          3.8.至此基本最小的根文件系统制作完毕
              cd /opt/
              du rootfs -lh //3.2MB
              继续精简rootfs的体积:
              arm-cortex_a9-linux-gnueabi-strip rootfs/lib/* -lh
              du rootfs -lh //2.7MB
              结论:
              strip命令用于产品软件的最后发布阶段,可以让软件的体积变小,节省磁盘空间
              反过来产品研发阶段不要使用,一旦使用无法进行gdb跟踪调试!
              最后利用NFS网络进行测试      
           
          3.9.下位机测试:
              重启下位机,进入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=/linxurc  
               console=ttySAC0,115200 maxcpus=1
              saveenv
              tftp 48000000 uImage
              bootm 48000000 //查看是否能够挂接自己制作的根文件系统
              注意:shell终端之前的打印信息仔细看,是否有错误!
               
          3.10.在自己制作的根文件系统中添加一个应用程序
               上位机执行:
               cd /opt/rootfs
               vim helloworld.c //最好是线程程序
               arm-cortex_a9-linux-gnueabi-gcc -o helloworld helloworld.c
                
               下位机测试:
               cd /
               ls
                  helloworld
               ./helloworld //看是否能够正常运行
               是否出现类似:libxxx.so...找不到
               问:如何解决呢?
               答:只需到交叉编译器中找到对应的动态库并且
                   拷贝到根文件系统rootfs的lib目录下
                   注意软连接问题噢!
         问:cannot run /etc/init.d/rcS: Permission denied  
         答:rcS脚本文件没有可执行权限,解决办法:
             cd /opt/rootfs
             chmod 777 etc/init.d/rcS
 
案例.向根文件系统rootfs添加自己移植或者自己制作的动态库
实施步骤:
1.明确:自己移植或者自己制作的动态库一律不允许放到根文件系统rootfs  
  的必要目录lib下,要单独存放,注意设置环境变量
2.上位机执行:
  mkdir /opt/rootfs/home/applib   
  cd /opt/rootfs/home/applib
  vim test.h //声明 添加如下内容
    #ifndef __TEST_H
    #define __TEST_H
    extern void my_test(void);
    #endif
  保存退出
  vim test.c //定义 添加如下内容
  #include <stdio.h>
  void my_test(void)
  {
      printf("%s\n", __func__);
  }
  保存退出
   
  vim main.c //调用  
  #include <stdio.h>  
  #include "test.h"  
   
  int main(void)
  {
      my_test(); //调用
      return 0;
  }   
   
  编译:
  arm-cortex_a9-linux-gnueabi-gcc -shared -fpic -o libtest.so test.c  
  arm-cortex_a9-linux-gnueabi-gcc -o main main.c -L. -ltest
  注意:不要将libtest.so拷贝到/opt/rootfs/lib下,相当危险!
   
  下位机测试:
  进入下位机的linux系统,执行:
  cd /home/applib  
  ls
     libtest.so main
  ./main  //势必提示libtest.so找不到
  解决办法:
  export LD_LIBRARY_PATH=/home/applib:$LD_LIBRARY_PATH
  然后
  ./main
   
 
案例:在根文件系统中添加应用程序自启动功能
上位机实施步骤:
cd /opt/rootfs
vim etc/init.d/rcS 在文件最后添加如下内容:
export LD_LIBRARY_PATH=/home/applib:$LD_LIBRARY_PATH
/home/applib/main &
保存退出
重启下位机,看main程序是否能够自己运行
 
案例:问:rootfs_ext4.img从何而来?
答:rootfs_ext4.img仅仅是根文件系统rootfs一个镜像文件而已
    此镜像中同样包含了根文件系统rootfs的内容
问:如何将自己制作的rootfs制作成一个单个二进制镜像文件呢
    也就是将/opt/rootfs(目录)->rootfs_ext4.img(单个文件)
上位机实施步骤:
  cd /opt/
  sudo dd if=/dev/zero of=rootfs_ext4.img bs=1k count=8196
  命令说明:
  dd:用于创建一个单个镜像文件
  if=/dev/zero:将来创建的单个镜像文件里面的内容全部来自设备/dev/zero
               /dev/zero设备能够源源不断产生0数据                                   
  of=rootfs_ext4.img:指定将来创建单个镜像文件名
               并且此文件里面填充全0     
  bs=1k:生成的rootfs_ext4.img以块为单位,一块1024字节
  count=8196:总共8196块
  结论:生成的rootfs_ext4.img数据块为8MB
   
  sudo mkfs.ext4 rootfs_ext4.img //格式化镜像文件rootfs_ext4.img
                                 //把rootfs_ext4.img比作U盘
                                 指定的文件系统格式为ext4
                                 //类似windows下格式化U盘
   
  sudo mkdir /mnt/initrd //创建一个目录
  sudo mount -t ext4 -o loop rootfs_ext4.img /mnt/initrd
  命令说明:
  挂接rootfs_ext4.img到目录/mnt/initrd,并且指定的文件系统类型ext4
  挂接命令的结果就是将来只需要访问/mnt/initrd,本质
  就是在访问rootfs_ext4.img里面的内容
   
  sudo cp /opt/rootfs/* /mnt/initrd -frd //向目录initrd
          拷贝rootfs的内容,本质上就是向rootfs_ext4.img
          拷贝rootfs的内容
          结果是拷贝完毕,rootfs_ext4.img里面的内容
          就是/opt/rootfs里面的内容
   
  sudo umount /mnt/initrd //卸载/mnt/initrd,将来initrd
                          不再作为rootfs_ext4.img的入口
  cp rootfs_ext4.img /tftpboot
  至此第一天烧写系统使用的rootfs_ext4.img就是这么来的!
 
3.2.向EMMC烧写战果rootfs_ext4.img
  下位机重启,进入uboot命令行模式执行:
   
  1.向下位机部署系统软件之前,切记记得要进行分区规划
  EMMC存储空间的划分:
  0--512----------1M--------7M--------17M---------剩余
          uboot       uImage    rootfs    大片
       mmcblkboot0   mmcblk0p1 mmcblk0p2
      uboot已经完成    自己分    自己分   
   
  2.烧写uImage
  tftp 48000000 uImage
  mmc write 48000000 0x800  0x3000 //emmc地址以块单位,一块0x200=512字节
   
  计算流程:
  0x800起始地址=0x100000/0x200
  0x3000分区大小=7M-1M=0x600000/0x200
   
  3.烧写rootfs_ext4.img
  tftp 480000000 rootfs_ext4.img //下载自己制作的rootfs
  mmc write 48000000 3800 5000
   
  计算流程:
  0x3800起始地址=0x700000/0x200
  0x5000分区大小=10M=0xa00000/0x200
   
  4.设置系统启动参数
  setenv bootcmd mmc read 48000000 0x800  0x3000 \; bootm 48000000
  setenv bootargs root=/dev/mmcblk0p2 console=ttySAC0,115200 rootfstype=ext4
         maxcpus=1
  saveenv
   
  8.6.切记:如果对分区进行了修改,记得要对EMMC重新分区
      利用uboot提供的fdisk命令
      fdisk 2 2 0x100000:0x600000 0x700000:0xa00000
      说明:
      第一个‘2’:表示emmc
      第二个‘2’:表示分两个分区(uImage+rootfs)
                 uboot分区不用做
      0x100000:0x600000:第二个分区的起始地址和大小
      0x700000:0xa00000:第三个分区的起始地址和大小
       
  8.7.重新启动系统,测试rootfs_ext4.img
      系统启动完毕,观察main程序是否启动,看main的打印的信息
 
大的面试题:谈谈对嵌入式linux系统的认识
       
       
     
 
           
                          
            
原创粉丝点击