day 1
【1】移植的目的 -> 匹配
硬件的改变软件也要做相应的改变
【2】系统移植基本内容
windows装机:
进入BIOS选择U盘启动(即开机后执行U盘的程序)->从U盘中启动老毛桃(引导安装系统)->安装windows驱动->安装应用程序
linux系统移植:
通过拨码开关选择启动方式(SD卡启动)->bootloader(uboot)引导安装linux->安装linux驱动->安装linux应用程序
【3】交叉编译
程序的编译和运行不在同一台机器上
-> 在开发主机上进行编辑与编译,在目标机上去运行
-> 我们需要在开发主机安装交叉编译工具链,将代码编译成目标机处理器能执行的代码
【4】编译原理
1--- 预处理:主要进行宏替换、头文件的展开以及注释的删除等 不检查语法错误
gcc -E hello.c -o hello.i
gcc -E == cpp(C预处理器)
2--- 编译:编译生成汇编文件,会检查语法错误
gcc -S hello.i -o hello.s
gcc -S == cc1(不在全局环境变量,使用时需指定绝对路径)
3--- 汇编:将汇编文件编译生成目标文件(二进制文件)
gcc -c hello.s -o hello.o
gcc -c == as(汇编器)
4--- 链接:将.o文件链接上对应的库函数,生成可执行文件
gcc hello.o -o hello
gcc = ld(需要指定库的名称与其所在系统中的路径)
注:汇编对硬件平台有依赖性
【5】ELF格式是Unix/Linux平台上应用最广泛的二进制工业标准之一(a.out就是elf格式)
readelf:显示elf格式的可执行文件的信息
1) readelf -h a.out 列出头部信息
2)readelf -a a.out 列出所有信息
注:readelf只可以显示elf格式的可执行文件的信息,使用时必须加一个选项
【6】实验一 交叉编译工具链的安装
使用新的编译工具,将代码编译成ARM架构处理器能够支持的机器码,使其能够在开发板上运行
【7】GNU命令补充
1.size 列出目标文件每一段的大小以及总体的大小
size a.out
2.nm 列出目标文件中的符号(标示符)
nm a.out
重要的命令:
3.strip 丢弃目标文件中的全部或者特定符号,减小文件体积。对于嵌入式系统,这个命令必不可少
strip a.out
注1:删除的都是一些与功能代码无关的标签符号,不影响程序的执行
注2:ll a.out查看文件体积
4.file 显示文件的详细信息
file a.out
注1:file也可以查看其它格式文件的信息
5.objdump 显示一个或者更多目标文件的信息,主要用来反汇编(机器码->汇编)
1) objdump -d a.out(elf) 将可执行文件反汇编显示在终端
2) objdump -d a.out(elf) > test.dis 将反汇编重定向到文件 生成反汇编文件
3)objdump -D -b binary -m i386 test.bin > test.dis 反汇编bin格式的文件 此处一定是-D
注:-b binary指定二进制文件格式 -m i386指定二进制文件的处理器架构
6.objcopy 进行目标文件格式转换
objcopy --gap-fill=0xff -O binary a.out a.bin
注1:--gap-fill=0xff 文件格式不同需要填充数据使格式对其
注2:-O binary指定输出文件的格式 binary(bin)
内核调试命令:
7.addr2line 把程序地址转换为文件名和行号(前提是这个可执行文件包括调试符号 -g),常用于定位内核错误
addr2line 0x080483c4 -e a.out -f
错误地址 要定位的elf文件 显示函数名
【8】主机与开发板进行文件传输 -> tftp服务:简单文本传输协议(基于udp)
tftp服务配置:
1.修改tftp配置文件
sudo vi /etc/default/tftpd-hpa
TFTP_USERNAME="tftp" # 用户名
TFTP_DIRECTORY="/tftpboot" # 服务器端下载文件的目录
TFTP_ADDRESS="0.0.0.0:69" # 端口号 tftp在ubuntu下固定为69
TFTP_OPTIONS="-c -s -l" # 设置服务器端最大的权限
2.创建服务端文件夹并设置为最高的权限
sudo mkdir /tftpboot
sudo chmod 777 /tftpboot
3.重启tftp服务
sudo service tftpd-hpa restart # 重启TFTP服务
注:每次开启虚拟机后都需要重启tftp服务
【9】NFS服务 网络文件系统
-> NFS服务的主要的任务是把本地的一个目录通过网络导出,其他计算机可以远程访问该目录
NFS服务配置:
1.修改配置文件
sudo vi /etc/exports
在文件最后添加如下内容
/source/rootfs *(rw,sync,no_subtree_check,no_root_squash)
注1:/source/rootfs 要共享的目录
注2:* 所有主机都可以共享该目录
注3:(rw,sync,no_subtree_check,no_root_squash) 访问该目录的主机拥有的权限
# rw 具有读写权限
# sync 文件同步写入到内存和硬盘
# no_subtree_check 不检查子目录权限 子目录与顶层目录具有相同的权限
# no_root_squash 如果客户端是root的话,那么他对这个共享目录具有root的权限
2.创建共享目录
sudo mkdir -p /source/rootfs
sudo chmod 777 /source
3.重启nfs服务
sudo service nfs-kernel-server restart
注:每次开启虚拟机都需要重启nfs服务
【10】FS4412开发板启动流程
1. 三星固化代码通过拨码开关的启动方式将对应存储器中的uboot加载到内存
2. uboot启动->初始化硬件(时钟、RAM、串口等),然后将linux内核引导加载到内存
3. 启动linux内核,做进一步的初始化,挂载根文件系统(linux系统运行所必须的文件)
4. 执行根文件系统上的应用程序
【11】bootloader(启动、加载)
上电后在操作系统内核或用户应用程序运行之前运行的一小段代码
硬件启动的引导程序,是运行操作系统的前提
基本功能:
1. 初始化相关的硬件(比如内存、时钟、串口等)
2. 引导加载操作系统内核
3. 给操作系统传递必备的参数
4. 执行用户命令
特点:
1. C与汇编混合编程
-> 第一阶段由汇编完成基本的初始化
-> 第二阶段由C进行功能的完善
2. 不具有通用性
-> 依赖于CPU的体系结构,依赖于嵌入式系统板级设备的配置
嵌入式开发常用的bootloader -> uboot
【12】uboot操作模式
1. 自启动模式:Bootloader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入
2. 交互模式: 目标机上的Bootloader将通过串口或网络等通信手段从开发主机(Host)上下载内核映像和根文件系统映像等到RAM中
【13】配置Ubuntu网络环境
1.在windows中“网络共享中心-更改适配器设置”中禁止无线网络
2.在VMWare中“虚拟机-设置-网络适配器”修改为桥接模式
3.使用图形化界面删除所有连接
4.修改为静态IP
1)打开配置文件
sudo vim /etc/network/interfaces
修改为
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.1.249
netmask 255.255.255.0
gateway 192.168.1.1
2)重启网卡
sudo /etc/init.d/networking restart
3)设置DNS服务
sudo vim /etc/resolv.conf
修改为
nameserver 192.168.1.1
【14】uboot常用命令(前提是运行在uboot环境下)
命令分类:
环境设置、数据传输、存储器访问、加载运行
1.环境设置
1) printenv/pri 显示所有环境变量
2) setenv/set 设置新的环境变量
setenv ipaddr 192.168.0.123 # 更改原来的环境变量
setenv Myboard FS4412 # 设置新的环境变量
setenv Myboard # 赋值为空表示删除该环境变量
3) saveenv/save #将当前定义的所有的环境变量值存入flash中保存
4) 必要的设置
ipaddr: 开发板的IP地址
serverip:ubuntu主机(服务器)的ip
注:ipaddr和serverip必须是在同一网段的不同地址
eg:
setenv ipaddr 192.168.1.100
setenv serverip 192.168.1.249
saveenv
2.存储器访问
1) movi 自己添加的操作EMMC的命令 uboot源码中没有
movi read kernel addr # 将kernel分区中的数据读取到物理内存的addr地址上去
比如:movi read kernel 41000000
movi write kernel addr # 将物理内存addr地址上的数据写入到kernel分区上
比如:movi write kernel 41000000
movi read rootfs addr size # 将rootfs分区中的数据读取到物理内存的addr地址上去,大小为size(字节)
比如:movi read rootfs 43000000 300000
movi write rootfs addr size # 将物理内存addr地址上开始的size(字节)的数据写入到rootfs分区上
比如:movi write rootfs 41000000 300000
2)将SD卡中的uboot烧写到EMMC中的uboot分区
sdfuse flashall
3.数据传输
1) tftp 使用tftp服务下载文件
tftp 41000000 application.bin # 使用tftp服务将服务器中application.bin文件下载到物理地址41000000处
注:tftp不需要指定服务器端的IP 因为在serverip已经指定
day 2
【14】uboot常用命令(前提是运行在uboot环境下)
4.加载运行
1) bootcmd 自启动的环境变量
setenv bootcmd tftp 41000000 uImage\; ......
saveenv
eg:
setenv bootcmd tftp 41000000 uImage\;bootm 41000000
注:当有多个命令时使用 "\;"分割
2) bootm 跳转到指定的地址上去执行代码,为内核传参
bootm kernel-addr ramdisk-addr dtb-addr
内核的下载地址 ramdisk的下载地址 设备树的下载地址
三个地址是固定的 如果没有对应的地址使用‘-’
eg:
bootm 41000000 43000000 42000000
bootm 41000000 - 42000000
3)bootargs 自启动参数,传递给内核必要的参数,使内核顺利启动
bootargs=root=/dev/nfs nfsroot=192.168.1.101:/source/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.1.100
/dev/nfs nfsroot -> 文件系统类型
192.168.1.101:/source/rootfs -> 文件系统路径
rw -> 操作文件系统的权限
console=ttySAC2,115200 -> 控制台使用串口2 波特率115200
init=/linuxrc -> 启动脚本 第一个运行的程序
ip=192.168.1.100 -> 自身IP
【15】实验二 u-boot的烧写及使用
将配置编译好的u-boot烧写到EMMC中,并使用u-boot引导加载操作系统内核
【16】uboot版本
命名:
前期:uboot-1.2.3
现在:uboot-2008.01
版本选择:
1. 支持对应的硬件平台
2. 相对成熟的(资料多)
现有平台版本:uboot-2013
【17】uboot的目录结构
README:对源码进行基本的介绍的说明性文档
平台相关代码:与CPU架构或开发板硬件直接相关的源码,硬件的改动需要进行对应的配置
arch:与CPU相关的目录文件,包含了各种体系架构的处理器的源代码
board:与开发板相关的目录文件,包含与各种官方评估板的对应的源代码
平台无关代码:与CPU架构与开发板硬件无关的上层的功能代码,硬件平台的改变不需要做修改
common:实现了uboot下支持的命令,每一个命令对应一个文件,可以自己添加命令
disk:对大存储磁盘设备的支持
drivers:各类设备的驱动程序
fs:对各类文件系统的支持,较少
include:uboot源码中使用的头文件
lib:通用的库文件
net:与网络协议相关的代码,tftp等
post:上电自检程序
工具、文档、示例程序:
api、CREDITS、doc、examples、tools
【17】uboot的配置与编译
1.配置(确定了当前硬件平台-确定了要编译的平台相关代码)
make
_config # 比如 make fs4412_config 前提是uboot支持该开发板
2.编译(生成可使用的uboot文件)
make # 编译后在uboot顶层目录下生成u-boot.bin即为我们要使用的文件
【18】uboot的配置过程
在顶层目录下执行make fs4412_config后会在include目录中创建config.mk文件(MakeFile引用)
和config.h头文件(包含对应平台的源文件),并创建了对应的软链接。
软链接与头文件的创建源码就确定了目标平台与编译所要引用的文件并对其进行引用
【19】执行make命令后按照顶层的MakeFile的规则会调用一系列的源文件、工具、配置文件、子目录下的MakeFile等,
最终在最顶层MakeFile文件所在的目录下生成u-boot.srec、u-boot.bin、System.map三个文件,
其中u-boot.bin文件是我们所要使用的文件(由elf格式的u-boot文件经过格式转换得到)
【20】实验三 u-boot的移植
u-boot源码并不支持fs4412开发板,我们使用u-boot源码中与fs4412最接近的硬件平台
在此基础上进行修改生成在fs4412平台上能运行的uboot
day 3
【21】代码分析工具ctags
1.在源码顶层目录生成交叉索引文件
ctags -R
注:如果想用ctags索引符号,打开一个文件时必须在tags文件所在路径打开
2.跟踪代码,将光标放在跟踪的符号上
Ctrl + ]
3.进入符号所在文件
Esc + Esc + Num
4.后退
ctrl + T
【22】u-boot的启动流程(arch/arm/cpu/u-boot.lds链接脚本决定了整个uboot的排布)
1.arch/arm/cpu/armv7/start.s -- 汇编文件
1) 构建异常向量表 # 39 _start: b reset
2)SVC模式、ARM状态、关中断 # 126 reset:
3)重置异常向量表
4)关闭MMU和Cache(uboot运行在物理地址) # 167 bl cpu_init_cp15
5)跳转到lowlevel_init # 168 bl cpu_init_crit -> LR中保存了指令bl _main的地址
6) 跳到特定的板子进行时钟、存储器等的初始化 # 344 b lowlevel_init
2.board/samsung/fs4412/lowlevel_init.S -- 汇编文件
1) 初始化栈,压栈返回地址 # 41 lowlevel_init: / 43 push {lr}
2)判断uboot是开机启动还是被唤醒启动,不同的启动方式跳转到不同的代码
3)关闭看门狗
4)判断是否运行在物理内存上(BL2已将uboot搬移到内存) # 101 beq 1f
5)初始化串口 # 109 1:
6) 返回arch/arm/cpu/armv7/start.s/_main # 115 pop {pc}
3.arch/arm/lib/crt0.S -- 汇编文件
1)初始化C运行环境(栈指针) # 96 _main:
2)跳转 # 115 bl board_init_f
4.arch/arm/lib/board.c -- C文件
1)初始化全局数据,循环调用指针函数数组进行各种硬件的初始化 # 277 void board_init_f(ulong bootflag)
5.arch/arm/lib/crt0.S -- 汇编文件
1)为跳转到relocate_code做准备工作 # 130 adr lr, here -> lr寄存器中保存了here的地址
2)uboot自搬移到高端物理地址,为操作系统的加载留出空间 # 136 b relocate_code
6.arch/arm/cpu/armv7/start.s -- 汇编文件
1)为自搬移进行准备工作(起始、终止地址及大小) # 183 ENTRY(relocate_code)
2)uboot自搬移核心代码 # 196 copy_loop:
3)重定向符号,以免绝对地址跳转失败
4)返回到arch/arm/lib/crt0.S/here: # 243 bx lr
7.arch/arm/lib/board.c -- C文件
1)初始化大部分硬件设备 # 277 void board_init_f(ulong bootflag)
2)跳转到common/main.c/main_loop(); # 703 main_loop();
8.common/main.c -- C文件
1)根据环境变量,根据环境变量的值做对应的操作 # 353 void main_loop (void)
9.uboot结束
u-boot启动流程:
1.第一阶段(主要是汇编)
1)初始化必要的硬件
2)uboot自搬移到内存高地址处
3)设置C语言运行环境
2.第二阶段(C语言)
1)初始化大部分硬件
2)读取环境变量
3)运行用户命令、引导加载内核
【23】linux内核
1.内核与操作系统
1)内核:系统软件,用于管理和分配软硬件资源
2)操作系统:在内核的基础上,添加了各种工具集、库、桌面管理器、应用程序等
2.linux特点
1)支持广泛的硬件平台
2)稳定性高
3)扩展性好(使用各种场合)
4)模块化设计(高内聚、低耦合)
3.linux内核版本
linux内核版本号组成:主版本号.次版本号.修订版本
4.版本选择
1)支持对应的平台
2)相对成熟(资料)
3)稳定版本(次版本号为偶数的版本都是稳定版)
-> linux 3.14.0
5.内核子系统(主要功能)
进程管理、内存管理、文件系统、网络协议、设备管理
【24】linux内核源码目录结构
1)平台相关
arch(处理器架构)
2)平台无关
block(大容量磁盘设备)、crypto(加密算法)、Documentation(文档)、 drivers(设备驱动)、firmware(固件)、
fs(文件系统的实现)、include(头文件)、init(内核初始化)、ipc(进程间通信机制)、kernel(内核核心进程调度等)
lib(库函数)、mm(内存管理)、net(网络协议)、samples(示例代码)、scripts(配置编译所需工具、脚本)、
security(安全相关)、sound(音频驱动)、tools(自带工具)、usr(打包与压缩算法)、virt(内核虚拟)
【25】linux内核的配置与编译
1.配置内核
1)导入默认配置:
make xxxx_defconfig
注1:xxxx表示内核支持的芯片的名称 比如make exynos_defconfig
注2:内核源码中对每个支持的芯片都有默认的配置,默认配置很少只能保证系统完成最基本的功能
注3:可以通过直接修改.config文件来进行内核的配置(麻烦),所有内核配置的本质都是修改.config文件
注4:配置文件中xxxx=y表示添加了该功能,注释表示不添加该功能(减小内核体积)
2)修改配置(内核提供了几种简单的配置方法,其本质都是修改.config文件中的配置)
make gconfig 依赖于GTK库
make xconfig 依赖于QT库
make config 麻烦
-> make menuconfig
选中的状态切换:空格
搜索一个选项:/
[ ] 有两种状态。
输入Y,显示为“*”,表示内核中该功能被选中,相关功能代码将会被编译进内核。
输入N,显示为空,表示内核中该功能没有被选中。
< > 有三种状态
输入Y,显示为“*”,表示内核中该功能被选中
输入N,显示为空,表示内核中该功能没有被选中
输入M,则显示为“M”,表示内核中该功能被选为模块(编译后与内核镜像不在同一文件)
2.编译内核(在内核源码顶层目录下)
make uImage 编译内核镜像(编译选中为“*”的选项到内核)
make modules 编译内核模块(编译选中为“M”的选项)
make dtbs 编译设备树文件(使驱动与设备建立关系的文件)
make clean 删除文件
【26】实验四 内核的编译和配置
day 4
【27】uImage
uImage和zImage都是内核镜像文件,uImage是在zImage的基础上添加了64字节的头部信息用于uboot加载内核镜像时校验使用
因为这个头部信息最终是要被uboot解析使用,所以使用uboot中的工具进行制作
【28】linux内核(uImage)的编译过程
一系列的源码与工具按照arch/arm/kernel/vmlinux.lds与各级Makefile的规则编译生成源码顶层目录下的vmlinux(elf格式内核)即第一次链接
vmlinux经过二进制转换生成arch/arm/boot/Image(二进制格式的内核)
Image文件经过压缩得到piggy.gzip文件(压缩后的二进制内核)(piggy.gzip文件的运行需要解压)
piggy.gzip与解压该文件的代码misc.c、decompress.c、等按照链接脚本arch/arm/boot/compressed/vmlinux.lds链接成一个文件(第二次链接)vmlinux(elf)
vmlinux文件经过二进制转换生成zImage
zImage经过添加头部生成uImage
【29】在把uImage加载到内存的41000000地址后首先uImage要进行自解压,将内核解压到40008000地址,所以内核的运行地址是40008000
【30】设备树
设备树:一种描述开发板硬件设备的数据结构,有一系列的节点和属性组成,根据实际硬件信息对其作对应的修改
在linux中驱动与设备无关,设备树就是来描述硬件信息的,这样可以是驱动与设备建立关系
linux在ARM平台上在linux 2.6(2011年)之前使用平台代码,之后使用设备树,源码中每一个板子都有一个对应的平台代码
设备树文件后缀:
dtb 编译后的设备树二进制文件
dts 设备树源文件
dtsi 类似于C语言头文件 一些共同的代码 可以被其他设备树文件引用
设备树的语法结构:
#include "exynos4412.dtsi"
/ {
node_1{ # 子节点
child_node_1{ # 子节点下的节点
};
child_node_2{ # 子节点下的节点
};
};
node_2{ # 子节点
xxxx
};
};
设备树的语法为树状结构,根节点下可以有子节点,子节点下还可以包含字节的子节点,子节点与其上一级节点要有上下级的包含关系
详情参考 /Documentation/devicetree/bindings
设备和驱动是一一对应的关系,设备和驱动的对应就是靠设备树来完成
【31】实验5
【32】在内核中添加子菜单
每一级目录中都包含一个Kconfig文件,用于生成选配菜单的。按照Kconfig的语法修改对应的Kconfig文件就可以在菜单中添加自己的选项
在一个Kconfig文件中menu是本级菜单,如menu "Character devices",如果我们想在该菜单下自己添加子菜单按照语法添加在menu和endmenu之间即可
语法:
config FS4412_LED # 固定语法,config XXX,XXX用来与MakeFile关联,即配置后MakeFile会按照该配置进行编译
tristate "FS4412LED Device Support" # tristate “菜单显示名称”;tristate表示该选项有三个状态<>(即可以选择成N/Y/M),bool表示有两个选项[](N/Y)
default n # default n 默认是'N',即不添加该功能 default y 默认添加该功能 default m 默认编译成模块(重新配置后生效)
depends on ARCH_EXYNOS4 # depends on 依赖的菜单 当有依赖关系的选项没有被选择时该选项也不显示
help
support leddevice on FS4412 develop board # 帮助文档 可有可无
详情参考:Documentation/kbuild
将自己的模块添加到菜单的步骤:
1.修改Kconfig添加子菜单
2.添加对应的.C文件(驱动文件)
3.修改对应的Makefile使菜单与编译关联(将驱动文件编译)
【33】实验6、7、8
【34】内核的启动流程
内核的解压过程:uboot将压缩后的内核镜像下载到了内存中以41000000为起始地址处(uImage约2.9M),该文件中用于解压的代码放在低地址处,所以uboot
在执行bootm 41000000后会首先执行解压缩内核的代码对内核进行解压,解压内核会将内核解压到以40008000为起始地址的空间上(40008000-41000000约15M的空
间,内核没有压缩前Image文件约5M,如果下载的起始位置与解压的起始位置之间的空间不足以存放内核,那么内核在解压过程中会有自己的解压缩的算法将内核的
下载地址挪到高地址处)
1.arch/arm/kernel/head.S
1)设置CPU为SVC模式,暂时关闭中断 # 92 safe_svcmode_maskall r9
2)获得处理器id # 94 mrc p15, 0, r9, c0, c0 @ get processor id
3) 判断当前处理器是否支持内核 # 95 bl __lookup_processor_type(arch/arm/kernel/head-common.S)
4)判断uboot给内核传递参数的方式(设备树/tags) # 121 bl __vet_atags(arch/arm/kernel/head-common.S)
5)创建页表,为开启MMU做准备 # 128 bl __create_page_tables(arch/arm/kernel/head.S)
6)r13 = __mmap_switched # 137 ldr r13, =__mmap_switched @ address to jump to after
7)开启MMU # 144 1: b __enable_mmu(arch/arm/kernel/head.S)
# 421 __enable_mmu:
# 444 b __turn_mmu_on(arch/arm/kernel/head.S)
# 463 ENTRY(__turn_mmu_on)
6)跳转到__mmap_switched # 471 mov pc, r3
2.arch/arm/kernel/head-common.S
1)拷贝一些相关数据,清BSS段 # 80 __mmap_switched:
2)跳转到C继续初始化 # 104 b start_kernel
3.init/main.c
1)各种初始化 # 479 asmlinkage void __init start_kernel(void)
509 setup_arch(&command_line); # 用获取uboot传递的参数(设备树对硬件信息的描述)
581 console_init(); # 初始化控制台,该函数调用之前,终端上不会有任何输出语句
# 不要在此之前修改内核源码 否则不能使用控制台调试
... ... # 一系列的硬件的初始化
652 rest_init(); # 最后的初始化 完成该初始化后完成系统的启动
# 调度器的初始化
# 第一个内核线程的创建
# 根据uboot参数(bootargs)去挂载根文件系统
# ... ... # 一系列内核功能的初始化
# 尝试去执行第一个应用程序
# ... ...
day 5
【32】内核调试
1.顶层代码的调试(即linux系统启动后)
1)打印语句
2)gdb调试
... ...
2.内核调试(即内核启动阶段出现了错误)
1)解压内核阶段(arch/arm/boot/compressed/misc.c/)可以使用putstr函数
2)汇编阶段
a.printascii函数(arch/arm/kernel/debug.S实现)
b.点灯法 在某一个位置加上点灯程序来验证是否运行到该阶段
3)C语言阶段(init/main.c/start_kernel之后)
a.console_init(初始化终端)之前
a)printascii
b)点灯法
b.console_init之后
a)printk函数(kernel/printk/printk.c)
int printk(const char *fmt, ...)
int printf(const char *format, ...)
printk的输出语句带有级别,从0-7共8种级别,数字越小级别越高(include/linux/kern_levels.h)
printk输出时会和终端的级别做比较如果级别大于终端级别正常输出否则不会输出
linux系统下默认情况下终端的默认级别是4,即当printk消息的级别小于4时才会打印(终端级别:proc/sys/kernel/printk)
printk( KERN_ERR “Level \n” ); # KERN_ERR就是该消息的级别
printk(“default_message_loglevel \n”); # 不写级别默认使用的是消息的默认级别4
注:消息级别只决定显示与系统是否故障没有关系
【33】实验十
内核调试
【34】根文件系统
1.概念
根文件系统:系统运行所必须依赖的一些文件(比如脚本、库、配置文件...),本质就是目录和文件。
根文件系统镜像:将根文件系统按照某种格式进行打包压缩后生成的单个文件
文件系统:一种管理和访问磁盘的软件机制,不同文件系统管理和访问磁盘的机制不同
2.根文件系统基本目录(嵌入式)
bin: 命令文件(通过工具制作)
dev: 设备文件(被操作系统识别的设备才有对应的文件,即设备运行时)
etc: 配置文件(配置内核相关的一些信息)
lib: 库文件、比如C的标准库(从交叉编译工具链中复制的)
linuxrc:根文件系统被挂载后运行的第一个程序(通过工具制作)
mnt: 共享目录(非必要)比如挂载SD卡等时将SD卡挂载在该目录
proc: 与进程相关的文件(当有进程运行时才会有文件)
root: 用户权限(板子本身就是以root用户运行)
sbin: 超级用户命令、一般用户不可用(板子本身是超级用户 通过工具制作)
sys: 系统文件(系统运行时,系统加载后才会有文件)
tmp: 临时文件(比如插入新的设备时会产生临时文件)
usr: 命令(通过工具制作)
var: 存放下载的文件和软件 (可有可无)
3.根文件系统制作工具 --> BusyBox
特点:短小精悍
将很多普通的UNIX工具集成到一个很小的可执行文件中,为普通用户提供大多数常用的命令
busybox实现的命令都是精简版的命令,不支持扩展
4.根文件系统制作:
1)make menuconfig # 选择对应的配置
2)make # 编译 生成busybox的可执行文件
3)make install # 在busybox源码顶层目录生成_install目录,其中就生成了根文件系统所必须的文件
4)在_install目录中继续完善其他目录和文件
a)lib中的库可以直接拷贝交叉编译工具链中的库
b)proc、sys、tmp目录都是虚拟文件系统的挂载点
/***********************************************************************************************/
虚拟文件系统:将存在于内存的信息以文件的形式呈现给用户空间 生成的文件不占用磁盘空间只存在内存
proc: 与进程相关,通常会被挂载到/proc目录
sysfs:设备管理相关,通常会被挂载到/sys目录
tmpfs:相当于存放临时文件,通常会被挂载到/tmp目录
... ...
内核要想运行虚拟文件系统必须在配置内核时选择支持对应的虚拟文件系统
/***********************************************************************************************/
b)etc目录的制作
etc中为配置文件,比如虚拟文件系统的挂载点的设置、全局环境变量的设置等
5.使用打包工具将根文件系统压缩打包成想要的格式(即打包成板子上文件系统支持的格式这,样才可以被访问)
【35】嵌入式领域中常见文件系统格式:
cramfs: 只读文件系统,比如固件
jffs2: 可读可写文件系统,常用于Nor Flash或者Nand Flash
yaffs2: 可读可写文件系统,常用于Nand Flash
ext2/ext3:linux中最常用的文件系统类型,ext3之后的为日志文件系统
ramdisk: 内存文件系统,将部分内存当做磁盘使用(速度快、空间小、不可保存)
文件系统的选择:根据使用场合和存储介质