写点什么

Linux 设备驱动系列(五)——字符驱动设备文件

  • 2024-04-26
    浙江
  • 本文字数:2141 字

    阅读完需:约 7 分钟

Linux设备驱动系列(五)——字符驱动设备文件

关注微信公众号:Linux 内核拾遗

文章来源:https://mp.weixin.qq.com/s/4Bl0j2dJx4rEx1MrSWMoYA

1 设备文件

设备文件不是普通的文件,它提供了一种便利的方式来访问系统资源,而不需要应用开发者了解底层设备的工作原理。与大多数 Unix 系统一样,设备驱动程序本身就是 Linux 内核的一部分。


  1. 从应用程序角度来看,它表现得跟文件一样,允许程序从中读取数据、写入数据以及进行内存映射等。

  2. 从内核角度看,当设备文件被访问的时候,内核需要识别访问的 I/O 请求并将其传递给设备驱动程序,由设备驱动执行某些操作以完成请求的处理。


所有的设备文件都保存在/dev/目录下。


设备文件通常在系统初始化的时候创建,设备文件关联的设备可以是真实的硬件设备,例如/dev/ttyS0、/dev/hda1 等;也可以是伪设备,例如/dev/null,所有往/dev/null 的写请求都会被丢弃。


2 创建设备文件

有两种创建设备文件的方式:手动创建和自动创建。

2.1 手动创建设备文件

mknod 命令可以手动创建一个设备文件:


mknod -m <permissions> <name> <device type> <major> <minor>
复制代码


说明:


  • name:设备文件名,即/dev/<name>;

  • device type:设备类型,c - 字符设备,b - 块设备;

  • <major>:<minor>:设备驱动程序的主/次设备号;

  • -m <permissions>:设备文件权限,跟普通文件类似。


这种方式允许任何人创建设备文件,并且可以在设备驱动加载之后再创建。

2.1.1 完整示例

manually_device_file.c


#include<linux/kernel.h>#include<linux/init.h>#include<linux/module.h>#include <linux/kdev_t.h>#include <linux/fs.h> dev_t dev = 0;
static int __init my_init(void){ if((alloc_chrdev_region(&dev, 0, 1, "manually_dev")) <0){ pr_err("Cannot allocate major number for device\n"); return -1; } pr_info("Kernel Module Inserted\n"); return 0;}static void __exit my_exit(void){ unregister_chrdev_region(dev, 1); pr_info("Kernel Module Removed\n");}
module_init(my_init);module_exit(my_exit); MODULE_LICENSE("GPL");MODULE_AUTHOR("feifei <feifei@gmail.com>");MODULE_DESCRIPTION("Simple linux device driver");MODULE_VERSION("1.1");
复制代码


编译后往内核插入模块,通过 mknod 手动创建设备文件:


2.2 自动创建设备文件

udev 是 Linux 内核中的设备管理器,它能够在/dev 目录下自动创建或者移除设备节点,自动创建的设备文件都会被 udev 管理起来。


基本的使用步骤如下:


  1. 引用头文件"linux/device.h"和"linux/kdev_t/h";

  2. 创建 struc class;

  3. 使用前面 sruct class 创建设备。

2.2.1 创建 struct class

可以通过 class_create 为设备驱动创建对应的 struct class,对应到/sys/class 下面的一个结构,后续可以使用 class_destroy 来销毁 struc class:


struct class * class_create(struct module *owner, const char *name);void class_destroy (struct class * cls);
复制代码

2.2.2 创建设备

可以通过 device_create 创建字符设备,并注册到指定的 struct class,相应地会在/sys/device 下面创建对应的结构。


struct device *device_create(struct *class, struct device *parent, dev_t dev,                             void * drvdata, const char *fmt, ...);void device_destroy (struct class * class, dev_t devt);
复制代码


说明:


  • class:要注册的目标 struct class;

  • parent:即将创建的新设备的父设备;

  • devt:设备的主/次设备号;

  • drvdata:往设备回调函数传递的数据;

  • fmt 和...:设备名称。

2.2.3 完整示例

dynamically_device_file.c


#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/kdev_t.h>#include <linux/fs.h>#include <linux/err.h>#include <linux/device.h> dev_t dev = 0;static struct class *dev_class;
static int __init my_init(void){ if((alloc_chrdev_region(&dev, 0, 1, "my_dev")) <0){ pr_err("Cannot allocate major number for device\n"); return -1; } pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
dev_class = class_create(THIS_MODULE,"my_class"); if(IS_ERR(dev_class)){ pr_err("Cannot create the struct class for device\n"); goto r_class; }
if(IS_ERR(device_create(dev_class,NULL,dev,NULL,"my_device"))){ pr_err("Cannot create the Device\n"); goto r_device; } pr_info("Kernel Module Inserted\n"); return 0;
r_device: class_destroy(dev_class); r_class: unregister_chrdev_region(dev,1); return -1;}
static void __exit my_exit(void){ device_destroy(dev_class,dev); class_destroy(dev_class); unregister_chrdev_region(dev, 1); pr_info("Kernel Module Removed\n");} module_init(my_init);module_exit(my_exit); MODULE_LICENSE("GPL");MODULE_AUTHOR("feifei <feifei@gmail.com>");MODULE_DESCRIPTION("Simple linux device driver");MODULE_VERSION("1.2");
复制代码


编译完成后插入内核模块,可以看到/dev 相面自动创建了对应的设备文件:



关注微信公众号:Linux 内核拾遗

文章来源:https://mp.weixin.qq.com/s/4Bl0j2dJx4rEx1MrSWMoYA


发布于: 刚刚阅读数: 5
用户头像

聚沙成塔 2023-01-12 加入

分享Linux内核开发相关的编程语言、开发调试工具链、计算机组成及操作系统内核知识、Linux社区最新资讯等

评论

发布
暂无评论
Linux设备驱动系列(五)——字符驱动设备文件_Linux内核_Linux内核拾遗_InfoQ写作社区