设备节点
在Linux中,设备节点(也称为设备文件)是文件系统中的一种特殊文件,它提供了一个接口来访问硬件设备。设备节点允许用户空间程序通过标准的文件操作(如open
、read
、write
、close
等)来与硬件设备交互,而无需了解硬件的具体细节。
设备节点通常位于/dev
目录下,这是专门用于存放设备文件的目录。在/dev
目录中,每个设备节点都代表了一个硬件设备或设备的一个特定部分(如磁盘的一个分区)。
设备节点根据其代表的设备类型被分为两类:字符设备(character device)和块设备(block device)。
-
字符设备:字符设备以字符为单位进行数据的读写操作。这种设备通常不支持随机访问,即每次读写操作都是从设备的当前位置开始的。字符设备通常用于那些需要按字节或按行进行输入输出的设备,如串口、键盘、鼠标、打印机等。
-
块设备:块设备以数据块为单位进行数据的读写操作。这种设备支持随机访问,即可以跳过设备上的某些部分直接访问其他部分。块设备通常用于存储大量数据,如硬盘、U盘、SD卡等。
设备号的申请
在Linux中,每个字符设备都需要一个唯一的设备号来标识。设备号由主设备号和次设备号组成,其中主设备号用于区分不同类型的设备,而次设备号则用于区分同一类型下的不同设备。
设备号是一个无符号32位整数,数据类型为dev_t,设备号分为两部分:
主设备号:占高12位,用来表示驱动程序相同的一类设备
次设备号:占低20位,用来表示被操作的哪个具体设备
应用程序打开一个设备文件时,通过设备号来查找定位内核中管理的设备。
MKDEV宏用来将主设备号和次设备号组合成32位完整的设备号,用法:
dev_t devno;
int major = 11;//主设备号
int minor = 0;//次设备号
devno = MKDEV(major,minor);
MAJOR宏用来从32位设备号中分离出主设备号,用法:
dev_t devno = MKDEV(11,0);
int major = MAJOR(devno);
MINOR宏用来从32位设备号中分离出次设备号,用法:dev_t devno = MKDEV(11,0);
int minor = MINOR(devno);
1. 静态指定设备号
如果开发者知道想要使用的设备号,可以使用register_chrdev_region
函数来静态指定设备号。
int register_chrdev_region(dev_t from, unsigned count, const char *name);
from
:指定的起始设备号(主设备号和次设备号的组合)。count
:要申请的设备号的数量。name
:设备驱动的名称。
2.动态申请设备号
如果开发者不确定使用哪个设备号,可以使用alloc_chrdev_region
函数来动态申请一定范围内的设备号。
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
dev
:用于存储申请到的设备号的指针。baseminor
:次设备号的起始值。count
:要申请的设备号的数量。name
:设备驱动的名称。
如果已知一个设备的主次设备号,应用层指定好设备文件名,那么可以用mknod命令在/dev目录创建代表这个设备的文件,即此后应用程序对此文件的操作就是对其代表的设备操作。
下面是在Linux上面操作的命令:
在include/linux 查询指定函数:
grep 名称 ./ -r -n
查看当前设备号:
cat /proc/devices | grep mychar
创建一个字符设备:
sudo mknod /dev/mydev c 11 0
mknod
:是创建特殊文件的命令。/dev/mydev
:是你要创建的设备文件的路径和名称。c
:指定要创建的文件是一个字符设备(character device)。与之相对的是块设备(block device),用b
表示。11
:是主设备号(major number),用于区分不同类型的设备。在这个例子中,选择了11作为主设备号。请注意,选择一个已经在使用中的主设备号可能会导致冲突。0
:是次设备号(minor number),用于在同一种类型的设备中区分不同的设备实例。在这个例子中,选择了0作为次设备号。
若在应用程序中要创建设备可以调用系统调用函数mknod:
int mknod(const char *pathname,mode_t mode,dev_t dev);
- pathname:带路径的设备文件名,无路径默认为当前目录,一般都创建在/dev下
- mode:文件权限 位或 S_IFCHR/S_IFBLK
- dev:32位设备号
返回值:成功为0,失败-1
3.释放设备号:
void unregister_chrdev_region(dev_t from, unsigned count);
功能:释放设备号
- from:已成功分配的设备号将被释放
- count:申请成功的设备数量
基本模板示例(mychar.c):
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
int major = 11; //主设备号
int minor = 0; //次设备号
int char_num = 1; //设备号数量
int __init mychar_init(void)
{
int ret = 0;
//将主次设备号合成一个完整设备号
dev_t devno = MKDEV(major, minor);
/* 手动申请设备号 若返回值不为0,则自动设置*/
ret = register_chrdev_region(major, char_num, "mychar");
if (ret) {
/* 动态申请设备号 */
ret = alloc_chrdev_region(&devno, minor, char_num, "mychar");
if(ret){
printk("get devno failed\n");
return -1;
}
/*申请成功 更新设备号*/
major = MAJOR(devno);//自动分配的设备号不一定是11,所以需要分离出其主设备号
//因为次设备号都是从0开始.
}
return 0;
}
void __exit mychar_e