跳转至

Linux 设备文件如何创建?

Linux 系统中,有句话说“一切皆是文件”。这是 Unix/Linux 的基本哲学之一,即使不是文件类的东西都会采用文件形式进行访问读写控制,这也算是Linux 特点之一了。

阅读本章节你将会获取到大概知识点:

对于设备文件是如何创建,销毁,其中过程中使用到的接口,还有例子。

本文章主要以字符设备驱动文件为主!

我是你们的老朋友,@hywelstar,别忘记点关注了《码思途远》!分享嵌入式的新鲜事,技术笔记相关。

1. 设备文件

了解设备文件,首先对概念进行一个了解。

设备文件,顾名思义是和设备有关,承载着与特定硬件进行交互的一个文件。它提供了一种便利的方式来访问系统资源,而不需要应用开发者了解底层设备的工作原理。与大多数Unix系统一样,设备驱动程序本身就是Linux内核的一部分。

应用程上看:就当文件一样进行操作,允许程序从中读取数据、写入数据以及进行内存映射等。

内核上看:当设备文件被访问的时候,内核需要去识别请求,再传递给设备驱动,驱动将会根据内容进行执行相关的操作。

设备文件包含真是的硬件设备,比如:

ls /dev -al

会发现很多设备,串口设备/dev/ttymxc2 /dev/video等等。

截图一部分:

image-20240701134952458

2. 设备驱动文件创建

关于驱动设备文件的创建,一般都是在内核驱动里面进行创建,另外可以手动命令创建。

2.1 设备文件创建关键

首先了解创建设备文件主要结构体:

struct classstruct device,路径分别在./include/linux/device/class.h./include/linux/device.h

由于结构体较大不一一展开,主要包含设备类的信息,设备的信息。

创建过程:

image-20240701152724406

创建设备类:

struct class *class_create(struct module *owner, const char *name);
  • owner : THIS_MODULE
  • name : 设备名

创建具体的设备目录和设备属性文件

struct device *device_create(struct class *class, struct device *parent,
                             dev_t devt, void *drvdata, const char *fmt, ...)
  • class: 设备所属的类。
  • parent: 设备的父设备,可以为 NULL。
  • devt: 设备号(由主设备号和次设备号组成)。
  • drvdata: 驱动程序私有数据,可以为 NULL。
  • fmt: 设备名称格式,可以包含格式化字符串。

2.2 以串口驱动为例分析

这里以串口为例分析,创建设备文件过程。

对于串口驱动是属于TTY设备的一种,这里雨TTY子系统联系比较紧密,这里分析创建驱动设备文件过程:

image-20240701160726430

如图分析

前期都是注册相关信息,将uart信息填入,注册成一个TTY设备,在通过tty_cdev_add 加入这个列表当中。当设备启动进入文件系统后将会调用char_dev_init 调用tty_init。 在tty_init将会调用device_create创建设备。

2.3 注销

驱动设备的注销过程两个步骤:

删除设备类目录下的设备: 这是通过调用 device_destroy 函数完成的,该函数会从 /dev 目录中删除相应的设备节点,并移除与之关联的设备对象。

删除 /sys/class 目录下的设备类: 这是通过调用 class_destroy 函数完成的,该函数会从 /sys/class 目录中删除设备类,并释放相应的资源。

3. 其他

3.1 主设备号和次设备号

设备文件的标识由主设备号和次设备号组成。

  • 主设备号:标识设备类型(如字符设备、块设备)。
  • 次设备号:标识同一类型设备中的具体设备。

在内核中,可以使用 MKDEV 宏来创建 dev_t 类型的设备号。具体的序号代码可以查看源代码。

image-20240701161558823

3.2 设备文件操作

设备驱动程序通过文件操作函数与用户空间交互。这些函数包括:

  • open:打开设备文件
  • release:关闭设备文件
  • read:从设备读取数据
  • write:向设备写入数据

4. 总结

对于设备文件的创建主要先创建一个设备类文件,对设备类文件进行选定,然后创建这个设备文件的相关属性,创建节点。对于这个设备类的文件操作将会在驱动中进行实现处理,可能会使用到一些内核与用户层的数据交互等。