一、内核驱动与启动流程

1. Linux内核驱动
-
Nor Flash: 可线性访问,有专门的数据及地址总线(与内存访问方式相同)。
-
Nand Flash: 不可线性访问,访问需要控制逻辑(软件)。
2. Linux启动流程
-
ARM架构:
-
IRAM (4KB): 内部RAM,用于存储初始引导程序。
-
Nor Flash (2M): 存储u-boot程序。
-
内存 (64M): 用于加载内核和根文件系统。
-
Nand Flash (256M): 存储内核、根文件系统等数据。
-
-
-
启动过程:
-
Bootloader (u-boot):
-
初始化CPU、异常向量表、栈、时钟、内存等。
-
关闭看门狗、中断、Cache、MMU。
-
初始化相关硬件和软件协议。
-
将内核加载到内存。
-
向内核传递参数(根文件系统类型、位置、控制台等)。
-
启动内核。
-
-
内核 (kernel):
-
文件管理、内存管理、进程管理、网络管理、设备管理。
-
启动到最后阶段加载根文件系统。
-
init
进程启动后台服务程序、加载配置、启动shell和应用程序。
-
-
根文件系统 (rootfs):
- 包含程序(应用、系统、命令)、配置文件、库文件、普通文件(txt、mp3)。
-
3. Windows与Linux对比
-
Windows: 使用BIOS启动。
-
Linux: 使用bootloader引导内核启动,内核加载rootfs。
4. 具体启动步骤
-
Nor Flash:
- 系统上电后,PC指向0地址,直接执行Nor Flash中的u-boot程序。
-
Nand Flash:
-
系统上电后,自动搬移u-boot前4KB程序到IRAM。
-
CPU执行IRAM中的代码,u-boot初始化内存并将剩余代码搬移到内存执行。
-
5. 内核与文件系统
-
内核 (uImage):
-
启动前u-boot向内核传递参数(
tag_list
)。 -
Nand Flash: u-boot直接读取Nand Flash中的uImage并写入内存的0x30008000地址处,启动内核。
-
Ubuntu: 通过TFTP下载uImage到内存的0x30008000地址处,启动内核。
-
-
根文件系统 (rootfs):
-
Nand Flash: uImage启动到最后阶段时,直接挂载Nand Flash中的rootfs。
-
Ubuntu: uImage启动到最后阶段时,通过NFS挂载Ubuntu中的rootfs。
-
6. 前置步骤

-
向Nor Flash 0地址处烧写u-boot.bin。
-
拷贝uImage到Ubuntu的TFTP服务目录下。
-
将rootfs.tar.gz拷贝到Ubuntu的NFS服务目录下,并解压
sudo tar -xvf rootfs.tar.gz
7. U-Boot命令
-
环境变量管理:
-
printenv
: 打印环境变量。 -
reset
: 重启。 -
setenv serverip 192.168.1.3
: 设置环境变量。 -
saveenv
: 保存环境变量到Nand Flash。 -
setenv serverip
: 删除环境变量。
-
-
下载与启动:
-
tftp 0x30008000 uImage
: 通过TFTP协议下载uImage到内存的0x30008000地址处。 -
bootm 0x30008000
: 启动内存0x30008000地址处的内核。 -
go 0x30008000
: 运行内存0x30008000地址处的程序。
-
8. 设置启动参数
setenv bootargs console=ttySAC0,115200 root=/dev/nfs nfsroot=192.168.1.3:/home/linux/nfs/rootfs ip=192.168.1.123 init=/linuxrc
-
console: 控制台(终端)。
-
root: 根文件系统类型。
-
nfsroot: 根文件系统位置。
-
ip: 内核阶段使用的IP。
-
init: 指定init进程。
二、内核编译
1. 内核编译步骤
-
Kconfig : 定义
make menuconfig
的配置选项。 -
make menuconfig: 内核配置。
-
.config: 配置文件,决定哪些文件被编译进内核。
-
CONFIG_SSL = n
: 不启用SSL。 -
CONFIG_MM = y
: 启用内存管理。
-
-
makefile: 条件编译,编译内核。
2. 内核镜像类型
-
Image: 可以直接使用的内核镜像。
-
zImage: 一段解压代码 + Image的压缩文件。
-
uImage: 64字节的头信息 + zImage。
3. 地址相关代码
-
地址相关代码: 链接地址和加载地址一致。
-
地址无关代码: 链接地址和加载地址无关。
4. 跳转指令
-
相对跳转、短跳转 :
b fun
-
绝对跳转、长跳转 :
ldr pc, 0x00000000
5. 内核目录结构

6. 向内核新增文件
以向drivers/char
下添加demo.c
为例:
-
在
drivers/char
目录下新建并编辑demo.c
。 -
修改同层目录下的
Makefile
,添加:makefile
obj-$(CONFIG_DEMO) += demo.o
-
修改同层目录下的
Kconfig
,添加一个DEMO的配置。 -
执行
make menuconfig
。 -
执行
make uImage
。
7. 内核编译命令
-
配置内核
cp config_mini2440_t35 .config make menuconfig
-
编译内核
make uImage
8. 内核镜像说明
-
Image: 可直接使用的内核镜像。
-
zImage: 压缩的内核镜像,包含解压代码。
-
uImage: 带有64字节头信息的压缩内核镜像。
9. Makefile和Kconfig
- 每层目录都有
Makefile
和Kconfig
文件,用于配置和编译内核。
10. 编译流程总结
-
配置内核 : 使用
make menuconfig
选择内核配置选项。 -
编译内核 : 使用
make uImage
生成内核镜像。 -
验证内核: 确保生成的内核镜像可以正常启动。
11. 注意事项
-
配置文件 :
.config
文件决定了哪些模块被编译进内核。 -
条件编译 : 使用
obj-$(CONFIG_XXX)
进行条件编译。 -
目录结构 : 每层目录都有
Makefile
和Kconfig
文件,确保编译过程正确。
三、驱动程序
1. 设备文件与驱动模块
-
设备文件: 用户空间程序通过设备文件与驱动程序交互。
- 示例:
open("/dev/led");
- 示例:
-
驱动模块: 内核中的驱动程序负责控制硬件设备。
- 示例:
sys_open(led)
调用驱动模块。
- 示例:
2. 设备驱动类型
-
字符设备驱动: 数据按顺序访问,90%以上的设备使用字符设备驱动。
-
块设备驱动: 可以随机访问,主要用于存储设备。
-
网络设备驱动: 网卡,集成复杂协议,通过套接字通信,没有设备号,靠名字维护。
3. 设备号
-
设备号: 用于标识设备。
-
主设备号 (高12位): 区分设备类型。
-
次设备号 (低20位): 区分同类的不同设备。
-
-
示例:
dev_t
是32位设备号。
4. 创建设备节点
-
使用
mknod
命令创建设备节点mknod /dev/demo3 c 255 0
-
/dev/demo3
: 设备节点名。 -
c
: 字符设备。 -
255
: 主设备号。 -
0
: 次设备号。
-
5. 驱动模块结构
-
驱动模块 : 包含
open
、read
、write
、ioctl
、close
等函数。 -
示例:
drv_led
、drv_key
、drv_adc
分别对应LED、按键、ADC设备。
6. 设备驱动流程
-
应用程序调用设备文件:
open("/dev/led");
-
内核调用驱动模块:
sys_open(led)
→drv_led
。
-
驱动模块控制硬件设备:
drv_led
控制LED设备。
7. 设备号与驱动模块关系
-
每个设备号对应一个驱动模块。
-
内核通过设备号找到对应的驱动模块。
8. 设备号结构
+--------+--------+----+-------+
| 8 | 8 | 2 | 14 |
+--------+--------+----+-------+
设备类型 命令编号 数据 参数大小
魔幻数 流向
-
设备类型 (魔幻数): 8位,标识设备类型。
-
命令编号: 8位,标识具体命令。
-
数据流向: 2位,标识数据方向。
-
参数大小: 14位,标识参数大小。
9. 设备驱动总结
-
字符设备: 顺序访问,适用于大多数设备。
-
块设备: 随机访问,适用于存储设备。
-
网络设备: 复杂协议,通过套接字通信。
-
设备号: 区分设备类型和具体设备。
-
驱动模块: 内核中的程序,负责控制硬件设备。
10. 关键命令
-
创建设备节点 :
mknod
。 -
设备文件操作 :
open
、read
、write
、ioctl
、close
。
11. 注意事项
-
设备号分配: 确保主设备号和次设备号唯一。
-
驱动模块编写 : 需要实现
open
、read
、write
等函数。 -
设备文件操作: 应用程序通过设备文件与驱动模块交互。