linux系统 = linux内核 + 根文件系统;
根文件系统 = 库(/lib和/usr/lib) + 命令( /bin和/usr/bin) + 配置文件( /etc) + GUI(图形化界面,如:QT)
为后边学习linux驱动开发打基础
linux驱动开发:基于linux内核提供的接口,完成硬件驱动的开发。
给开发板移植一个linux操作系统
arm-none-linux-gnueabi-gcc 1.c //生成elf文件
arm-none-linux-gnueabi-objdump -D a.out >1.dis
arm-none-linux-gnueabi-gcc 1.c //生成elf文件
arm-none-linux-gnueabi-objcopy -O binary a.out >1.bin
如果是交叉编译工具链(在命令的前边添加交叉编译工具链对应的前缀)
比如:arm-none-linux-gnueabi-
以ARM阶段,汇编点灯的代码为例进行gnu命令的演示:
//只编译不链接
arm-none-linux-gnueabi-gcc -c led_asm.S -o led_asm.o
//链接到代码段的0x43c00000这个地址上
arm-none-linux-gnueabi-ld -Ttext=0x43c00000 led_asm.o -o led_asm.elf
//生成反汇编文件,重定向到led_asm.dis(若不重定向则默认输出到终端)
arm-none-linux-gnueabi-objdump -D led_asm.elf > led_asm.dis
//生成.bin二进制文件
arm-none-linux-gnueabi-objcopy -O binary led_asm.elf led_asm.bin
//查看led_asm.elf文件符号表 (地址 loop等),查看有哪些函数名等
arm-none-linux-gnueabi-nm led_asm.elf
//查看各个段的文件大小
arm-none-linux-gnueabi-size led_asm.elf
//查看文件的头部信息(起始地址,ARM架构等等)
arm-none-linux-gnueabi-readelf -h led_asm.elf
//压缩文件
arm-none-linux-gnueabi-strip led_asm.elf
//在interface.elf中0x43c016f8这个地址对应的函数名和行号
arm-none-linux-gnueabi-addr2line 0x43c016f8 -e interface.elf -f
---->
hal_gpio_init
/home/arm/02led_c/src/led.c:11
0x43c016f8 : 错误信息的地址
-e ***.elf : 指令elf文件
-f : 回显函数的名字和行号
使用场合:常用于调试内核中段错误和野指针。(可以通过backtrace函数获取错误信息的地址)
注意:
1.在实际的开发中,不同的硬件平台,使用的交叉编译工具链的版本可能不一致。
2.uboot源码或linux内核源码的版本不一致,可能交叉编译工具链的版本也不一致。
tftp:基于TCP/IP的简单文本传输协议。
Trivial File Transfer Protocol:
基于TCP/IP协议的一个用来在客户机与服务器之间进行简单文件传输的协议。
作用:基于网络,使用tftp服务下载程序到开发板。
tftp服务安装的具体步骤:
1. 检查ubuntu是否安装了tftp服务(可以忽略,因为大部分没有安装)
sudo dpkg -s tftpd-hpa
打印以下内容表示安装了tftp服务:
Architecture: i386
Source: tftp-hpa
Version: 5.2-7ubuntu3.1
2. 安装tftp服务 (前提:ubuntu必须能连接外网)
安装tftp服务的命令:
sudo apt-get install tftpd-hpa tftp-hpa
如果安装失败,可以更新源和依赖:
sudo apt-get update ---> 更新源
sudo apt-get install -f ---> 跟新依赖
3. 配置tftp服务
1>. 在家目录下创建一个tftpboot文件夹
mkdir tftpboot
目的:tftpboot目录下存放的是你要下载到
开发板上的可执行文件
2>. 修改tftpboot的权限
chmod 777 tftpboot
3>. 配置tftp服务的环境变量
打开sudo vi /etc/default/tftpd-hpa
修改以下内容:
1 # /etc/default/tftpd-hpa
2
3 TFTP_USERNAME="tftp"
# tftp用户名,不需要修改
4 TFTP_DIRECTORY="/home/linux/tftpboot"
# tftp服务下载文件的存放的路径,需要修改
# 改成自己的对应的tftpboot的路径
5 TFTP_ADDRESS="0.0.0.0:69"
# tftp服务默认使用的69端口号
6 TFTP_OPTIONS="-c -s -l"
# tftp服务的参数,这个需要修改
# -l:以standalone/listen模式启动TFTP服务
# -c:可创建新文件。默认情况下,
# TFTP只允许覆盖原有文件,不能创建新文件。
# -s:改变TFTP启动的根目录。
# 加了-s后,客户端使用TFTP时,
# 不再需要输入指定目录,填写文件的完整路径,
# 而是使用配置文件中写好的目录。
# 这样也可以增加安全性。
4. 重启tftp服务(只需要执行以下命令中的一个即可)
1. sudo service tftpd-hpa start 启动TFTP服务
2. sudo service tftpd-hpa restart 重启TFTP服务(默认执行重启)
注意:
1. 有时一旦重启系统或者长时间不使用tftp服务,都需要重启tftp服务
2. 只需修改了tftp的配置文件就需要重启tftp服务
5. 本地测试tftp服务是否安装成功
// 使用本地tftp客户端,连接tftp本地的服务器
$ tftp 127.0.0.1 # 本地回环测试IP地址
tftp> get 1.c # 从tftpboot目录下,下载1.c文件到当前目录
# 注:需要在tftpboot目录下创建1.c
tftp> put 2.txt # 把当前目录中的2.c文件,上传到tftpboot文件夹中
# 注:需要在当前目录下需要创建2.c
tftp> q <回车> 退出
6. 可能出现的问题
下载或上传是,一直卡,
原因:
1. tftp服务安装成功,需要重启tftp服务
2. tftp服务安装不成功,重新安装tftp服务
3. tftp安装成功,检查/etc/default/tftpd-hpa环境变量修改是否正确,如果修改环境变量,必须重启tftp服务
4. 关闭windows和ubuntu的防火墙(ubuntu的防火墙默认都是关闭的)sudo ufw disable
tftp服务的作用 参考下图
nfs:Network File System(网络文件系统)
作用:让开发板通过网络的方式远程从服务器端挂载根文件系统
ubuntu安装nfs服务器的步骤:
1. 检查nfs服务是否安装
sudo dpkg -s nfs-kernel-server
2. 安装nfs服务(前提:可以上网)
sudo apt-get install nfs-kernel-server
3. 配置nfs服务
1>在家目录下创建nfs文件夹
mkdir nfs
2>设置文件夹的权限最大
chmod 777 nfs
3>拷贝根文件系统到nfs目录下
根文件系统一会发给你们(rootfs-ok.tar.bz2)
cp /mnt/hgfs/share/rootfs-ok.tar.bz2 ~/nfs
4>对根文件系统的压缩包进行解压缩
cd ~/nfs
tar -vxf rootfs-ok.tar.bz2
5>配置nfs服务的环境变量
sudo vi /etc/exports
在文件的最后一行添加以下内容:
/home/linux/nfs/rootfs/ *(rw,sync,no_subtree_check,no_root_squash)
解析:
/home/linux/nfs/rootfs/:自己的根文件系统的路径
需要修改为自己的路径
*:所有的用户,
注:*和后边的左括号"("之间不可以出现空格.
rw:可读可写的权限
sync:同步文件
no_subtree_check:不对子目录检查文件的权限
no_root_squash:如果客户端为root用户,那么他对整个文件具有root的权限
注意:这段话前边不要加#,#号是这个文件中的注释符号
4. 重启nfs服务(任选其一)
1. sudo service nfs-kernel-server start 启动nfs服务
2. sudo service nfs-kernel-server restart 重启nfs服务
5. 本地测试nfs服务是否安装成功
1>回到家目录下
cd ~
2>sudo mount -t nfs 本机IP地址:/home/linux/nfs/rootfs/ /mnt
nfs:使用nfs服务,将本机IP地址:/home/linux/nfs/rootfs/文件挂载到/mnt目录下
3>检查/mnt目录下是否挂载成功
cd /mnt
ls
4>卸载挂载的文件
sudo umount /mnt
注意:不可以在/mnt目录下执行卸载的命令
nfs服务的作用参考图:如下
boot : 引导
loader : 加载
bootloader : 一系列引导加载系统启动的程序的统称。
比如:
u-boot属于bootloader中的一种引导程序
BIOS属于bootloader中的一种引导程序
嵌入式开发中使用最多的是u-boot
德国DENX软件工程中心负责维护u-boot源码。
help 查看uboot支持的所有的命令
如:(列举部分)
uboot命令 uboot命令的描述
loadb - load binary file over serial line (kermit mode)
loadbmp - load bmpfile with command or 'bootlog' environment
loadx - load binary file over serial line (xmodem mode)
loady - load binary file over serial line (ymodem mode)
查看u-boot命令的帮助手册
help u-boot命令
如:help loadb
作用: 通过串口的方式下载二进制文件到内存中、
用法: loadb+下载的地址+波特率(可省)
作用: 从内存的某个地址运行程序
用法: go+地址
作用:打印u-boot默认的环境变量
u-boot解析命令时,是部分字符的比较,所以用printenv和print和pri都可以
baudrate=115200 # 串口波特率
bootdelay=3 # 倒计时的时间
gatewayip=192.168.5.1 # 开发板的网关
ipaddr=192.168.5.222 # 开发板的IP地址
netmask=255.255.255.0 # 开发板的子网掩码
serverip=192.168.5.250 # ubuntu系统的IP地址
stderr=serial # 标准错误
stdin=serial # 标准输入
stdout=serial # 标准输出
setenv : 设置环境变量,默认在内存中(断电就丢失)
saveenv :保存环境变量,将内存中的环境变量保存到EMMC中(断电不会丢失)
setenv 新的环境变量名 变量的值
saveenv
注意:
eg:
setenv target s5p6818
saveenv
setenv 要修改的环境变量名 变量的值
saveenv
eg:
setenv bootdelay 5
注: 不要将bootdelay的值改成0。
setenv 要删除的环境变量名
saveenv
eg:
setenv target
作用: 回显一块连续空间内存地址中的值
格式 : md mem_addr (内存地址)
eg: md 0xC001A000
作用: 修改某个内存地址中的内容
案例: 熄灭红色LED灯
GPIOA28
0xC001A024[25:24] = 00
0xC001A004[28] = 1
0xC001A000[28] = 0
------------------------------------------------
FS6818# nm 0xc001a024
地址 地址中的值 输入的值
c001a024: 74555555 ? 74555555 回车
c001a024: 74555555 ? q 回车 :退出
FS6818# nm 0xc001a004
c001a004: 00000000 ? 10000000
c001a004: 10000000 ? q
FS6818# nm 0xc001a000
c001a000: 10000000 ? 00000000
c001a000: 00000000 ? 10000000
c001a000: 10000000 ? 00000000
用法: ping pingAddress(地址)
测试开发板是否可以和ubuntu的服务器ping通
格式:tftpboot [loadAddress] [bootfilename]
作用:使用tftp服务下载程序bootfilename到内存loadAddress中
[准备工作]
1>. ubuntu系统必须安装tftp服务
2>. 关闭windows和ubuntu的防火墙
3>. 设置有线网卡为百兆全双工
控制面板->网络和Internet->网络和共享中心->
更改适配器设置->以太网(USB转网卡)->属性->
配置->高级->Speed & Duplex ->100M full Duplex
每个人的电脑配置不同,可能略有差距。
[Target和PC端硬件连接]
pc Target
---------- ----------
| | | |
| | 电源--| |
| | | |
| |--串口线-| |
| | | |
| |--网线---| |
| | | |
---------- ----------
PC端和开发板进行网线连接的方式:参考下图
[配置ubuntu的网络]
ubuntu必须使用桥接的模式
具体的配置过程参考下图
注意:
1.在虚拟网络编辑器中:
桥接到不能选择自动,因为若选择自动,则可能会选用wifi,无法与开发板ping通
桥接应该选择Realtek PCIe GBE Family Controller(本地网卡,在windows的网络共享中心-->以太网看),但是此时虚拟机会没有网,但是无关紧要,只要开发板可以ping通即可
2.开发板要和ubuntu链接,必须是桥接模式(同理主机间的通信也必须是桥接模式,net为网络共享模式,不可进行跨主机通信)
3.开发板和主机必须在同一局域网下,主机号不同
4.在日常使用桥接模式给ubuntu连网时,注意在虚拟网络编辑器中选择桥接到自动,且在虚拟机-->设置-->网络适配器-->桥接模式
[配置开发板的网络]
配置开发板的网络就是配置u-boot的以下几个环境变量。
gatewayip=192.168.5.1 # 开发板的网关
ipaddr=192.168.5.222 # 开发板的IP地址
netmask=255.255.255.0 # 开发板的子网掩码
serverip=192.168.5.250 # ubuntu系统的IP地址
IP地址的网段,和IP地址根据自己的需要进行配置
配置过程演示:(在串口工具上执行)
FS6818# setenv gatewayip 192.168.1.1
FS6818# setenv netmask 255.255.255.0
FS6818# setenv ipaddr 192.168.1.222
FS6818# setenv serverip 192.168.1.250
FS6818# saveenv
FS6818# ping 192.168.1.250
出现以下信息,表示开发板可以ping通ubuntu系统:
dwmac.c0060000 Waiting for PHY auto negotiation to complete..... done
Speed: 100, full duplex
Using dwmac.c0060000 device
host 192.168.1.250 is alive
无法ping通的打印信息如下:
FS6818# ping 192.168.1.250
dwmac.c0060000 Waiting for PHY auto negotiation to complete......... TIMEOUT !
Waiting for PHY realtime link...... TIMEOUT !
done
dwmac.c0060000: No link.
ping failed; host 192.168.1.250 is not alive
注意:开发板无法ping通ubuntu的可能原因?
1. windows和ubuntu的防火墙
2. ubuntu网络的配置(桥接模式,网卡的选择,IP地址)
3. 开发板的网络的配置(u-boot的环境变量)
4. 检查网线(网线好坏,网线未插,网线松紧)
1. 拷贝ARM阶段某个interface.bin文件到~/tftpboot目录下
2. 使用tftp命令下载程序
下载的命令:
FS6818# tftp 0x43c00000 interface.bin
出现以下信息表示下载成功:
Speed: 100, full duplex
Using dwmac.c0060000 device
TFTP from server 192.168.1.250; our IP address is 192.168.1.222
Filename 'interface.bin'.
Load address: 0x43c00000
Loading: #
1.1 MiB/s
done
Bytes transferred = 14412 (384c hex)
3. 运行程序
FS6818# go 0x43c00000
注意:tftp下载不成功的可能原因?
1. 开发板无法ping通ubuntu
2. tftp安装成功,需要重启tftp服务
sudo service tftpd-hpa restart
3. tftp服务安装不成功
检查你的配置文件中,存放下载文件的路径。
一旦修改配置文件,必须重启tftp服务
ubootpak.bin : u-boot的镜像文件(ubuntu)
win_ubootpak.bin : u-boot的镜像文件(windows)
uImage : linux内核的镜像
rootfs : 根文件系统
ramdisk.img : 根文件系统的镜像;对rootfs进行打包压缩得到ramdisk.img
开发板上电->启动u-boot->加载内核启动内核->内核启动成功,挂载根文件系统
u-boot镜像-->烧写到-> SD/EMMC
uImage镜像-->使用tftp服务下载到-> 内存
根文件系统 -->使用nfs服务挂载-> ubuntu服务器
(rootfs)
u-boot镜像-->烧写到-> SD/EMMC
uImage镜像-->烧写到-> EMMC
根文件系统 -->烧写到-> EMMC
(ramdisk.img)
参考如下图
通过windows的工具烧写win_ubootpan.bin到SD卡中
打开win32DiskImager.exe—>选中win_uboottpak.bin—>write
参考:"烧写uboot到SD卡中"目录下的文档
通过ubuntu的sdtool工具制作sd卡启动盘
注意: 必须使用读卡器,不可以使用电脑自带的sd卡卡槽
1> 拷贝sdtool工具到ubuntu中
2> 将sd卡被ubuntu识别(默认是被windows识别)
先格式化sd卡
虚拟机–>设置–>USB控制器–>USB兼容性:USB3.0
虚拟机–>可移动设备–>选择自己的SD卡的名字–>连接
3> 烧写ubootpak.bin到sd卡中
1) 进入sdtool目录下
cd sdtool
2) sdtool目录下的文件分析
s5p6818-sdmmc.sh : 烧写的脚本文件
ubootpak.bin : uboot的镜像文件
3) 烧写ubootpak.bin到sd卡中
sudo ./s5p6818-sdmmc.sh /dev/sdb ubootpak.bin
解释:使用root的方式执行脚本文件
/dev/sdb : SD卡对应的设备文件
ubootpak.bin : uboot的镜像文件
将ubootpak.bin烧写到/dev/sdb设备对应的sd卡中
4> 以sd卡的方式重新启动,进行测试。
使用update_mmc命令将ubootpak.bin烧写到EMMC中
前提:
1> 开发板必须有能够启动的uboot
2> uboot必须支持tftp的命令
步骤:
1> 切换到sd卡的方式启动uboot,进入uboot的交互界面
2> 将ubootpak.bin拷贝到ubuntu的~/tftpboot目录下
3> 使用tftp命令烧写ubootpak.bin到内存的0x48000000地址中
tftp 0x48000000 ubootpak.bin
如果下载的过程中报权限不够的错误:
在ubuntu中修改ubootpak.bin文件的权限为777
chmod 777 ubootpak.bin
4> 将内存中的ubootpak.bin使用update_mmc命令搬移到EMMC中
update_mmc 2 2ndboot 0x48000000 0x200 0x78000
解析:
update_mmc <dev no> <type> <mem> <addr> <length>
- type : 2ndboot | boot | raw | part
<dev no> : 设备号: EMMC的设备号为2
<type> : 设备分区的类型 2ndboot
flash=mmc,2:ubootpak:2nd:0x200,0x78000;
flash=mmc,2:2ndboot:2nd:0x200,0x4000;
<mem> : 内存的起始地址
<addr> : EMMC的起始地址,以字节为单位
<length> : 搬移数据的长度,以字节为单位(必须大于文件的字节数)
5> 切换到EMMC的启动方式进行测试
u-boot镜像 -烧写到-> SD/EMMC
uImage镜像 -使用tftp服务下载到-> 内存
根文件系统 -使用nfs服务挂载-> ubuntu服务器
(rootfs)
bootargs:自启动参数, 给内核传递参数
root=/dev/nfs : 使用nfs的方式挂载根文件系统
nfsroot=192.168.1.250:/home/hqyj/nfs/rootfs
: 根文件系统的服务器的IP地址和路径
: 需要修改位自己的IP地址和路径
tcp,v4 : nfs服务基于tcp协议,使用v4版本
console=/dev/ttySAC0,115200
: 指定使用开发板的串口0,波特率115200
init=/linuxrc
: 系统启动之后运行的1号进行
ip=192.168.1.222
: 开发板的IP地址
启动内核
FS6818# bootm 0x41000000
系统启动成功之后,会自动通过nfs服务的方式从ubuntu中挂载
根文件系统。
启动内核的命令:bootm
编译一个helloworld程序,使用交叉编译工具链进行编译,在开发板上运行应用程序。
在 ubuntu上编写代码,
使用交叉编译工具链进行编译
arm-none-linux-gnueabi-gcc hello.c -o hello
或者
arm-none-linux-gnueabi-gcc hello.c
拷贝可执行文件到根文件系统(~/nfs/rootfs)中。
目标机放在nfs/rootfs中的文件在开发板上也会同步显示
在开发板新建文件也会在nfs/rootfs中新建
ubuntu系统是18.04以上版本需要修改ubuntu中的配置文件:
参考文档:
https://www.jianshu.com/p/10e3245f15f3?tdsourcetag=s_pctim_aiomsg
u-boot镜像 -烧写到-> SD/EMMC
uImage镜像 -烧写到-> EMMC
根文件系统 -烧写到-> EMMC
(ramdisk.img)
//mmc info显示当前的MMC设备的信息
mmc info - display info of the current MMC device
mmc read addr blk# cnt
mmc write addr blk# cnt
mmc erase blk# cnt
mmc: EMMC读写命令
read: 将EMMC中的内容读到内存中
addr: 读到内存的起始地址
blk#: EMMC设备的起始块号,读的起始块号
flash读写是以块为单位的,一块的大小是512字节
cnt: 读多少块
addr,blk#,cnt:都用十六进制数表示
mmc: EMMC读写命令
write: 将内存中的内容写到EMMC中
addr: 写到EMMC中数据在内存的起始地址
blk#: EMMC设备的起始块号,写的起始块号
flash读写是以块为单位的,一块的大小是512字节
cnt: 写多少块
addr,blk#,cnt:都用十六进制数表示
mmc: EMMC命令
erase: 擦除EMMC中的数据
blk#: 擦除EMMC中数据的起始块号
flash读写是以块为单位的,一块的大小是512字节
cnt: 擦除多少块
blk#,cnt:都用十六进制数表示
解析:
root=/dev/ram :从ram中挂载根文件系统
rw : 可读可写的权限
initrd=0x49000040,0x1000000
:0x49000000 : 根文件系统镜像的起始地址
:0x40 : 偏移64字节,跳过镜像文件的文件头
:0x1000000 : 根文件系统的大小
rootfstype=ext4
:根文件系统的类型
console=/dev/ttySAC0,115200
init=/linuxrc
uboot中bootcmd和bootargs环境变量的作用?
bootcmd:自启动命令
uboot倒计时减到0之前,不按下任意键,uboot会自动解析bootcmd环境变量后边的uboot命令,命令会被依次的执行
bootargs:自启动参数
用于uboot给内核传递参数,告诉内核一些重要的信息,使用哪种方式挂载根文件系统,从哪里挂载根文件系统,使用哪个串口,串口的波特率等。
1> u-boot官方获取
https://ftp.denx.de/pub/u-boot/
2> 芯片厂家
3> 开发板厂家
4> 公司主管
本次使用的u-boot-2014.07版本,
注: 不可以从uboot官方去下载uboot源码使用,samsung公司没有将对s5p6818芯片的硬件支持,开源到u-boot官方源码中。
u-boot-2021.01-rc2.tar.bz2 测试版
u-boot-2021.01.tar.bz2 稳定版
名字 年月
rc : 测试版
了解移植硬件平台的相关硬件配置信息:
cpu : Cortex-A53 * 8
Arch : arm
Vender : Samsung
SOC : S5P6818
Board : FS6818
1>. 拷贝u-boot源码到ubuntu中
u-boot-2014.07-netok.tar.bz2
2>. 解压缩
tar -vxf u-boot-2014.07-netok.tar.bz2
3>. 进入到u-boot源码目录
cd u-boot-2014.07
注:u-boot源码不要在共享目录或者windows下解压,因为在源码中有很多软链接文件,而windows不支持软链接文件
平台相关代码:跟硬件相关的代码
arch: 架构 --> 不同架构相关的代码
board: 板子 --> 处理器相关的代码
平台无关代码:跟硬件无关的代码
common: uboot相关的命令
drivers: 驱动
fs : 文件系统
include :头文件
lib : 库
net : 网络
tools : 工具
api : 接口
disk : 存储相关
doc : 文档
dts : 设备树
.....
1>. 修改uboot源码顶层目录的Makefile,使用交叉编译工具链对uboot源码进行编译。
将以下信息:
198 ifeq ($(HOSTARCH),$(ARCH))
199 CROSS_COMPILE ?=
200 endif
修改为:
198 ifeq (arm,arm)
199 CROSS_COMPILE ?= arm-none-linux-gnueabi-
200 endif
注:CROSS_COMPILE是指定交叉编译工具的前缀的
2> 获取make编译源码时的帮助信息
/********************
规则:(Makefile文件中的规则)
目标:依赖
命令
make 目标(去找目标,如果依赖不存在,则去找依赖如何合成的;如果依赖存在则执行下面的命令)
*********************/
make help 或者make的帮助信息
清除目标文件:(从上往下一个比一个清理得干净)
clean eg:make clean
clobber eg:make clobber
mrproper ....
distclean ....
编译uboot源码
all:根据配置信息编译uboot源码
For further info see the ./README file(更多信息可以查看readme文件)
3> 读README
//选择处理器的架构和板子的类型
251 Selection of Processor Architecture and Board Type:
252 ---------------------------------------------------
253
//对于所有的板子我们可以使用默认的配置变量
254 For all supported boards there are ready-to-use default
//可以使用"make _config"命令来实现让u-boot源码支持板子
255 configurations available; just type "make _config" .
256
//举例:让u-boot支持TQM823L板子
//进入 u-boot源码目录,使用命令make TQM823L_config即可
257 Example: For a TQM823L module type:
258
259 cd u-boot
260 make TQM823L_config
4> 配置u-boot-2014.07源码支持fs6818开发板
make fs6818_config
打印以下信息,表示配置成功:
Configuring for fs6818 board...
注:
1). 配置只需要在编译源码的时候执行一次(最开始的时候执行一次)
2). 如果执行了make distclean命令,需要重新执行make fs6818_config
5>. 编译u-boot源码
方法1:make
方法2:make all
方法3:make -j4 all # 使用4个线程编译源码
编译成功之后,会在uboot源码的顶层目录生成ubootpak.bin
6> 将自己编译的ubootpak.bin文件烧写到EMMC中,进行测试
boot :执行bootcmd后边的命令
reset : 复位uboot重新启动
问题总结:
1.启动内核总是重启:
原因:内核启动成功,但是挂载根文件系统失败。
挂载根文件系统失败的原因:
1> 检查bootargs参数设置是否正确
2> 检查nfs的配置是否正确
配置的跟文件系统的路径和bootargs的路径是否一致
3> 重启nfs服务
4> ubuntu是18.04及以上版本,需要修改配置文件
linux内核使用的nfs的版本是v4的,
而ubuntu18.04及以上版本,默认使用的是v5版本。
uboot源码本身已经支持了大多数的uboot命令
1> 思考:uboot源码是否支持loadb命令
//查找文件:find / -name 1.c(从根目录下查找1.c文件)
//使用greb命令搜索“loadb”字符串
grep "loadb" * -nR
获取以下信息:
common/cmd_load.c:389: * loadb command (load binary) included
common/cmd_load.c:1059: loadb, 3, 0, do_load_serial_bin,
2> 打开common/cmd_load.c文件定位到389和1059行
有以下信息:
1057 #if defined(CONFIG_CMD_LOADB)
1058 U_BOOT_CMD(
1059 loadb, 3, 0, do_load_serial_bin,
1060 "load binary file over serial line (kermit mode)",
1061 "[ off ] [ baud ]\n"
1062 " - load binary file over serial line"
1063 " with offset 'off' and baudrate 'baud'"
1064 );
说明uboot源码本事已经支持了loadb的命令
3> 思考2:有可能cmd_load.c没有被编译到ubootpak.bin
打开common目录下的Makfile,得到以下信息:
133 obj-y += cmd_load.o
all:$(obj-y)
说明:cmd_loadb.o被编译到ubootpak.bin中
4> 通过对cmd_load.c源码的分析,可能是CONFIG_CMD_LOADB没有被定义,添加一个定义然后重新编译。
打开include/configs/fs6818.h文件,
在121行添加以下内容:
#define CONFIG_CMD_LOADB
5>. 编译u-boot源码
make
make all
make -j4 all # 使用4个线程编译源码
编译成功之后,会在uboot源码的顶层目录生成
ubootpak.bin
6> 将自己编译的ubootpak.bin文件烧写到EMMC中,进行测试
启动开发板,测试loadb命令
需要具备的能力: Makefile shell脚本
//目标 依赖
467 %_config:: outputmakefile
//命令
468 @$(MKCONFIG) -A $(@:_config=)
469 @cp net/x6818-eth.mk net/eth.o
解析:
% :模式匹配(*多个字符匹配,?单个字符匹配)
@ :命令不在终端回显
$ : 使用某个变量
技巧:去掉命令前边的@符, 重新执行make fs6818_config
$(MKCONFIG) : /home/hqyj/20010-20011/porting/u-boot-2014.07/mkconfig
$(@:_config=) : fs6818
解析的结果:
/home/hqyj/20010-20011/porting/u-boot-2014.07/mkconfig -A fs6818
使用file命令,查看mkconfig文件的属性,
file mkconfig 可知mkconfig是一个shell脚本文件。
-A fs6818 是给mkconfig传递的两个参数
//$#:参数的个数,\是转义字符
//$1: 第一个参数
//[ ]: test命令
//如果参数个数等于2且第一个参数等于-A
24 if [ \( $# -eq 2 \) -a \( "$1" = "-A" \) ] ; then
25 # Automatic mode
//``:命令置换符
//$srctree/boards.cfg:uboot源码的顶层目录下的板子配置文件
//给line变量赋值;
//awk命令的含义是:从boards.cfg中一行一行去拿信息(print中的$0(不是#号的),并且$7等于$2(拿到的第二个参数))
//最终line=Active arm slsiap s5p6818
// $1 $2 $3 $4
//s5p6818 fs6818 fs6818 -
// $5 $6 $7 $8
26 line=`awk '($0 !~ /^#/ && $7 ~ /^'"$2"'$/) { print $1, $2, $3, $4, $5, $6, $7, $8 }' $srctree/boards.cfg`
27 if [ -z "$line" ] ; then
28 echo "make: *** No rule to make target \`$2_config'. Stop." >&2
29 exit 1
30 fi
31
//set ${line} : 重新设置执行mkconfig脚本文件时传递的参数
//最开始的参数:mkconfig -A fs6818
// 执行set ${line}之后变成
//mkconfig Active arm slsiap s5p6818 s5p6818 fs6818 fs6818 -
32 set ${line}
33 # add default board name if needed
//$# !=3,则与后的条件不会执行
34 [ $# = 3 ] && set ${line} ${1}
35 fi
给几个特殊的变量赋值:
50 # Strip all options and/or _config suffixes
//给CONFIG_NAME赋值$7:fs6818
51 CONFIG_NAME="${7%_config}"
52
//判断${BOARD_NAME}是否为空
53 [ "${BOARD_NAME}" ] || BOARD_NAME="${7%_config}"
54
//$2:arm
55 arch="$2"
//$3: slsiap
56 cpu=`echo $3 | awk 'BEGIN {FS = ":"} ; {print $1}'`
57 spl_cpu=`echo $3 | awk 'BEGIN {FS = ":"} ; {print $2}'`
58
59 if [ "$cpu" = "-" ] ; then
60 cpu=
61 fi
62
63 [ "$6" != "-" ] && board="$6"
64 [ "$5" != "-" ] && vendor="$5"
65 [ "$4" != "-" ] && soc="$4"
这几个变量中的值,具体是什么可以通过echo输出变量的值。
84 if [ "${ARCH}" -a "${ARCH}" != "${arch}" ]; then
85 echo "Failed: \$ARCH=${ARCH}, should be '${arch}' for ${BOARD_NAME}" 1>&2
86 exit 1
87 fi 没有执行
解析:
ARCH = arm
arch = arm
96 if [ "$options" ] ; then
97 echo "Configuring for ${BOARD_NAME} - Board: ${CONFIG_NAME}, Options: ${options}"
98 else // 板子配置成功的打印信息
99 echo "Configuring for ${BOARD_NAME} board..."
100 fi
126 # 创建make时需要的头文件
127 # Create include file for Make
128 #
129 ( echo "ARCH = ${arch}"
130 if [ ! -z "$spl_cpu" ] ; then
131 echo 'ifeq ($(CONFIG_SPL_BUILD),y)'
132 echo "CPU = ${spl_cpu}"
133 echo "else"
134 echo "CPU = ${cpu}"
135 echo "endif"
136 else
137 echo "CPU = ${cpu}"
138 fi
139 echo "BOARD = ${board}"
140
141 [ "${vendor}" ] && echo "VENDOR = ${vendor}"
142 [ "${soc}" ] && echo "SOC = ${soc}"
143 exit 0 ) > config.mk
//把以上很多信息重定向到config.mk文件中
config.mk是Makefile的头文件,
打开include/config.mk文件,有以下信息:
1 ARCH = arm
2 CPU = slsiap
3 BOARD = fs6818
4 VENDOR = s5p6818
5 SOC = s5p6818
Makefile根据config.mk中的变量的信息,决定哪些文件编译,那些不编译。
152 # 创建板子指定的头文件
153 # Create board specific header file
154 #
155 if [ "$APPEND" = "yes" ] # Append to existing config file
156 then
157 echo >> config.h
158 else
159 > config.h # Create new config file
160 fi
161 echo "/* Automatically generated - do not edit */" >>config.h
162
163 for i in ${TARGETS} ; do
164 i="`echo ${i} | sed '/=/ {s/=/ /;q; } ; { s/$/ 1/; }'`"
165 echo "#define CONFIG_${i}" >>config.h ;
166 done
167
168 echo "#define CONFIG_SYS_ARCH \"${arch}\"" >> config.h
169 echo "#define CONFIG_SYS_CPU \"${cpu}\"" >> config.h
170 echo "#define CONFIG_SYS_BOARD \"${board}\"" >> config.h
171
172 [ "${vendor}" ] && echo "#define CONFIG_SYS_VENDOR \"${vendor}\"" >> config.h
173
174 [ "${soc}" ] && echo "#define CONFIG_SYS_SOC \"${soc}\"" >> config.h
175
176 [ "${board}" ] && echo "#define CONFIG_BOARDDIR board/$BOARDDIR" >> config.h
177 cat << EOF >> config.h
178 #include <config_cmd_defaults.h>
179 #include <config_defaults.h>
180 #include <configs/${CONFIG_NAME}.h>
181 #include <asm/config.h>
182 #include <config_fallbacks.h>
183 #include <config_uncmd_spl.h>
184 EOF
打开include/config.h文件,有以下信息:
2 #define CONFIG_SYS_ARCH "arm"
3 #define CONFIG_SYS_CPU "slsiap"
4 #define CONFIG_SYS_BOARD "fs6818"
5 #define CONFIG_SYS_VENDOR "s5p6818"
6 #define CONFIG_SYS_SOC "s5p6818"
7 #define CONFIG_BOARDDIR board/s5p6818/fs6818
8 #include <config_cmd_defaults.h>
9 #include <config_defaults.h>
10 #include <configs/fs6818.h>
11 #include <asm/config.h>
12 #include <config_fallbacks.h>
13 #include <config_uncmd_spl.h>
重点关注:configs/fs6818.h头文件,
板子的头文件, 还有一些其他的配置信息都在这个文件中,
764 all: $(ALL-y)
712 ALL-y += u-boot.srec u-boot.bin System.map binary_size_check
804 u-boot.bin: u-boot FORCE
//$@:目标 $<:依赖
805 $(call if_changed,objcopy)
806 $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
807 $(BOARD_SIZE_CHECK)
808 ./tools/mk6818 ubootpak.bin nsih.txt 2ndboot u-boot.bin
分析 ./tools/mk6818 ubootpak.bin nsih.txt 2ndboot u-boot.bin
elf可执行文件 参数
使用file命令查看mk6818文件的数据,file ./tools/mk6818, 可知mk6818是一个可执行文件
mk6818可执行文件的功能:根据nsih.txt, 2ndboot和u-boot.bin文件合成ubootpak.bin
nsih.txt : 三星提供的代码,不开源,是机器码
2ndboot : 三星提供的代码,不开源,是机器码
u-boot.bin : u-boot源码编译生成的,开源
参考如下图片
主版本号.次版本号.修订版本号
主版本号:内核源码有较大的改动才会更新主版本号
次版本号:修订版本号用完升级次版本号
偶数:稳定版
奇数:测试版
修订版本号:内核代码只要有更新就会修改修订版本号
通过菜单图形化界面的方式,
去掉研发中心添加的外设驱动代码
Device Drivers --->
Character devices --->
[ ] FS6818 beep driver 去掉*
FS6818 board device driver support --->
[ ] adc driver for farsight FS6818 all platform
[ ] pwm timer driver for farsight FS6818
[ ] DS18B20 driver for farsight FS6818
FS6818 extension device driver support --->
< > This is FS6818_LED!
[ ] zlg7290 driver support input device
enter是选择键
两次esc是退出键
?是帮助;/是搜索
y是包含,n是不包含,m是模块化
[*]编译到uImage中,[]不编译,<M>是模块化编译,<>不模块化编译
有箭头则可以用enter进入下一级菜单
5》保存退出,编译内核源码
time make -j4 uImage
time: 回显编译的时间
问题1:make menuconfig
第一次使用make menuconfig 需要安装图形化界面的工具
配置之前需要安装图形图(make meuconfig):
sudo apt-get install libncurses5-dev
sudo apt-get install lib32z1 (64位)
问题2 :make menuconfig
scripts/kconfig/mconf Kconfig
Your display is too small to run Menuconfig!
It must be at least 19 lines by 80 columns.
make[1]: *** [menuconfig] Error 1
make: *** [menuconfig] Error 2
终端字体太大,缩小终端字体
问题3:make uImage
在编译的过程中可能出现如下错误:
"mkimage" command not found - U-Boot images will not be built
make[1]: *** [arch/arm/boot/uImage] Error 1
make: *** [uImage] Error 2
错误的原因:找不到mkimage命令,
根据提示分析出来mkimage应该存在uboot源码目录中
uboot源码必须进行编译之后才会有mkimage可执行程序
解决问题的方法:
将uboot源码的tools目录下的mkimage,
拷贝到到ubuntu的/usr/bin目录下:
sudo cp ~/uboot源码目录/tools/mkimage /usr/bin
uboot源码目录 ubuntu目录
再次make uImage重新编译内核即可。
6》 编译成功之后,测试uImage镜像是否可以使用
打印以下信息表示编译成功:
Image Name: Linux-3.4.39-farsight
Created: Mon Jul 20 15:09:28 2020
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 5391608 Bytes = 5265.24 kB = 5.14 MB
Load Address: 40008000
Entry Point: 40008000
uImage内核镜像在以下目录中:
Image arch/arm/boot/uImage is ready
拷贝uImage到tftpboot目录下
切换开发板的启动方式为开发阶段系统的部署方式
启动linux系统
"make ${PLATFORM}_defconfig"
Create a ./.config file by using the default
symbol values from
arch/$ARCH/configs/${PLATFORM}_defconfig.
Use "make help" to get a list of all available
platforms of your architecture.
make fs6818_defconfig命令根据arch/arm/configs/目录下的缺省配置文件fs6818_defconfig,在内核源码的顶层目录下生成.config配置文件。
491 %config: scripts_basic outputmakefile FORCE
492 $(Q)mkdir -p include/linux include/config
493 $(Q)$(MAKE) $(build)=scripts/kconfig $@
解析:
$(Q) --> @ 取消在终端的显示
$@ --> 目标
$^ --> 所有的依赖
$< --> 第一个依赖
去掉命令前边的$(Q),重新执行make fs6818_defconfig,
得到以下执行结果:
mkdir -p include/linux include/config
//进入scripts/kconfig目录下继续执行make fs6818_defconfig
make -f scripts/Makefile.build obj=scripts/kconfig fs6818_defconfig
$(MAKE) --> make -f
$(build) --> scripts/Makefile.build obj
-f : 将-f后边的文件当成一个Makefile文件
obj=scripts/kconfig :执行make时给obj变量赋值
//conf可执行程序根据arch/arm/configs目录下的默认的缺省配置文件fs6818_defconfig和内核源码顶层目录下的Kconfig文件,在内核源码顶层目录下生成.config文件。
95 %_defconfig: $(obj)/conf
96 $(Q)$< --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
解析:
$(obj) --> scripts/kconfig
$(Q) --> @
$< --> scripts/kconfig/conf
$(SRCARCH) --> arm
$@ --> fs6818_defconfig
$(Kconfig) --> Kconfig
去掉命令前边的$(Q),重新执行make fs6818_defconfig
scripts/kconfig/conf --defconfig=arch/arm/configs/fs6818_defconfig Kconfig
conf文件:
通过file conf命令可知,conf是一个可执行文件。
fs6818_defconfig文件:
缺省的fs6818的配置文件
Kconfig文件:
内核源码顶层目录下的Kconfig文件
可以同时打开.config和fs6818_defconfig文件进行对比,一模一样
491 %config: scripts_basic outputmakefile FORCE
492 $(Q)mkdir -p include/linux include/config
493 $(Q)$(MAKE) $(build)=scripts/kconfig $@
解析:
$(Q) --> @
$@ --> 目标
$^ --> 所有的依赖
$< --> 第一个依赖
去掉命令前边的$(Q),重新执行make menuconfig,
得到以下执行结果:
mkdir -p include/linux include/config
make -f scripts/Makefile.build obj=scripts/kconfig menuconfig
$(MAKE) --> make -f
$(build) --> scripts/Makefile.build obj
-f : 将-f后边的文件当成一个Makefile文件
obj=scripts/kconfig :执行make时给obj变量赋值
进入scripts/kconfig目录下继续执行make menuconfig
20 menuconfig: $(obj)/mconf
21 $< $(Kconfig)
解析:
$(obj)/mconf --> scripts/kconfig/mconf
$< --> scripts/kconfig/mconf
$(Kconfig) --> Kconfig
解析命令的结果:
scripts/kconfig/mconf Kconfig
根据file mconf命令可知,mconf是一个可执行程序,
mconf会调用ncurses库中的接口函数,根据Kconfig
文件中的菜单选项的配置信息,生成图形化界面的
菜单选项。
ncurses : 这个库是一个图形化界面的库
5 mainmenu "Linux/$ARCH $KERNELVERSION Kernel Configuration"
6
7 config SRCARCH
8 string
9 option env="SRCARCH"
10
11 source "arch/$SRCARCH/Kconfig"
mainmenu : 修饰主菜单
语法格式:
mainmenu "主菜单的名字"
参考下图
APP (led灯的应用程序:fs6818led_test.c)
(代码逻辑)
--------------------接口----------
kernel (led灯驱动:fs6818_led.c)
(代码机制)
hal 硬件LED灯
Device Drivers --->
Character devices --->
[ ] FS6818 beep driver 去掉*
FS6818 board device driver support --->
[ ] adc driver for farsight FS6818 all platform
[ ] pwm timer driver for farsight FS6818
[ ] DS18B20 driver for farsight FS6818
FS6818 extension device driver support --->
< > This is FS6818_LED!
[ ] zlg7290 driver support input device
拷贝led灯的驱动代码fs6818_led.c和fs6818_led.h文件到内核的drivers/char目录下
修改drivers/char目录下的Kconfig文件,添加菜单选项。添加的内容如下
在这个的下边menu "Character devices",
添加一下信息:
config HQYJ_LED_DRIVER
bool "20010-20011 led driver"
default y
help
20010-20011 led driver test!
修改drivers/char目录下的Makefile,添加一下信息:
obj-$(CONFIG_HQYJ_LED_DRIVER) += fs6818_led.o
执行make menuconfig, 查看效果
可以通过图形化界面的方式进行配置,
查看.config文件中的配置信息。
执行make uImage编译源码,并进行测试
1> 出现以下信息表示led灯驱动被编译到uImage中
CC drivers/char/fs6818_led.o
2> 拷贝uImage到~/tftpboot目录下
cp arch/arm/boot/uImage ~/tftpboot
3> 重启开发板,以开放阶段系统部署的方式启动linux系统,及通过tftp下载uImage,nfs挂载根文件系统
4> 使用交叉编译工具链编译led灯的应用程序,并将可执行文件拷贝到根文件系统中拷贝day04/led-driver文件夹到ubuntu中,
进入到led-driver: cd led-driver
编译:
arm-none-linux-gnueabi-gcc fs6818led_test.c -o led_test
拷贝led_test到根文件系统中:
cp led_test ~/nfs/rootfs
bool --> []
[*] --> 对应的驱动被编译到uImage中
[ ] --> 对应的驱动不被编译
tristate --> <> 三态
<*> --> 对应的驱动被编译到uImage中
< > --> 对应的驱动不被编译
<M> --> 对应的驱动采用模块化的方式进行编译,使用make modules命令编译模块化的驱动程序,将***.c编译生成***.ko
模块化的驱动程序如何使用(***.ko)?
需要使用驱动时,通过insmod命令加载***.ko文件加载到内核中,
不需要使用驱动时,可以通过rmmod命令,将驱动文件从内核中进行卸载。
Device Drivers --->
Character devices --->
[ ] FS6818 beep driver 去掉*
FS6818 board device driver support --->
[ ] adc driver for farsight FS6818 all platform
[ ] pwm timer driver for farsight FS6818
[ ] DS18B20 driver for farsight FS6818
FS6818 extension device driver support --->
< > This is FS6818_LED!
[ ] zlg7290 driver support input device
拷贝led灯的驱动代码fs6818_led.c和fs6818_led.h
文件到内核的drivers/char目录下
修改drivers/char目录下的Kconfig文件,添加菜单选项。添加的内容如下
在这个的下边menu "Character devices",
添加一下信息:
config HQYJ_LED_DRIVER
tristate "20010-20011 led driver"
default y
help
20010-20011 led driver test!
Device Drivers --->
Character devices --->
<M> 20010-20011 led driver (配置为M)
查看.config文件中的配置信息:
CONFIG_HQYJ_LED_DRIVER=m :表示采用模块化的方式编译驱动
可以使用搜索的功能,搜索CONFIG_后边的内容
目的:去掉上一次编译内核源码是uImage中的led灯驱动
拷贝新生成的uImage到~/tftpboot目录下.
cp arch/arm/boot/uImage ~/tftpboot
出现以下信息表示编译成功:
LD [M] drivers/char/fs6818_led.ko
1. 缩小uImage内核镜像的体积
2. 提高linux驱动开发的效率
3. 提高排错的能力
在实际linux驱动开发中,都是采用模块化的方式进行编译
insmod 加载驱动
lsmod 查看驱动
rmmod 删除驱动
mknod 在/dev创建设备节点
mknod /dev/设备文件名 c/b 主设备号 次设备号
解析:
/dev/设备文件名 -->设备文件名
c -->字符设备文件
b -->块设备文件
主设备号 : 0-2^12
次设备号 : 0-2^20
1. printk
2. kgdb
3. addr2line (GNU中的工具)
实验13:内核调试.docx
linux内核启动过程主要干了那些事?
1> uboot通过tftp命令将uImage下载到内存中
2> uImage需要完成自解压
3> 获取cpu的ID号,创建页表,
初始化MMU,完成物理地址到虚拟地址的映射
4> 清除BSS段
5> 完成绝大多数硬件的初始化,进一步对硬件初始化
内存,时钟,串口,EMMC,nfs客户端…
5> 从u-boot环境变量的内存分区获取bootargs参数,
根据bootargs参数,决定从哪里挂载根文件系统。
6> 挂载根文件系统,
7> 执行根文件系统中的1号进程,linuxrc程序
8> 到此开发板的linux系统启动成功
根文件系统(rootfs):系统运行所必须依赖的一些文件(比如脚本、库、配置文件、命令…),本质就是目录和文件。
根文件系统镜像(ramdisk.img):将根文件系统按照某种格式进行打包压缩后生成的单个文件 rootfs-----> ramdisk.img
文件系统:一种管理和访问磁盘的软件机制,不同文件系统管理和访问磁盘的机制不同
https://busybox.net/downloads/
选择busybox-1.33.0.tar.bz2版本
注释:各文件功能解析
busybox生成的目录有:bin,sbin,usr,linuxrc
其他的目录都是手动添加
bin: 命令文件,如ls,cd等(通过busybox工具制作)
dev: 设备文件(被操作系统识别的设备才有对应的文件,即设备运行时)
etc: 配置文件,如bashrc,environment等(配置内核相关的一些信息)
lib: 库文件、比如C的标准库(从交叉编译工具链中复制的)
linuxrc:根文件系统被挂载后运行的第一个程序(通过busybox工具制作)
mnt: 共享目录(非必要)比如挂载SD卡等时将SD卡挂载在该目录
proc: 与进程相关的文件(当有进程运行时才会有文件)
root: 用户权限(板子本身就是以root用户运行)
sbin: 超级用户命令、一般用户不可用(板子本身是超级用户 通过busybox工具制作)
sys: 系统文件(系统运行时,系统加载后才会有文件)
tmp: 临时文件(比如插入新的设备时会产生临时文件)
usr: 用户文件(通过busybox工具制作)
var: 存放下载的文件和软件 (可有可无)
home: 家目录
uboot源码,linux内核编译使用4.5.1版本的交叉编译工具链
根文件系统busybox使用4.6.4版本的交叉编译工具链
注意:编译源码之前先使用命令测试交叉编译工具链的版本,
如果版本不对进行切换。
arm-none-linux-gnueabi-gcc -v
清除中间目标:
clean
distclean
编译:
all
配置:
menuconfig
安装:
install
uninstall
修改以下几个内容:
164 CROSS_COMPILE ?=
190 ARCH ?= $(SUBARCH)
修改为:
164 CROSS_COMPILE ?= arm-none-linux-gnueabi-
190 ARCH ?= arm
2> 清除中间文件
make distclean
3> 使用图形化界面的方式对busybox进行配置
make menuconfig
问题: 分析make menuconfig执行过程详解?
a) 采用静态编译,不使用共享库
Settings --->
[*] Build static binary (no shared libs)
b) 采用vi风格的命令行编译
Settings --->
[*] vi-style line editing commands
c) 设置根文件系统的安装路径
Settings --->
(./_install) Destination path for 'make install' (NEW)
配置为:
(/home/hqyj/rootfs) Destination path for 'make install'
注意:每个人的家目录可能都不一样,修改为自己的。
d) 配置支持所有的模块化的命令
Linux Module Utilities --->
[ ] Simplified modutils
4> 编译busybox源码
make all
5> 安装
make install
根文件系统中的很多文件散落在busybox的各个目录下,
可以通过make install命令将散落在buxybox目录的的各个文件,
重定向到rootfs文件中的的各个目录下。
6> 测试自己新生成的rootfs根文件系统,将之前提供的可以使用的rootfs根文件系统进行备份
cd ~/nfs
mv rootfs rootfs-ok
移动自己生产的rootfs到nfs目录下
mv ../rootfs ./
7> 启动开发板,使用开发阶段的方式启动开发板
tftp下载uImage nfs挂载根文件系统
检查:bootcmd和bootargs两个参数。
内核启动成功,并且成功挂载了根文件系统,
只是有很多的错误信息,错误信息如下:
can't run '/etc/init.d/rcS': No such file or directory
can't open /dev/tty2: No such file or directory
can't open /dev/tty3: No such file or directory
can't open /dev/tty4: No such file or directory
解决思路:<没有文件则创建对应的文件>
在根文件系统中创建/etc/init.d目录和/dev目录,
并在/etc/init.d目录下添加rcS文件,修改rcS的文件权限为777.
$ cd ~/nfs/rootfs
$ mkdir -p etc/init.d dev
$ cd etc/init.d
$ touch rcS
$ chmod 777 rcS
打开rcS文件,添加一下信息:
#!/bin/sh
/bin/mount -a
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev -s
解析:
#!/bin/sh ---> 使用sh脚本解析器
/bin/mount -a ---> 系统会自动解析fstab配置文件,根据fstab文件中的信息,实现一系列的挂载动作。
echo /sbin/mdev > /proc/sys/kernel/hotplug ---->进行重定向,告诉内核用于创建设备文件的程序是sbin/mdev
/sbin/mdev -s ---->在dev目录下自动创建设备文件
8> 启动开饭,继续观察现场
出现以下错误信息:
mount: can't read '/etc/fstab': No such file or directory
解决办法:
在etc/目录下创建fstab文件,
$ cd ~/nfs/rootfs/etc
$ touch fstab
打开fstab文件,并添加一下信息:
#device mount-point type options dump fsck orde
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
解析:
第1列: 设备
第2列: 挂载点
第3列: 设备类型
其他列:权限相关,保持默认即可。
/etc/init.d/rcS: line 3: can't create /proc/sys/kernel/hotplug: nonexistent directory
解决办法: 在根文件系统中创建proc目录
$ cd ~/nfs/rootfs
$ mkdir proc
mdev: /sys/dev: No such file or directory
解决办法: 在根文件系统中创建sys目录
$ cd ~/nfs/rootfs
$ mkdir sys
9> 重启开发,观察现象
出现以下错误信息:
mount: mounting tmpfs on /tmp failed: No such file or directory
解决办法: 在根文件系统中创建tmp目录
$ cd ~/nfs/rootfs
$ mkdir tmp
10> 重启开发,观察现象
出现以下错误信息:
can't open /dev/tty2: No such file or directory
can't open /dev/tty3: No such file or directory
can't open /dev/tty4: No such file or directory
解决办法:
在根文件系统内的etc目录下创建inittab文件,
$ cd ~/nfs/rootfs/etc
$ touch inittab
打开inittab文件,并添加以下信息:
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
解析:
::sysinit:/etc/init.d/rcS ---》 系统启动之后运行linuxrc程序,执行/etc/init.d/rcS脚本文件。
::askfirst:-/bin/sh ---》按下任意键之后,开启一个shell中断
::restart:/sbin/init ---》重启系统自动执行/sbin/init程序
::ctrlaltdel:/sbin/reboot ---》 按下ctrl + alt + del 执行/sbin/rebot,重启系统
11> 重启开发板,观察现象
到此根文件系统制作,基本完成,
创建剩余的几个文件
$ cd ~/nfs/rootfs
$ mkdir lib home mnt root var
12> 添加用户名:
在根文件系统的etc目录下创建profile文件,
并添加以下信息:
$ cd ~/nfs/rootfs/etc
$ vi profile
添加的信息如下:
export HOSTNAME=20101-20111
export USER=root
export HOME=root
#export PS1="\[\u@\h \W\ ]\$ "
#cd root
export PS1="[$USER@$HOSTNAME \W]\# "
PATH=/bin:/sbin:/usr/bin:/usr/sbin:$PATH
LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
export PATH LD_LIBRARY_PATH
13> 编写一个hello world应用程序,使用交叉编译工具链进行编译,
将可执行程序拷贝到根文件系统中,并运行,观察现象。
[root@20101-20111 ]# ./hello
运行时,出现以下错误信息:
-/bin/sh: ./hello: not found
使用以下命令查看hello依赖的动态库:
$ arm-none-linux-gnueabi-readelf -d hello
0x00000001 (NEEDED) Shared library: [libc.so.6]
解决办法:移植共享库
从交叉编译工具链中移植arm版本的共享库到根文件系统的lib目录下。
$ cd ~/toolchain/gcc-4.6.4
$ find . -name libc.so.6
./arm-arm1176jzfssf-linux-gnueabi/sysroot/lib/libc.so.6
将交叉编译工具量./arm-arm1176jzfssf-linux-gnueabi/sysroot/lib/
目录下的所有的库拷贝到根文件系统的lib目录下
$ cd ./arm-arm1176jzfssf-linux-gnueabi/sysroot/lib/
$ cp -raf * ~/nfs/rootfs/lib/
再次运行hello可执行程序,运行成功。
14> 测试linux驱动模块化相关的命令
拷贝fs6818_led.ko到根文件系统中
拷贝led_test到根文件系统中,
以上几个文件在讲解linux内核的模块化编程时,
进行讲解生成的。
【将rootfs打包生成ramdisk.img镜像文件】
根文件系统的制作实验手册.docx