嵌入式知识点总结 Linux驱动 (八)-Linux设备驱动
针对于嵌入式软件杂乱的知识点总结起来,提供给读者学习复习对下述内容的强化。
目录
1.请简述主设备号和次设备号的用途?
2.字符驱动设备怎么创建设备文件?
3.设备驱动程序中如何注册一个字符设备?分别解释一下它的几个参数的含义?
4./dev/下面的设备文件是怎么样创建出来的?
5.Linux设备中字符设备和块设备有什么主要区别?分别举例?
6.驱动中操作物理绝对地址为什么要先ioremap?
7.insmod,rmmod一个驱动模型,会执行模块中的哪个函数?在设计上要注意哪些问题?
8.NAND驱动的probe流程
9.Linux驱动开发中,常用的调试方法有哪些?
1.请简述主设备号和次设备号的用途?
主设备号:主设备号标识设备对应的特定的驱动程序。虽然现代的linux内核允许多个驱动程序共享主设备号,但我们看待的大多数设备仍然按照“一个主设备对应一个驱动程序”的原则组织。
次设备号:次设备号由内核使用,用于确定由主设备号对应驱动程序中的各个设备。依赖于驱动程序的编写方式,我们可以通过次设备号获得一个指向内核设备的直接指针,也可将此设备号当作设备本地数组的索引。
在 Linux 设备驱动中,主设备号(Major Number) 和 次设备号(Minor Number) 用于 标识设备类型 和 区分同类型设备实例,它们共同决定了 /dev
目录下的 设备文件 如何映射到具体的设备驱动程序。
- 区分同一类型的不同设备实例。
- 设备驱动可以使用次设备号来区分多个设备(如多个硬盘分区
/dev/sda1
、/dev/sda2
)。
2.字符驱动设备怎么创建设备文件?
在 Linux 下,字符设备(Character Device)需要一个 设备文件(Device File) 供用户空间访问,设备文件通常位于 /dev
目录下。例如 /dev/ttyS0
、/dev/i2c-1
。
创建字符设备文件的 完整流程 如下:
注册设备号(指定或自动分配)
dev_t dev;
alloc_chrdev_region(&dev, 0, 1, "my_device"); // 自动分配
int major = MAJOR(dev);
初始化 cdev
并注册
static struct cdev my_cdev;
cdev_init(&my_cdev, &my_fops);
cdev_add(&my_cdev, dev, 1);
自动创建设备文件 /dev/my_device
struct class *my_class = class_create(THIS_MODULE, "my_class");
device_create(my_class, NULL, dev, NULL, "my_device");
用户空间访问
ls -l /dev/my_device # 查看设备文件
echo "test" > /dev/my_device # 写入
cat /dev/my_device # 读取
卸载时清理
device_destroy(my_class, dev);
class_destroy(my_class);
cdev_del(&my_cdev);
unregister_chrdev_region(dev, 1);
3.设备驱动程序中如何注册一个字符设备?分别解释一下它的几个参数的含义?
4./dev/下面的设备文件是怎么样创建出来的?
普遍说法有三种方式,devfs机制,udev机制,再有一个就是手动创建设备节点。谈谈个人见解
1.devfs机制从来没用过,应该是2.6以前的内核使用的;
2.udev,其实就是现在常用的device_create()、class create()这一套接口,所谓udev是上层用户空间程序,是基于驱动中创建使用了这两个接口而起作用的,但是udev在日常开发中几乎接触不到我们只需在驱动中调用创建节点的这两个API就ok了,剩下的工作就交给udev去做了,有想深究它具体实现原理的那就自己去研究吧,我觉得会用就行了;
3.mknod,新手最常用的一种创建设备节点方法,但并非入门后就再没有用途,在某些情境下,或许有人不想使用udev机制,于是把节点创建工作写在脚本里,这样也是无可厚非的。
mknod <设备文件路径> <设备类型> <主设备号> <次设备号>
<设备文件路径>
:设备文件的路径,例如/dev/mydevice
。<设备类型>
:字符设备使用c
,块设备使用b
。<主设备号>
:标识设备的主设备号(例如,硬盘的主设备号是 8)。<次设备号>
:标识设备的次设备号(例如,第一个硬盘分区是 1)。
mknod /dev/mydevice c 240 0 # 创建一个字符设备文件,主设备号为 240,次设备号为 0
现代 Linux 内核通过 udev
(用户设备管理器)自动创建设备文件。当设备驱动程序加载并注册设备时,udev
会自动在 /dev/
目录下创建设备文件。
自动创建设备文件的过程:
驱动程序通过调用 register_chrdev_region()
(字符设备)或 register_blkdev()
(块设备)来注册设备。
内核通过设备号(主设备号和次设备号)识别设备。
udev
守护进程监听内核事件(如设备插入、拔出),根据设备信息自动创建或删除 /dev/
中的设备文件。
alloc_chrdev_region(&dev, 0, 1, "my_device");
udev
可以根据设备的属性来决定如何命名设备文件。可以在 /etc/udev/rules.d/
目录中定义规则文件,定制设备文件的创建方式。
KERNEL=="mydevice", NAME="mydevice", MODE="0666"
5.Linux设备中字符设备和块设备有什么主要区别?分别举例?
Linux中I/0设备分为两类:块设备和字符设备。两种设备本身没有严格限制,但是,基于不同的功能进行了分类。
字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取。相反,此类设备支持按字节/字符来读写数据。字符终端、串口、鼠标、键盘、摄像头、声卡和显卡等就是典型的字符设备,
块设备:应用程序可以随机访问设备数据,程序可自行确定读取数据的位置。硬盘是典型的块设备,应用程序可以寻址磁盘上的任何位置,并由此读取数据。此外,数据的读写只能以块(通常是512B)的倍数进行。与字符设备不同,块设备并不支持基于字符的寻址。如:u盘,SD卡,磁盘等。
特性 | 字符设备 | 块设备 |
---|---|---|
数据存取方式 | 按字符或字节流(连续读取或写入) | 按块(通常为512字节或更大)进行数据存取 |
设备访问 | 顺序访问,通常一次读写一个字节或字符 | 随机访问,支持按块进行访问 |
典型设备 | 串口设备、键盘、鼠标、终端等 | 硬盘、SSD、U盘、光驱等 |
驱动接口 | 通过 read() 、write() 等系统调用进行读写操作 | 通过块设备接口(如 open() 、read() 、write() 等)进行块操作 |
缓存管理 | 不提供缓存(用户可以自行管理) | 提供缓存管理,块设备驱动通常会有缓存(例如页缓存) |
使用方式 | 适用于数据流式处理(如串口通信) | 适用于大容量数据的存取和管理(如文件系统操作) |
6.驱动中操作物理绝对地址为什么要先ioremap?
ioremp是内核中用来将外设寄存器物理地址映射到主存上去的接口,即将io地址空间映射到虚拟地址空间上去,便于操作。为什么非要映射呢,因为保护模式下的cpu只认虚拟地址,不认物理地址,给它物理地址它并不帮你做事,所以你要操作外设上的寄存器必须先映射到虚拟内存空间,拿着虚拟地址去跟cpu对接,从而操作寄存器。
因为有MMU,从硬件上理解!MMU 和 保护模式 使得只有虚拟地址能够与 CPU 对接操作,物理地址无法直接访问外设。
7.insmod,rmmod一个驱动模型,会执行模块中的哪个函数?在设计上要注意哪些问题?
分别会执行 module_init()和 module_exit()指定的init函数和exit函数。要注意的就是,尽量使在init函数中出现的资源申请及使用,都要有对应的释放操作在exit中,即init申请,eixt释放。
8.NAND驱动的probe流程
9.Linux驱动开发中,常用的调试方法有哪些?
利用printk,查看OOP消息,利用strace,利用内核内置的hacking选项,利用ioctl方法,利用/proc文件系统,使用kgdb。