ARM嵌入式——制作根文件系统并使用NFS挂载运行。

制作根文件系统并使用NFS挂载运行。


上位机准备:

  • 准备busybox,安装menuconfig所需依赖的库:

    • sudo apt-get install build-essential
    • sudo apt-get install libncurses5
    • sudo apt-get install libncurses5-dev
  • 在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提供的命令已经足够使用

  • 修改Makefile进行交叉编译:

    • vim Makefile +164 //修改为指定的交叉编译器。
    • vim Makefile +190 //修改为ARCH=arm。(指定架构)
    • 保存退出
  • 正式进行编译:

    • make
    • make install
    • ls _install/ //查看编译生成的内容
    • cd _install; linuxrc bin sbin usr //验证了busybox仅仅提供各种命令。
  • 创建根文件目录:

    • cp _install /opt/rootfs -frd
    • cd /opt/rootfs //进入制作好的根文件系统根目录下。
    • mkdir dev lib etc proc sys //创建必要的系统目录。
    • mkdir home tmp var mnt //创建可选的目录
  • 添加必要的依赖动态库:

//获取到编译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

添加系统启动的必要配置文件和脚本

  • 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程序,用户可以输入各种命令

添加系统启动脚本文件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:系统启动,将内核驱动对应的设备文件进行自动创建

添加系统启动的配置文件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/三个目录中,关键的关键,这三个目录下将来内核创建的目录也好,文件也好,都是存在于内存中,掉电就会丢失!
    

下位机执行

重启下位机,进入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终端之前的打印信息仔细看,是否有错误!
  • 在自己制作的根文件系统中添加一个应用程序
  • 上位机执行:
   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就是这么来的!

向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

切记:如果对分区进行了修改,记得要对EMMC重新分区

  • 利用uboot提供的fdisk命令
 fdisk 2 2 0x100000:0x600000 0x700000:0xa00000
  • 说明:
  •  第一个‘2’:表示emmc
    
  •  第二个‘2’:表示分两个分区(uImage+rootfs),uboot分区不用做。
    0x100000:0x600000:第二个分区的起始地址和大小
    0x700000:0xa00000:第三个分区的起始地址和大小
    
  • 重新启动系统,测试rootfs_ext4.img
    系统启动完毕,观察main程序是否启动,看main的打印的信息

发布了78 篇原创文章 · 获赞 25 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_37596943/article/details/103530040