Linux下手动/自动创建设备节点
2021/10/31 7:13:12
本文主要是介绍Linux下手动/自动创建设备节点,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文章目录
- 前言
- 一、手动创建
- 1.cdev结构体的简要介绍
- 2.代码文件
- 1>.cdev.c文件
- 2>.Makefile文件
- 3>.app.c文件
- 3.执行结果
- 二、自动创建
- 1.创建类
- 2.代码文件
- 1>.autocdev.c文件
- 2>.Makefile文件
- 3>.app.c文件
- 3.执行结果
- 总结
前言
本文的主要内容是Linux下手动/自动创建设备节点。
一、手动创建
1.cdev结构体的简要介绍
cdev结构体:描述字符设备的结构体,定义在/linux-4.1.15/include/linux/cdev.h中。
struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; };
使用cdev_init函数初始化cdev结构体成员变量;
void cdev_init(struct cdev *, const struct file_operations *);
第一个参数:要初始化的cdev;
第二个参数:文件操作集。
使用cdev_add函数注册到内核。
int cdev_add(struct cdev *, dev_t, unsigned);
第一个参数: cdev的结构体指针;
第二个参数:设备号;
第三个参数:次设备号的数量。
删除/注销字符设备:
void cdev_del(struct cdev *);
字符设备注册完以后不会自动生成设备节点,需要使用mknod命令创建一个设备节点。
格式如下:
mknod 名称 类型 主设备号 次设备号
这里的类型一般为c,次设备号为0,主设备号是动态分配好的。
本例将使用如下命令进行创建。
mknod /dev/test c 242 0
注意这里的242是我动态生成的主设备号,每个人可能不太一样,执行完可以通过ls /dev/test查看是否已生成相应的文件。
2.代码文件
1>.cdev.c文件
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/kdev_t.h> #include <linux/cdev.h> #define DEVICE_NUMBER 1 //设备数量 #define DEVICE_SNAME "stachardevice" //静态名称 #define DEVICE_DNAME "dynchardevice" //动态名称 #define DEVICE_MINOR_NUMBER 0 //动态请求的第一个次设备号,通常为0 static int major_num,minor_num; struct cdev cdev; module_param(major_num,int,S_IRUSR); //主设备号 module_param(minor_num,int,S_IRUSR); //次设备号 int chrdev_open(struct inode *inode,struct file *file) { printk("chrdev_open!\n"); return 0; } struct file_operations chrdev_ops = { .owner = THIS_MODULE, .open = chrdev_open }; static int hello_init(void) { dev_t dev_num; int ret; if(major_num) //传入了主设备号用静态的方法 { printk("major_num = %d\n",major_num); //打印主设备号 printk("minor_num = %d\n",minor_num); //打印次设备号 dev_num = MKDEV(major_num,minor_num); //主次设备号合成一个dev_t类型 ret = register_chrdev_region(dev_num,DEVICE_NUMBER,DEVICE_SNAME); //静态分配 if(ret < 0) { printk("register_chrdev_region error!\n"); } printk("register_chrdev_region ok!\n"); } else //没有传入主设备号用动态的方法 { ret = alloc_chrdev_region(&dev_num,DEVICE_MINOR_NUMBER,DEVICE_NUMBER,DEVICE_DNAME); //动态分配 if(ret < 0) { printk("allo_chrdev_region error!\n"); } printk("allo_chrdev_region ok!\n"); major_num = MAJOR(dev_num); //从dev_t中分离出主设备号 minor_num = MINOR(dev_num); //从dev_t中分离出次设备号 printk("major_num = %d\n",major_num); //打印主设备号 printk("minor_num = %d\n",minor_num); //打印次设备号 } cdev.owner = THIS_MODULE; cdev_init(&cdev,&chrdev_ops); //初始化cdev cdev_add(&cdev,dev_num,DEVICE_NUMBER); //注册到内核 return 0; } static int hello_exit(void) { unregister_chrdev_region(MKDEV(major_num,minor_num),DEVICE_NUMBER); //注销,申请几个注销几个 cdev_del(&cdev); //删除cdev printk("unregister_chrdev_region ok!\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL");
2>.Makefile文件
obj-m += cdev.o KDIR:=/linux/linux-4.1.15 PWD?=$(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean
3>.app.c文件
#include "stdio.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "unistd.h" int main(int argc, char *argv[]) { int fd; char buf[64] ={0}; fd = open("/dev/test", O_RDWR); //指定打开/dev/test,打开成功则打印"chrdev_open!" if(fd < 0) { perror("open error!\n"); //相当于printf("open error!\n"); return fd; } return 0; }
3.执行结果
开发板接收到驱动文件和编译后的app文件后,先加载驱动动态分配主设备号,这时候执行app文件是不能成功打印的,因为/dev文件夹下还没有名为test的设备,需要使用下面的命令生成。
mknod /dev/test c 242 0
这里的主设备号一定要和动态分配的相同。
然后使用如下命令发现/dev/test已经生成了。
ls /dev/test
再运行app文件,就打印了我们想要的信息了。
注意,驱动卸载后虽然app不能再成功运行了,但是咱们创建的/dev/test文件依然存在,记得删除掉。
这就是手动创建设备节点的过程,我们发现整个流程还是比较麻烦,下面来看自动创建的过程。
二、自动创建
1.创建类
创建类的定义在/linux-4.1.15/include/linux/device.h中。
#define class_create(owner, name) \ ({ \ static struct lock_class_key __key; \ __class_create(owner, name, &__key); \ })
删除类:
extern void class_destroy(struct class *cls);
在/sys/class目录下可以查看创建的类。
在类下创建设备:
struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
device_create是个可变参数函数。
第一个参数:设备注册在该类下。
第二个参数:父设备,一般为NULL。
第三个参数:设备号。
第四个参数:设备可能会使用的数据,一般为NULL。
第五个参数:设备名字,生成后会在/dev目录下。
其返回值就是创建好的设备。
删除/注销设备:
extern void device_destroy(struct class *cls, dev_t devt);
第一个参数:类名称。
第二个参数:设备号。
2.代码文件
1>.autocdev.c文件
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/kdev_t.h> #include <linux/cdev.h> #include <linux/device.h> #define DEVICE_NUMBER 1 //设备数量 #define DEVICE_SNAME "stachardevice" //静态名称 #define DEVICE_DNAME "dynchardevice" //动态名称 #define DEVICE_MINOR_NUMBER 0 //动态请求的第一个次设备号,通常为0 #define DEVICE_CLASS_NAME "chrdev_class" //创建的类名称 #define DEVICE_NODE_NAME "chrdev_node" //创建的节点名称 static int major_num,minor_num; dev_t dev_num; struct cdev cdev; struct class *class; struct device *device; module_param(major_num,int,S_IRUSR); //主设备号 module_param(minor_num,int,S_IRUSR); //次设备号 int chrdev_open(struct inode *inode,struct file *file) { printk("chrdev_open!\n"); return 0; } struct file_operations chrdev_ops = { .owner = THIS_MODULE, .open = chrdev_open }; static int hello_init(void) { int ret; if(major_num) //传入了主设备号用静态的方法 { printk("major_num = %d\n",major_num); //打印主设备号 printk("minor_num = %d\n",minor_num); //打印次设备号 dev_num = MKDEV(major_num,minor_num); //主次设备号合成一个dev_t类型 ret = register_chrdev_region(dev_num,DEVICE_NUMBER,DEVICE_SNAME); //静态分配 if(ret < 0) { printk("register_chrdev_region error!\n"); } printk("register_chrdev_region ok!\n"); } else //没有传入主设备号用动态的方法 { ret = alloc_chrdev_region(&dev_num,DEVICE_MINOR_NUMBER,DEVICE_NUMBER,DEVICE_DNAME); //动态分配 if(ret < 0) { printk("allo_chrdev_region error!\n"); } printk("allo_chrdev_region ok!\n"); major_num = MAJOR(dev_num); //从dev_t中分离出主设备号 minor_num = MINOR(dev_num); //从dev_t中分离出次设备号 printk("major_num = %d\n",major_num); //打印主设备号 printk("minor_num = %d\n",minor_num); //打印次设备号 } cdev.owner = THIS_MODULE; cdev_init(&cdev,&chrdev_ops); //初始化cdev cdev_add(&cdev,dev_num,DEVICE_NUMBER); //注册到内核 class = class_create(THIS_MODULE,DEVICE_CLASS_NAME); //注册类 device = device_create(class,NULL,dev_num,NULL,DEVICE_NODE_NAME); //注册设备 return 0; } static int hello_exit(void) { unregister_chrdev_region(MKDEV(major_num,minor_num),DEVICE_NUMBER); //注销,申请几个注销几个 cdev_del(&cdev); //注销cdev device_destroy(class,dev_num); //注销设备 class_destroy(class); //注销类 printk("unregister_chrdev_region ok!\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL");
2>.Makefile文件
obj-m += autocdev.o KDIR:=/linux/linux-4.1.15 PWD?=$(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean
3>.app.c文件
#include "stdio.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "unistd.h" int main(int argc, char *argv[]) { int fd; char buf[64] ={0}; fd = open("/dev/chrdev_node", O_RDWR); //这里的chrdev_node设备是在加载时就生成的 if(fd < 0) { perror("open error!\n"); return fd; } return 0; }
3.执行结果
开发板接收到驱动文件和编译后的app文件后,先加载驱动。
通过如下命令查看所有类。
ls /sys/class
通过如下命令查看所有设备。
ls /dev
结果如下图。
可以看到我们预先设置的类和设备文件都成功生成了。
运行app文件,也成功打印了我们预设的信息。
卸载驱动后再看类和设备,发现它们都随着驱动的卸载而删除了。
这就是自动创建设备节点的过程,我们发现要比手动创建省事多了!
总结
以上就是Linux下手动/自动创建设备节点的所有内容了,要深刻体会其中的关联与不同!
这篇关于Linux下手动/自动创建设备节点的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-18git仓库有更新,jenkins 自动触发拉代码怎么配置的?-icode9专业技术文章分享
- 2024-12-18Jenkins webhook 方式怎么配置指定的分支?-icode9专业技术文章分享
- 2024-12-13Linux C++项目实战入门教程
- 2024-12-13Linux C++编程项目实战入门教程
- 2024-12-11Linux部署Scrapy教程:新手入门指南
- 2024-12-11怎么将在本地创建的 Maven 仓库迁移到 Linux 服务器上?-icode9专业技术文章分享
- 2024-12-10Linux常用命令
- 2024-12-06谁看谁服! Linux 创始人对于进程和线程的理解是…
- 2024-12-04操作系统教程:新手入门及初级技巧详解
- 2024-12-04操作系统入门:新手必学指南