随着工程复杂度 愈来愈高, 移植 操作系统 避不开的问题
一个操作系统 可以给 编程提供 很大的便利: 极大的发挥硬件性能
1. 进程管理 多任务 并发 使得程序设计更加模块化 结构化
2. 内存管理 堆栈管理 动态内存分配
3. 进程线程间 提供通信 实现消息传递,同步互斥
Linux:
4. 内核安全机制
5. 硬件管理 驱动管理
系统移植课程大纲:
分3个大步骤:
BootLoader: 开机引导程序 是一段裸机程序, 用于引导操作系统 例如:PC 中的BIOS
系统内核: 系统核心组成部件,通常不能缺少
根文件系统: 用于存放用户程序, 管理用户文件, 或系统配置
应用程序移植: 库,应用程序 等的移植 在项目阶段学习
系统与内核:
操作系统,
包含内核部分,以及 相关工具,软件,库,甚至有界面 是一个完整的 系统
内核:
仅有系统的 核心组成部件, 通常包括 进程管理 内存管理 设备管理
没有任何 应用程序以及库等
系统的启动流程:
上电, 1. BootLoader启动: 初始化CPU 初始化一些必要的外设 引导系统内核 (搬移内核代码到指定内存中)
2. 内核启动 : 再次初始化CPU 初始化外设 以及各种设备 挂载根文件系统
3. 根文件系统 执行用户应用程序
环境搭建:
资料下载
1. 交叉编译工具 arm-none-linux-gnueabi-gcc
2. 文件的下载 使用网络 tftp下载
1) 搭建 TFTP 服务器 在 Ubuntu上
2) 搭建 NFS 服务器 在 Ubuntu上
服务器的搭建 需要外网支持
1. 安装TFTP 服务器 用于上传或下载文件
sudo apt-get install tftpd-hpa // 安装服务器
sudo apt-get install tftp // 安装客户端
查看安装是否成功 dpkg -l | grep tftp
ii tftp 0.17-18ubuntu2 amd64 Trivial file transfer protocol client
ii tftpd-hpa 5.2+20150808-1ubuntu1.16.04.1 amd64 HPA's tftp server
2. 配置服务器
1) 准备 tftp工作目录
mkdir ~/tftpboot
2) 修改工作目录权限
chmod 0777 ~/tftpboot
3) 修改配置文件
sudo vim /etc/default/tftpd-hpa
参考如下配置:
# /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
# TFTP_DIRECTORY="/var/lib/tftpboot" # 改工作目录 为刚刚创建的
TFTP_DIRECTORY="/home/xwq/tftpboot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure -c" # 添加-c 选项 可以允许客户端上传文件
4) 重启服务 使配置生效
sudo /etc/init.d/tftpd-hpa restart
[ ok ] Restarting tftpd-hpa (via systemctl): tftpd-hpa.service.
5) 查看tftp服务器启动成功否
ps -aux | grep tftp
root 3363 0.0 0.0 15180 148 ? Ss 10:17 0:00 /usr/sbin/in.tftpd --listen --user tftp --address :69 --secure -c /home/xwq/tftpboot
搭建NFS服务器: 主要用于文件共享的 主要是通过nfs共享的方式 来实现 网络挂载 根文件系统
1) 安装nfs服务器
sudo apt-get install nfs-kernel-server
2) 准备 nfs工作目录 nfshome
mkdir ~/nfshome
3) 修改工作目录权限
chmod 0777 ~/nfshome
4) 修改配置文件
sudo vim /etc/exports
参考如下配置: 添加一行
/home/xwq/nfshome *(rw,sync,no_subtree_check)
工作路径 * 任意IP都可以访问 rw 可读可写 权限
5) 重启nfs服务器
sudo /etc/init.d/nfs-kernel-server restart
[ ok ] Restarting nfs-kernel-server (via systemctl): nfs-kernel-server.service.
6) 测试NFS 服务
挂载服务器上的 目录 到本地位置
sudo mount 192.168.124.101:/home/xwq/nfshome /mnt/nfs
挂载 服务器IP | |-> 挂载到本地路径 /mnt/nfs
|-> 工作目录 /home/xwq/nfshome
卸载 sudo umount nfs/
问题: nfs 服务器版本过高 导致开发板无法挂载
在nfs配置文件末尾加入一句:
RPCNFSDOPTS="--nfs-version 2,3,4 --debug --syslog"
系统的启动方式:
开发者阶段: 网络启动
1. 启动 板载uboot
通过拨码开关 确定是从flash(emmc)启动 还是从 sd卡启动
2. 通过uboot 下载 Linux内核 到内存中
3. 通过uboot 下载 设备树 到内存中
4. 通过nfs 挂载 根文件系统
系统固化阶段:
1. 下载并烧录 uboot
2. 下载并烧录 内核
3, 下载并烧录 设备树
4, 固化 根文件系统
产品阶段: 脱机运行
1. 启动uboot
2. uboot 加载 内核到内存 从flash或sd卡
3. uboot 加载 设备树
4. 加载根文件系统
开发者阶段:
线路连接:
1. 串口线 串口输出 调试信息 或进行 与开发板交互
2. 网线
方式1) 教学用 使用交换机机 将 开发板 PC 以及 教室路由器 连一起
PC 即可以访问 开发板 开发板 也可以访问PC
同时PC 机 可以通过 路由器访问外网和局域网
开发板IP 与 教室在一个局域网
方式2) 将开发板 与 PC 直接通过一根 网线 连接
PC(Ubuntu) 通过有线网卡 访问开发板
PC 机使用 无线网卡 访问 外网或屏幕共享
Linux使用的虚拟网卡 : 通过桥接方式 连接物理网卡
若 Linux虚拟机访问 开发板 就需要桥接到 接物理有线网卡
若 Linux虚拟机访问 外网 就需要桥接到 接物理无线网卡
Ubuntu 虚拟机ip配置问题:
参考 设置虚拟机使用指定网卡.doc
1. 网卡桥接, 参考 配置文档设置
2. 目的 实现 虚拟机Ubuntu 与开发板 通过 网线 TCP/IP 协议通信
推荐设置: Ubuntu 使用固定IP 192.168.3.200
子网掩码 255.255.255.0
网关 192.168.3.1
开发板IP: 192.168.3.201
子网掩码 255.255.255.0
网关 192.168.3.1
开发板IP配置:
uboot相关命令:
1. pri 命令 打印环境变量
printenv
FS4412[general] # pri
baudrate=115200
bootargs=root=/dev/nfs nfsroot=192.168.124.150:/home/xwq/nfshome/rootfs rw conso
le=ttySAC2,115200 init=linuxrc ip=192.168.124.250
bootcmd=loadb 0x40008000;go 0x40008000
bootdelay=1 // 自启动倒计时
ethact=dm9000
ethaddr=11:22:33:44:55:66
fileaddr=41000000
filesize=2E3EF0
gatewayip=192.168.124.1 // 网关
ipaddr=192.168.124.250 // 开发板IP地址
netmask=255.255.255.0 // 子网掩码
serverip=192.168.124.150 // 服务器ip 即 Ubuntu系统IP
stderr=serial
stdin=serial
stdout=serial
Environment size: 437/16380 bytes
2. 修改环境变量
set gatewayip 192.168.3.1
set ipaddr 192.168.3.201
set netmask 255.255.255.0
set serverip 192.168.3.200
save
3. 删除环境变量
set 环境变量名
开发板验证 是否能够通过网络访问 Ubuntu
ping 192.168.3.200
dm9000 i/o: 0x5000000, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 11:22:33:44:55:66
operating at 100M full duplex mode
Using dm9000 device
host 192.168.3.200 is alive
FS4412[general] #
3. 准备 需要的文件 放到到tftp 与 nfs 在Ubuntu上操作
1) uImage 复制到 tftpboot目录中
2) 设备树文件 exynos4412-fs4412.dtb 到 tftpboot目录中
3) 复制根文件系统 rootfs.tar.xz 到 nfshom目录中
4) 解压根文件系统
tar -xvf rootfs.tar.xz
5) 修改rootfs目录权限
chmod 0777 rootfs
4. 通过网络 TFTP 下载uImage 和 设备树 通过 网络 nfs 挂载 根文件系统 测试
1) 在开发板上操作
设置 bootargs参数 : 该参数用于 给内核提供启动参数
set bootargs root=/dev/nfs nfsroot=192.168.124.150:/home/xwq/nfshome/rootfs rw console=ttySAC2,115200 init=linuxrc ip=192.168.124.250
参数的含义:
root=/dev/nfs : 表示根文件系统 使用nfs访问挂载 对应修改
nfsroot=192.168.124.150 : 表示根文件系统的 服务器IP 对应修改
/home/xwq/nfshome/rootfs : 表示根文件系统的 服务器路径 对应修改
rw : 权限 可读可写
console=ttySAC2,115200 : 调试串口为 ttySAC2 波特率 115200
init=linuxrc : 初始化进程 为 linuxrc
ip=192.168.124.250 : 开发板IP 开发板Linux中的 对应修改
set bootargs root=/dev/nfs nfsroot=192.168.3.200:/home/xwq/nfshome/rootfs rw console=ttySAC2,115200 init=linuxrc ip=192.168.3.201
save
2) 下载 uImage : tftp 41000000 uImage
使用TFTP协议 从服务器(serverip) 上下载 文件 uImage 存放到 内存地址 41000000 位置
FS4412[general] # tftp 41000000 uImage
dm9000 i/o: 0x5000000, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 11:22:33:44:55:66
operating at 100M full duplex mode
Using dm9000 device
TFTP from server 192.168.3.200; our IP address is 192.168.3.201
Filename 'uImage'.
Load address: 0x41000000
Loading: #################################################################
#################################################################
#################################################################
############
867.2 KiB/s
done
Bytes transferred = 3028040 (2e3448 hex)
3) 下载 设备树 exynos4412-fs4412.dtb :
tftp 42000000 exynos4412-fs4412.dtb
FS4412[general] # tftp 42000000 exynos4412-fs4412.dtb
dm9000 i/o: 0x5000000, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 11:22:33:44:55:66
operating at 100M full duplex mode
Using dm9000 device
TFTP from server 192.168.3.200; our IP address is 192.168.3.201
Filename 'exynos4412-fs4412.dtb'.
Load address: 0x42000000
Loading: ###
780.3 KiB/s
done
Bytes transferred = 34358 (8636 hex)
5. 加载内核 设备树 启动系统
bootm 41000000 - 42000000
参数说明:
boom 引导内核的uboot命令
41000000 : uImage 内核镜像的内存位置
- : 根文件系统 的位置 - 表示根文件系统不在内存
42000000 : 设备树 的 内存位置
系统启动:
设置uboot 自动下载 uImage 设备树 以及 自动启动系统
set bootcmd tftp 41000000 uImage\;tftp 42000000 exynos4412-fs4412.dtb\;bootm 41000000 - 42000000
save
开发者阶段下: uboot环境变量
baudrate=115200
bootargs=root=/dev/nfs nfsroot=192.168.3.200:/home/xwq/nfshome/rootfs rw console=ttySAC2,115200 init=linuxrc ip=192.168.3.201
bootcmd=tftp 41000000 uImage;tftp 42000000 exynos4412-fs4412.dtb;bootm 41000000 - 42000000
bootdelay=1
ethact=dm9000
ethaddr=11:22:33:44:55:66
fileaddr=41000000
filesize=2E3EF0
gatewayip=192.168.3.1
ipaddr=192.168.3.201
netmask=255.255.255.0
serverip=192.168.3.200
stderr=serial
stdin=serial
stdout=serial
Environment size: 479/16380 bytes
uboot移植:
BootLoader: 用于加载内核 引导操作系统的 一段裸机程序, 开机最先运行
uboot 通用的 Linux 引导程序, 支持多种芯片架构, 使用者众多
uboot启动打印:
U-Boot 2013.01.general (Sep 24 2019 - 11:31:49) for FS4412
CPU: Exynos4412@1000MHz
Board: FS4412
DRAM: 1 GiB
WARNING: Caches not enabled
MMC: MMC0: 3728 MB
In: serial
Out: serial
Err: serial
MMC read: dev # 0, block # 48, count 16 ...16 blocks read: OK
eMMC CLOSE Success.!!
Checking Boot Mode ... EMMC4.41
Net: dm9000
Hit any key to stop autoboot: 0
uboot 工作模式:
1. 自启动模式 倒计时没有操作, 自动执行bootcmd变量中的命令
2. 交互模式 倒计时输入任何按键, 进入交互模式 可以手动输入命令执行
uboot命令:
pri : 打印环境变量
set : 设置环境变量
save : 保存环境变量
bootm : 引导操作系统
tftp : 下载文件命令 前提 IP配置,网络OK,服务器ok
tftp addr fileName
ping : 检查网络连接
loadb : 串口下载 使用Kermit协议
loadb addr
go : 跳转到指定内存运行
go addr
mmcinfo : 查看uboot支持的flash信息
movi: 命令用于 写入或读取mmc分区(flash或SD卡)中的 数据
uboot mmc分区: FS4412[general] 多用于系统固化时
1. uboot 分区 存储uboot本身
2. kernel 分区 存储uImage 4M
3. dtb 分区 存储dtb文件 1M
4. rootfs 分区 存储根文件系统镜像 10M
movi read 分区名 addr <根文件系统需要 size>
从分区中 读数据 到内存 addr
movi write 分区名 addr <根文件系统需要 size>
写入数据 到分区中 从addr内存除获取数据
uboot源码目录: u-boot-2013.01.tar.bz2
解压到 ~/sys_230901/ 目录
arch: 体系架构相关的 不同架构的CPU 初始化代码不同
Exynos4412 arch/arm/cpu/armv7
board: 开发板 相关代码 厂商提供的 一些 标准板子的 配置 以及相关代码
common: 通用的代码, 与体系架构和开发板 无关
//.... 参考图片
uboot 运行过程: 走读程序:
1. 第一行代码在哪?
汇编部分: 开始时 uboot 运行在flash中
b reset 复位异常入口
bl cpu_init_cp15: 初始化协处理器
bl cpu_init_crit:基本硬件初始化
b lowlevel_init
// 关闭看门狗,关闭中断,初始时钟。
lowlevel_init: //在文件 u-boot-2013.01/board/samsung/origen/lowlevel_init.S
system_clock_init 系统时钟初始化
mem_ctrl_asm_init 内存初始化
srom_ctrl_init 存储器初始化
代码运行位置检查
若运行在flash中 会进行 relocate_code 代码自搬移
内存自 搬移 : b relocate_code 为了提高运行速度
拷贝uboot代码 到RAM中
跳转 RAM中的 uboot运行
bl _main: //在文件 arch/arm/lib/crt0.S
bl board_init_f: C代码, 在arch/arm/lib/board.c 中
b board_init_r: C代码, 在arch/arm/lib/board.c 中
总结汇编部分: 1, 初始化基本硬件 关闭不必要的功能 单纯化
2, 代码自搬移 提高运行速度
C语言部分: 1. 大部分硬件初始化 准备引导操作系统需要的环境
2, 若有按键输入 进入交互模式
arch/arm/lib/board.c
board_init_f:
核心代码: 遍历函数指针数组,依次调用其中的函数
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
循环调用初始化函数, CPU特殊功能初始化、内存池、定时器、环境变量、串口、等设备初始化
board_init_r: 进入交互模式
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop(); // 主循环
}
uboot移植操作: 参考实验手册
ifeq (arm,$(ARCH))
CROSS_COMPILE ?= arm-none-linux-gnueabi-
endif
#if 1
ldr r0, =0x11000c40 @GPK2_7 led2
ldr r1, [r0]
bic r1, r1, #0xf0000000
orr r1, r1, #0x10000000
str r1, [r0]
ldr r0, =0x11000c44
mov r1,#0xff
str r1, [r0]
#endif
@#./mkuboot
@split -b 14336 u-boot.bin bl2
@+make -C sdfuse_q/
@#cp u-boot.bin u-boot-4212.bin
@#./sdfuse_q/add_sign
@./sdfuse_q/chksum
@./sdfuse_q/add_padding
@rm bl2a*
@echo
cat E4412_N.bl1.SCP2G.bin bl2.bin all00_padding.bin u-boot.bin tzsw_SMDK4412_SCP_2GB.bin > u-boot-fs4412.bin
烧写uboot 到SD卡:
1) 将要烧写的 u-boot-4212.bin 文件 复制到 sdfuse_q 目录中
2) 将 sd连接到 Ubuntu 系统中
3) 查看 连接成功否
sudo fdisk -l
Disk /dev/sda: 40 GiB, 42949672960 bytes, 83886080 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x0c1d422e
设备 启动 Start 末尾 扇区 Size Id 类型
/dev/sda1 * 2048 75493375 75491328 36G 83 Linux
/dev/sda2 75493376 83886079 8392704 4G 5 扩展
/dev/sda5 75495424 83884031 8388608 4G 83 Linux
Disk /dev/sdb: 7.4 GiB, 7948206080 bytes, 15523840 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x45db6c3e
设备 启动 Start 末尾 扇区 Size Id 类型
/dev/sdb1 2048 15523839 15521792 7.4G 83 Linux
4) 使用工具 烧录 uboot到 sd卡 在sdfuse_q 中
sudo ./sd_fusing_exynos4x12.sh /dev/sdb ./u-boot-fs4412.bin
烧录脚本 SD卡设备 要烧录的uboot文件
/dev/sdb reader is identified.
./u-boot-fs4412.bin fusing...
记录了1029+1 的读入
记录了1029+1 的写出
527104 bytes (527 kB, 515 KiB) copied, 2.48152 s, 212 kB/s
./u-boot-fs4412.bin image has been fused successfully.
Eject SD card
xwq@xwq-pc:~/sys_230901/u-boot-2013.01/sdfuse_q$
5) 将sd卡插入开发板 将拨码开关拨动到 SD卡启动
上电: 可以看到 LED2 亮 了 串口没有反应(该实验的效果就这样)
串口输出:
#if 1 /*for close watchdog */
/* PS-Hold high */
ldr r0, =0x1002330c
ldr r1, [r0]
orr r1, r1, #0x300
str r1, [r0]
ldr r0, =0x11000c08
ldr r1, =0x0
str r1, [r0]
/* Clear MASK_WDT_RESET_REQUEST */
ldr r0, =0x1002040c
ldr r1, =0x00
str r1, [r0]
#endif
ldr r0, =0x10030000
ldr r1, =0x666666
ldr r2, =CLK_SRC_PERIL0_OFFSET
str r1, [r0, r2]
ldr r1, =0x777777
ldr r2, =CLK_DIV_PERIL0_OFFSET
str r1, [r0, r2]
U-Boot 2013.01 (Dec 15 2023 - 16:09:18) for xwq fs4412
CPU: Exynos4412@1000MHz
Board: ORIGEN
DRAM: 1 GiB
WARNING: Caches not enabled
MMC: SAMSUNG SDHCI: 0
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
Hit any key to stop autoboot: 0
XWQ #
XWQ #
Ubuntu网卡驱动移植:
参考实验手册
#ifdef CONFIG_DRIVER_DM9000
#define EXYNOS4412_SROMC_BASE 0X12570000
#define DM9000_Tacs (0x1)
#define DM9000_Tcos (0x1)
#define DM9000_Tacc (0x5)
#define DM9000_Tcoh (0x1)
#define DM9000_Tah (0xC)
#define DM9000_Tacp (0x9)
#define DM9000_PMC (0x1)
struct exynos_sromc {
unsigned int bw;
unsigned int bc[6];
};
/*
* s5p_config_sromc() - select the proper SROMC Bank and configure the
* band width control and bank control registers
* srom_bank - SROM
* srom_bw_conf - SMC Band witdh reg configuration value
* srom_bc_conf - SMC Bank Control reg configuration value
*/
void exynos_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf)
{
unsigned int tmp;
struct exynos_sromc *srom = (struct exynos_sromc *)(EXYNOS4412_SROMC_BASE);
/* Configure SMC_BW register to handle proper SROMC bank */
tmp = srom->bw;
tmp &= ~(0xF << (srom_bank * 4));
tmp |= srom_bw_conf;
srom->bw = tmp;
/* Configure SMC_BC register */
srom->bc[srom_bank] = srom_bc_conf;
}
static void dm9000aep_pre_init(void)
{
unsigned int tmp;
unsigned char smc_bank_num = 1;
unsigned int smc_bw_conf=0;
unsigned int smc_bc_conf=0;
/* gpio configuration */
writel(0x00220020, 0x11000000 + 0x120);
writel(0x00002222, 0x11000000 + 0x140);
/* 16 Bit bus width */
writel(0x22222222, 0x11000000 + 0x180);
writel(0x0000FFFF, 0x11000000 + 0x188);
writel(0x22222222, 0x11000000 + 0x1C0);
writel(0x0000FFFF, 0x11000000 + 0x1C8);
writel(0x22222222, 0x11000000 + 0x1E0);
writel(0x0000FFFF, 0x11000000 + 0x1E8);
smc_bw_conf &= ~(0xf<<4);
smc_bw_conf |= (1<<7) | (1<<6) | (1<<5) | (1<<4);
smc_bc_conf = ((DM9000_Tacs << 28)
| (DM9000_Tcos << 24)
| (DM9000_Tacc << 16)
| (DM9000_Tcoh << 12)
| (DM9000_Tah << 8)
| (DM9000_Tacp << 4)
| (DM9000_PMC));
exynos_config_sromc(smc_bank_num,smc_bw_conf,smc_bc_conf);
}
#endif
#ifdef CONFIG_CMD_NET
int board_eth_init(bd_t *bis)
{
int rc = 0;
#ifdef CONFIG_DRIVER_DM9000
rc = dm9000_initialize(bis);
#endif
return rc;
}
#endif
#ifdef CONFIG_CMD_NET
#define CONFIG_NET_MULTI
#define CONFIG_DRIVER_DM9000 1
#define CONFIG_DM9000_BASE 0x05000000
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
#define CONFIG_DM9000_USE_16BIT
#define CONFIG_DM9000_NO_SROM 1
#define CONFIG_ETHADDR 11:22:33:44:55:66
#define CONFIG_IPADDR 192.168.3.201
#define CONFIG_SERVERIP 192.168.3.200
#define CONFIG_GATEWAYIP 192.168.3.1
#define CONFIG_NETMASK 255.255.255.0
#endif
Linux内核移植:
1. Linux特性 非实时操作系统
开源,免费
可以裁剪 , 可以移植性强
需要的基本硬件资源很少 SRMA 8M
单片机使用较多:
RTOS 实时操作系统 : 对中断(异常) 响应时间快,稳定,可靠
多用于控制系统
FreeRTOS : 开源免费的 1K RAM flash 16KB
UCos,.. : 开源收费的
VxWorks : 完全收费的
伪操作系统: 事件驱动型 OSAL ZigBee: ble: 低功耗蓝牙
Linux内核子系统:
进程管理: 多进程 fork
内存管理: 堆内存malloc/free
文件系统: IO与文件
网络系统: 网络编程
设备管理: 驱动开发
Linux层次: 了解
应用层
内核层
Linux内核编译步骤:
获取内核源码: 官网获取 www.kernel.org
内核版本: 6.6.7
| |====> 次版本号 是奇数: 过度版本 稳定性待验证 偶数: 较稳定的版本
|===> 主版本号
Linux源码包: linux-3.14.tar.xz
Linux源码目录:
arch: 与体系架构相关的代码
以下与平台无关
drivers 驱动相关,设备的相关驱动程序
将硬件信息与驱动剥离,放到设备树文件,当设备变化时只需要修改设备树就可以,增强了代码的移植性,减轻维护成本。
fs 文件系统相关代码
init 系统初始化相关的
include 头文件相关代码
kernel 内核实现相关代码,调度算法等
lib 系统相关库
net 网络协议相关
block 块设备相关的
Document 帮助文档
mm 内存管理相关的
编译内核:
1. 指定交叉开发工具链
修改Makefile
2. 导入默认配置 ./arch/arm/configs/exynos_defconfig
make exynos_defconfig
3. 进行内核配置调整 一个字符型 的 窗口界面, 可以在其中进行配置
make menuconfig
4. 编译内核
make uImage -j2
Linux uImage 生成过程 以及 步骤:
通过编译打印 可得
源码链接 ----> vmlinux -----> Image -----> zImage ----> uImage
elf bin 压缩格式 uboot压缩格式
vmlinux: 60M 第一次链接生成的 内核的可执行文件 包含调试数据 符号链接等 源码根目录
Image : 5.2M 去除调试信息 以及符号链接等的 格式转换 可执行内核 arch/arm/boot/Image
arch/arm/boot/compressed/vmlinux 二次链接 2.8M
由 Image 压缩 以及 额外添加了 解压缩代码
zImage : 2756488 2.7M 有Image 压缩 以及添加了 解压缩代码的 内核压缩包
uImage : 2756552 2.7M 在zImage前面 添加的64字节的 特殊信息, 专门用于 uboot 引导
说明这个内核的版本、加载位置、生成时间、大小等信息;其0x40之后与zImage没区别。
该步骤由mkimge工具完成的
内核启动 第一步: 自解压
设备树编译: 存储着硬件的差异信息 特殊结构文件 目的 将驱动程序与硬件信息分离
1. 使用origen板子 的设备树 复制
cp arch/arm/boot/dts/exynos4412-origen.dts arch/arm/boot/dts/exynos4412-fs4412.dts
2. 编译设备树 exynos4412-fs4412.dtb
make exynos4412-fs4412.dtb
或者使用 make dtbs 编译所有设备树
设备树的结构: 在 arch/arm/boot/dts/ 中
文件后缀 .dtsi .dts .dtb
.dtsi 类似于 .h文件 主要描述 核心信息 通常由其他板子的 .dts文件 引用
.dts 类似于 .c文件 描述硬件信息, 以字符形式展示
结构类似一颗倒置的树 有点类似与 Linux目录结构
/ 根节点
{
属性 以键值对形式表示
子节点1子节点名字 { 属性, 子节点 };
子节点2 { 属性, 子节点 };
// ,.....
};
.dtb 类似于 .o 文件 由 .dts 经过 DTC 工具 编译得到的
给内核使用的 二进制格式 人无法阅读
3. 验证 编译的 内核与设备树
复制 编译好的 设备树 与 内核镜像到 tftp工作目录
cp arch/arm/boot/dts/exynos4412-fs4412.dtb ~/tftpboot/
cp arch/arm/boot/uImage ~/tftpboot/
4. 开发板下载 并引导内核 uboot执行 配置开发板IP 设置bootargs
tftp 41000000 uImage
tftp 42000000 exynos4412-fs4412.dtb
bootm 41000000 - 42000000
Linux内核启动流程: 了解
Linux系统启动:
1. uboot 加载内核
2. linux 内核启动
3. 根文件系统 挂载, 应用程序运行
uboot:
tftp 41000000 uImage
或
movi read kernel 41000000
Starting kernel ...
linux 内核启动:
1. 内核自解压
由uImage 解压出来 Image
运行zImage中自带的 解压缩程序 将 内核释放到指定位置 0x40008000
uImage : 41000000
Image : 40008000
完成自解压后 跳转到 入口地址 40008000 地址运行内核 Image
2. 内核启动 arch/arm/kernel/head.S
汇编阶段: 进行CPU 类型检查, 基本硬件初始化
__lookup_processor_type : CPU信息获取与检查
__vet_atags 验证Uboot传入参数及设备树
__create_page_tables :准备页表,为后续开启mmu(内存管理单元)做准备
开启内存虚拟化:
__mmap_switched:跳转到文件 arch/arm/kernel/head-common.S
拷贝data段 如果需要 清除bss段
b start_kernel 跳转C阶段
start_kernel: 进入到C阶段
C阶段:
init/main.c 文件中
定时器初始化, 关中断 , 内存虚拟化 ...
rest_init(); // 该函数不会返回
内核启动ok
内核线程
3.挂载根文件系统 运行第一个应用层程序 linuxrc
内核 驱动移植:
1. 网卡驱动 参考实验手册
设备树 添加网卡节点
srom-cs1@5000000 { //bank1 物理设备 描述
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x5000000 0x1000000>;
ranges;
ethernet@5000000 {
compatible = "davicom,dm9000";
reg = <0x5000000 0x2 0x5000004 0x2>;
interrupt-parent = <&gpx0>; // 网卡的中断管脚 GPX0_6
interrupts = <6 4>;
davicom,no-eeprom;
mac-address = [00 0a 2d a6 55 a2];
};
};
网卡设备树 配置 参考 Documentation/devicetree/bindings/net/davicom-dm9000.txt
2. 修改时钟使能
3. 配置内核 make menuconfig
4. 编译,在开发板上运行
内核的配置过程以及原理:
.config : 源码根目录 存储 配置信息 定义宏以及其值
Kconfig : 在每个目录中都有, 用于提供界面选项(在make menuconfig)时
Makefile: 在每个目录中都有, 用于在编译时 根据.config定义的变量 进行选择编译
实验第三方驱动移植: 参考实验手册
第三方驱动: 不是内核自带的驱动程序 LED驱动
1. 直接编译到内核 系统启动需要的一些驱动
内核污染: 第三方驱动 污染的内核
2. 编译为模块
将驱动程序 编译为 一个独立的文件 后缀.ko
在程序需要使用的时候 动态添加模块, 使用完毕后 可以卸载
可以解决冲突问题 同时方便更新与维护
Kconfig格式:
source "xxx/Kconfig" 引入 xxx/Kconfig 文件展开到这里
menu " xxxx " 创建一个菜单(可以展开的)
// 写一些选项
config XXXXX #表示要创建一个选项 该选项控制宏 XXXXX
bool 选项名 #表示这是一个二选一的 选项 tristate 表示3选1
default y # 默认值
help
xxxx 帮助信息
endmenu
编译测试 应用程序 :
arm-none-linux-gnueabi-gcc fs4412_led_app.c -o led_app
编译模块 make modules
xxx.ko 内核模块
手动添加 设备节点
mknod /dev/led c 501 0
添加一个设备节点 c 字符型 501 主设备号 0 次设备号
安装内核模块: insmod xxx.ko
卸载内核模块: rmmod xxx
查看安装的模块: lsmod
内核调试:
打印法调试(内核中的打印)
puts 内核解压前使用
printascii 串口初始化前使用 输出到 缓存区
串口初始化后
printk: 与printf 非常详细
printk( "a=%d",a);
printk 带有输出等级
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>” /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages
printk(KERN_WARNING "warning ...");
方便调试, 可以宏观控制 高于某个等级的 打印可以输出
控制内核打印输出的等级 通过bootargs参数设定 增加选项 loglevel=输出等级 0-8
set bootargs root=/dev/nfs nfsroot=192.168.3.200:/home/xwq/nfshome/rootfs rw console=ttySAC2,115200 init=linuxrc ip=192.168.3.201 loglevel=8
内核OOP打印: 内核崩溃时的打印信息
回溯内核函数栈, 显示内核崩溃时调用了哪些函数
根文件系统制作:
使用工具 busybox 工具来制作 https://busybox.net/
将Linux 常用的命令 以及 linuxrc 都集成到一个程序中 busybox
业内称作 瑞士军刀
根文件系统主要有些啥?
1.目录结构
bin dev etc lib linuxrc mnt proc root sbin sys tmp usr var
bin: 存储Linux的shell命令
etc: 存储系统配置
lib: 存储一些需要的库
linuxrc: 是一个软链接 挂载根文件系统后的第一个进行 0号进程
2.使用 busybox 生成根文件系统
为什么需要根文件系统?
文件系统: 是数据的一系列抽象,标准化数据的一种方式
不同系统,不同架构,不同体系间都可以通过文件的形式共享,交流数据
根文件系统: Linux内核启动后 需要运行用户的应用程序
方便用户应用程序的 移植
用户应用程序
elf格式的 有操作系统情况下运行的 可执行文件
bin格式 用于裸机运行
内核与用户的交互接口 根文件系统
参考实验手册: 根文件系统的移植实验手册.doc
1. 解压源码busybox
2. 配置busybox
make menuconfig
配置 交叉编译工具链
使能 命令使用静态库
3. 编译
make
4.安装
make install
5. 添加其他目录 到 _install
mkdir dev etc mnt proc var tmp sys root
6. 添加一些库 只要动态库
cp /opt/gcc-4.6.4/arm-arm1176jzfssf-linux-gnueabi/sysroot/lib ./lib -r
7. 删除静态库 以及 不需要的库
sudo rm ./*.a ./*.la -rf
8. 删除动态库的符号表
arm-none-linux-gnueabi-strip lib/*
9. 添加开机配置文件
etc/
├── fstab : 系统挂载表 挂载一些系统目录 在开机启动脚本中 会访问该文件
├── init.d
│ └── rcS : 开机启动脚本,在inittab文件中指定 系统启动时需要执行一次该脚本
├── inittab : 指定系统 在一些情况下需要作的事
└── profile : 指定系统启动后的 环境变量,主机名,用户名等的 配置
10. 根文件系统 ok 测试
删除原有的 nfshome/rootfs 中的内容
复制 _install 中的所有内容 到 nfshome/rootfs 中
产品阶段
系统固化: 将已经 做好的 Uboot uImage dtb设备树 根文件系统 固化化到 开发板
Uboot: 烧录到 SD 卡 烧录到flash
uImage: 烧录到flash kernel分区
tftp 41000000 uImage
movi write kernel 41000000
dtb : 烧录到flash dtb分区
tftp 42000000 exynos4412-fs4412.dtb
movi write dtb 42000000
根文件系统固化:
2个方向:
1) 内存镜像: 掉电后,(系统启动后新增的)存储的数据会丢失
根文件系统 不会损坏
将根文件系统 作为 内存镜像方式 如图所示
根文件系统本身 以镜像(压缩包)形式存储在 flash
使用时(Linux启动时) 将镜像 解压到 内存中 在内存中挂载根文件系统
需要配置 Linux内核 要支持 内存镜像 根文件系统
2) 直接使用 flash分区或 SD等 存储介质
根文件系统 直接复制到 flash分区或 SD 卡上
系统数据 可以在掉电后存储
不能意外断电 突然断电 可能导致 系统数据丢失或出错
需要 Linux内核 支持 SD卡或emmc
3) 根文件系统 使用 内存镜像 通过 挂载的方式 将SD卡或 flash分区 挂载到
根文件系统 , 用于存储用户数据
实验 根文件系统 镜像制作:
1. 开辟一个 指定大小(10M)的 磁盘镜像空间
xwq@xwq-pc:~/tmp$ dd if=/dev/zero of=ramdisk bs=1k count=10240
2. 格式化 磁盘镜像空间
xwq@xwq-pc:~/tmp$ mkfs.ext2 -F ramdisk
3. 挂载 磁盘镜像空间
xwq@xwq-pc:~/tmp$ sudo mount ramdisk /mnt/mdisk/
4. 复制根文件系统到 磁盘镜像空间
xwq@xwq-pc:~/nfshome/rootfs$ sudo cp ./* /mnt/mdisk/ -r
5. 卸载 磁盘镜像空间
xwq@xwq-pc:/mnt$ sudo umount mdisk/
6. 压缩磁盘镜像 生成 ramdisk.gz
xwq@xwq-pc:~/tmp$ gzip --best -c ramdisk > ramdisk.gz
7. 格式化ramdisk.gz 生成 ramdisk.img 文件
xwq@xwq-pc:~/tmp$ mkimage -n "ramdisk" -A arm -O linux -T ramdisk -C gzip -d ramdisk.gz ramdisk.img
ramdisk.img 文件就是 根文件系统的 内存镜像
如何固化 与 使用 内存镜像 ?
1) 下载 ramdisk.img 到内存 ramdisk.img 需要先复制到 tftpboot目录中
tftp 43000000 ramdisk.img
FS4412[general] # tftp 43000000 ramdisk.img
dm9000 i/o: 0x5000000, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 11:22:33:44:55:66
operating at 100M full duplex mode
Using dm9000 device
TFTP from server 192.168.3.200; our IP address is 192.168.3.201
Filename 'ramdisk.img'.
Load address: 0x43000000
Loading: #################################################################
#################################################################
#################################################################
#################################################################
#########
897.5 KiB/s
done
Bytes transferred = 3943857 (3c2db1 hex)
3c2db1: img磁盘镜像的大小 (16进制)
2) 烧录 ramdisk.img 到 rootfs分区
movi write rootfs 43000000 3c2db1
使用固化的镜像 启动Linux系统:
设置uboot环境变量:
set bootcmd movi read kernel 41000000\;movi read dtb 42000000\;movi read rootfs 43000000 3c2db1\;bootm 41000000 43000000 42000000
set bootargs root=/dev/ram0 rw console=ttySAC2,115200 init=linuxrc ip=192.168.3.201 loglevel=8