arm嵌入式Linux系统移植实例过程及问题

简介: arm嵌入式Linux系统移植实例过程及问题

制作根文件系统并使用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/三个

目录中,关键的关键,这三个目录下将来内核创建的

目录也好,文件也好,都是存在于内存中,掉电就会丢失!


下位机执行

  • 分区
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)


  • 烧写内核到mmc
#擦除当前mmc中数据
mmc erase 0x800 0x3000
tftp  48000000 uImage
#注意:下载完毕以后,最后会提示uImage下载的文件大小
 #烧写Linux内核     
mmc write 48000000 0x800 0x3000
#48000000:内存的起始地址,按字节为单位
#0x800:要写的emmc的起始地址,按sector为单位,0x800=0x100000/512
#0x3000:要向emmc写入的数据大小,按sector为单位,0x3000=uImage文件大小/512


  • 也可以先将一个文件系统镜像rootfs_ext4.img烧写到mmc中
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烧写完毕
#使用该文件系统,修改参数
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:指定触摸屏的型号


  • 添加nfs文件系统读取参数
重启下位机,进入uboot命令行执行:
setenv bootargs root=/dev/nfs nfsroot=192.168.0.104:/opt/rootfs  ip=192.168.0.118:192.168.0.104:192.168.0.1:255.255.255.0  init=/linuxrc console=ttySAC0,115200   maxcpus=1 lcd=vs070cxn tp=gslx680-linux
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

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的打印的信息


相关文章
|
4月前
|
Ubuntu Linux
查看Linux系统架构的命令,查看linux系统是哪种架构:AMD、ARM、x86、x86_64、pcc 或 查看Ubuntu的版本号
查看Linux系统架构的命令,查看linux系统是哪种架构:AMD、ARM、x86、x86_64、pcc 或 查看Ubuntu的版本号
978 3
|
2天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
35 13
|
2月前
|
Ubuntu Shell API
Ubuntu 64系统编译android arm64-v8a 的openssl静态库libssl.a和libcrypto.a
Ubuntu 64系统编译android arm64-v8a 的openssl静态库libssl.a和libcrypto.a
|
4月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
128 3
|
4月前
|
Linux 网络安全 开发工具
内核实验(二):自定义一个迷你Linux ARM系统,基于Kernel v5.15.102, Busybox,Qemu
本文介绍了如何基于Linux Kernel 5.15.102版本和BusyBox创建一个自定义的迷你Linux ARM系统,并使用QEMU进行启动和调试,包括内核和BusyBox的编译配置、根文件系统的制作以及运行QEMU时的命令和参数设置。
364 0
内核实验(二):自定义一个迷你Linux ARM系统,基于Kernel v5.15.102, Busybox,Qemu
|
4月前
|
存储 Linux 网络安全
【Azure 应用服务】App Service For Linux 如何在 Web 应用实例上住抓取网络日志
【Azure 应用服务】App Service For Linux 如何在 Web 应用实例上住抓取网络日志
|
4月前
|
传感器 人工智能 网络协议
:嵌入式 Linux 及其用途
【8月更文挑战第24天】
217 0
|
弹性计算 Linux 数据安全/隐私保护
【ECS常见问题】Linux 实例使用问题
如何查询 Linux 实例的账号和密码? 云服务器 ECS 的账号和密码会在创建成功后发送到您账号绑定的手机和邮箱。Linux 系统的默认管理员账号是 root。
【ECS常见问题】Linux 实例使用问题
|
1月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
92 8
|
1月前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
251 6