Linux设备驱动开发-字符设备

news2024/11/17 19:34:46

阅读引言: 从linux文件的种类、字符设备的创建、设备号、申请设备号、cdev对象和字符设备的对应关系、应用层调用到我们编写的设备驱动方法合集的流程。

目录

一、Linux文件的种类

二、Linux对设备的分类

三、驱动程序如何向应用层提供接口

四、Linux中设备的划分

五、设备号的申请和注销

六、申请设备并根据设备号创建设备文件演示

七、字符设备驱动框架分析


一、Linux文件的种类

linux的文件种类:

1. -:普通文件

2. d:目录文件

3. p:管道文件

4. s:本地socket文件

5. l:链接文件

6. c:字符设备

7. b:块设备

相关说明

普通文件里面里面有内容

目录文件里面有内容

管道文件无内容, 它是内核维护的一段半双工发缓冲区管道,用于进程间通信

本地socket文件: 用于本地socket通信

链接文件: 分为软连接和硬链接, 软链接文件的内容就是一个路径, 硬链接其实就是将inode对象, 和内容拷贝了一份

字符设备: 无内容, 只是通过vfs抽象给应用层操作该设备的一个文件

块设备: 无内容和字符设备文件同理。

假设有这样一个问题, 有人问你linux下的文件有哪几种, 这里有一个顺口溜: bcd-lsp, 对应的就是上面其中文件类型。

设备号马上就说。

二、Linux对设备的分类

Linux内核按驱动程序实现模型框架的不同,将设备分为三类:

1. 字符设备:按字节流形式进行数据读写的设备,一般情况下按顺序访问,数据量不大,一般不设缓存

2. 块设备:按整块进行数据读写的设备,最小的块大小为512字节(一个扇区),块的大小必须是扇区的整数倍,Linux系统的块大小一般为4096字节(4k),随机访问,设缓存以提高效率

3. 网络设备:针对网络数据收发的设备

这里有一个小补充: 我们都知道在类Unix系统中一切皆文件, 其实这句话应该打上引号, 因为网络设备没有看成文件。大家想想看是不是这个道理。

网卡的表现形式

三、驱动程序如何向应用层提供接口

大家思考这样一个问题, 不同的驱动程序难道要向上层都提供一套操作该设备的API吗, 这样的话应用层的开发人员可能会想死的。

那又是如何解决这个问题的呢?

通过VFS虚拟文件系统来做, 对应用层的API还是那一套open close read write, 这样就大大减轻了上层开发的压力和难度。

四、Linux中设备的划分

内核用设备号来区分同类里不同的设备,设备号是一个无符号32位整数,数据类型为dev_t,设备号分为两部分:

1. 主设备号:占高12位,用来表示驱动程序相同的一类设备

2. 次设备号:占低20位,用来表示一类设备中的具体哪一个设备

应用程序打开一个设备文件时,通过设备号来查找定位内核中管理的设备。

MKDEV宏用来将主设备号和次设备号组合成32位完整的设备号,用法:

dev_t devno;

int major = 251;//主设备号

int minor = 2;//次设备号

devno = MKDEV(major,minor);

MAJOR宏用来从32位设备号中分离出主设备号,用法:

dev_t devno = MKDEV(249,1);

int major = MAJOR(devno);

MINOR宏用来从32位设备号中分离出次设备号,用法:

dev_t devno = MKDEV(249,1);

int minor = MINOR(devno);

这些宏本质也就是简简单单的位操作吧了。

如果已知一个设备的主次设备号,应用层指定好设备文件名,那么可以用mknod命令在/dev目录创建代表这个设备的文件,即此后应用程序对此文件的操作就是对其代表的设备操作,mknod用法如下:

在应用程序中如果要创建设备可以调用系统调用函数mknod,其原型如下:

int mknod(const char *pathname,mode_t mode,dev_t dev);

pathname:带路径的设备文件名,无路径默认为当前目录,一般都创建在/dev下

mode:文件权限 位或 S_IFCHR/S_IFBLK

dev:32位设备号

返回值:成功为0,失败-1

五、设备号的申请和注销

字符驱动开发的第一步是通过模块的入口函数向内核添加本设备驱动的代码框架,主要完成:

1. 申请设备号

2. 定义、初始化、向内核添加代表本设备的结构体元素

int register_chrdev_region(dev_t from, unsigned count, const char *name)

功能:手动分配设备号,先验证设备号是否被占用,如果没有则申请占用该设备号

参数:

from:自己指定的设备号

count:申请的设备数量

name:/proc/devices文件中与该设备对应的名字,方便用户层查询主设备号

返回值:

成功为0,失败负数,绝对值为错误码

int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count, const char *name)

功能:动态分配设备号,查询内核里未被占用的设备号,如果找到则占用该设备号

参数:

dev:分配设备号成功后用来存放分配到的设备号

baseminior:起始的次设备号,一般为0, 次设备号

count:申请的设备数量

name:/proc/devices文件中与该设备对应的名字,方便用户层查询主次设备号

返回值:

成功为0,失败负数,绝对值为错误码

分配成功后在/proc/devices 可以查看到申请到主设备号和对应的设备名,mknod时参数可以参考查到的此设备信息

void unregister_chrdev_region(dev_t from, unsigned count)

功能:释放设备号

参数:

from:已成功分配的设备号将被释放

count:申请成功的设备数量

释放后/proc/devices文件对应的记录消失

六、申请设备并根据设备号创建设备文件演示

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>

#define DEV_NUM 1            /* 设备号数量 */

int major = 200;              /* 静态的主次设备号 */
int minor = 0;

static int __init mychar_init(void)      /* 模块入口函数 */
{
	int ret;
	dev_t num;

	ret = register_chrdev_region(num, DEV_NUM, "mychar");
	if(ret) {
		ret = alloc_chrdev_region(&num, minor, DEV_NUM, "mychar");
		if(ret) {
			printk("alloc_chrdev_region failed");
		}
		major = MAJOR(num);
	}
	
	return 0;
}

static void __exit mychar_exit(void)
{
	dev_t num = MKDEV(major, minor);

	/* 将申请的设备号归还给内核 */
	unregister_chrdev_region(num, DEV_NUM);
}


MODULE_LICENSE("GPL");
module_init(mychar_init);
module_exit(mychar_exit);

在/proc/devices文件中查看设备号, mknod创建设备文件。

七、字符设备驱动框架分析

struct inode
{
	//....
	dev_t  i_rdev;//设备号
	struct cdev  *i_cdev;//如果是字符设备才有此成员,指向对应设备驱动程序中的加入系统的struct cdev对象
	//....
}

1. 内核中每个该结构体对象对应着一个实际文件,一对一

2. open一个文件时如果内核中该文件对应的inode对象已存在则不再创建,不存在才创建

3. 内核中用此类型对象关联到对此文件的操作函数集(对设备而言就是关联到具体驱动代码)

struct file
{
	//...
	mode_t f_mode;//不同用户的操作权限,驱动一般不用
	loff_t f_pos;//position 数据位置指示器,需要控制数据开始读写位置的设备有用, 使用指针描述实际上是不准确的。
	unsigned int f_flags;//open时的第二个参数flags存放在此,驱动中常用
	struct file_operations *f_op;//open时从struct inode中i_cdev的对应成员获得地址,驱动开发中用来协助理解工作原理,内核中使用
	void *private_data;//本次打开文件的私有数据,驱动中常来在几个操作函数间传递共用数据
	struct dentry *f_dentry;//驱动中一般不用,除非需要访问对应文件的inode,用法flip->f_dentry->d_inode
        int refcnt;//引用计数,保存着该对象地址的位置个数,close时发现refcnt为0才会销毁该struct file对象
	//...
};

1. open函数被调用成功一次,则创建一个该对象,因此可以认为一个该类型的对象对应一次指定文件的操作

2. open同一个文件多次,每次open都会创建一个该类型的对象

3. 文件描述符数组中存放的地址指向该类型的对象

4. 每个文件描述符都对应一个struct file对象的地址

对于一个字符设备来讲, 内核使用了cdev这样一个对象来描述

我在学习的时候, 有一个文件, 对于一个设备的文件操作方法的合集

有这样的对应关系, inode结构体和外存中文件一一对应, struct file *类型的数组和进程对应, 每打开一个文件又和一个struct file_operations对象一一对应。

画了一幅图: 

最后, 当我们实现了设备的驱动程序之后, 我们在应用层调用的read write ioctl等和文件描述符相关的函数后, 该函数的系统调用会根据文件描述符去到文件描述符数组, 根据下标值, 找到对应的struct file对象, 接着通过struct file对象里面的struct file_operations指针找到相应发设备驱动程序。

最后提一句, 文件描述符其实本质是数组的下标, 那这个数组是什么类型的呢, 类型是struct file *, 文件操作引擎指针类型的数组。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1594876.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微服务之LoadBalancer负载均衡服务调用

一、概述 1.1什么是负载均衡 LB&#xff0c;既负载均衡&#xff08;Load Balancer&#xff09;,是高并发、高可用系统必不可少的关键组件&#xff0c;其目标是尽力将网络流量平均分发到多个服务器上&#xff0c;以提高系统整体的响应速度和可用性。 负载均衡的主要作用 高并发…

华为机考入门python3--(15)牛客15-求int型正整数在内存中存储时1的个数

分类&#xff1a;二进制 知识点&#xff1a; int转二进制 binary bin(n)[2:] 题目来自【牛客】 def count_ones_in_binary(n): # 将输入的整数转换为二进制字符串 # bin(n)为0b11011binary bin(n)[2:]# 初始化计数器为0 count 0 # 遍历二进制字符串的每一位 fo…

2024年大唐杯备考

努力更新中…… 第一章 网络架构和组网部署 1.1 5G的网络整体架构 5G网络中的中传、回传、前传&#xff08;这里属于承载网的概念&#xff09; CU和DU之间是中传 BBU和5GC之间是回传 BBU和AAU之间是前传&#xff08;这个好记&#xff09; 这里竟然还藏了MEC&#xff08;…

YOLTV8 — 大尺度图像目标检测框架(欢迎star)

YOLTV8 — 大尺度图像目标检测框架【ABCnutter/YOLTV8: &#x1f680;】 针对大尺度图像&#xff08;如遥感影像、大尺度工业检测图像等&#xff09;&#xff0c;由于设备的限制&#xff0c;无法利用图像直接进行模型训练。将图像裁剪至小尺度进行训练&#xff0c;再将训练结果…

使用Python的Pillow库进行图像处理书法参赛作品

介绍&#xff1a; 在计算机视觉和图像处理领域&#xff0c;Python是一种强大而流行的编程语言。它提供了许多优秀的库和工具&#xff0c;使得图像处理任务变得轻松和高效。本文将介绍如何使用Python的wxPython和Pillow库来选择JPEG图像文件&#xff0c;并对选中的图像进行调整和…

STM32常见调试工具介绍

STM32的常见调试工具主要包括ST-LINK、USB转TTL、USB转485以及USB转CAN。这些工具在嵌入式系统开发、调试以及通信中发挥着重要的作用。 1.ST-LINK&#xff1a; ST-LINK是STMicroelectronics公司专为其STM32系列微控制器开发的调试和编程工具。既能仿真也能将编译好的程序下载…

python应用-os库操作目录

python自带的os模块提供了许多与操作系统交互的函数&#xff0c;适配多种操作系统&#xff0c;比如windows&#xff0c;mac&#xff0c;linux等&#xff0c;比如常用路径操作、进程管理、环境参数等都可通过os模块实现。 以下是自带的os.py中的前面一部分代码。 第一个红框中主…

asp.net core 网页接入微信扫码登录

创建微信开放平台账号&#xff0c;然后创建网页应用 获取appid和appsecret 前端使用的vue&#xff0c;安装插件vue-wxlogin 调用代码 <wxlogin :appid"appId" :scope"scope" :redirect_uri"redirect_uri"></wxlogin> <scri…

Qt快速入门(Opencv小案例之人脸识别)

Qt快速入门&#xff08;Opencv小案例之人脸识别&#xff09; 编译出错记录 背景 因为主要使用qt&#xff0c;并且官网下载的win版本的编译好的opencv默认是vc的&#xff0c;所以我们需要自己下载opencv的源码使用mingw自行编译&#xff0c;我直接使用的vscode。 报错 报错…

LabVIEW直流稳定电源自动化校准系统

LabVIEW直流稳定电源自动化校准系统 直流稳定电源正向着智能化、高精度、多通道、宽量程的方向发展。基于LabVIEW开发环境&#xff0c;设计并实现了一种直流稳定电源自动化校准系统&#xff0c;以提升校准过程的整体效能&#xff0c;实现自动化设备替代人工进行电源校准工作。…

YOLOv8 测试 5:Linux 中 Docker 部署 YOLOv8,Python 封装 API 接口,base64 图片处理

一、前言 记录时间 [2024-4-14] 系列文章简摘&#xff1a; Docker 学习笔记&#xff08;二&#xff09;&#xff1a;在 Linux 中部署 Docker&#xff08;Centos7 下安装 docker、环境配置&#xff0c;以及镜像简单使用&#xff09; API 接口简单使用&#xff08;二&#xff09;…

对常见FTP客户端/服务器的调查与分析

前言 主要是想看看常见的服务器和客户端是如何实现协议中要求的功能的&#xff0c;。 比如RF959要求的记录结构&#xff08;Record Structure&#xff09;、页结构&#xff08;Page Structure&#xff09;、Block Mode、Compress Mode&#xff0c;看起来就很抽象。 实测发现…

springBoot+vue编程中使用mybatis-plus遇到的问题

mybatis-plus中遇到的问题Code Companion Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)…

专题十三、预处理器

预处理器 1. 预处理器的工作原理2. 预处理指令3. 宏定义3.1 简单的宏3.2 带参数的宏3.3 # 运算符3.4 ## 运算符3.5 宏的通用属性3.6 宏定义中的圆括号3.7 创建较长的宏3.8 预定义宏3.9 C99 中新增的预定义宏3.10 空的宏参数3.11 参数个数可变的宏3.12 __func__ 标识符 4. 条件编…

SMS垃圾短信识别项目

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 项目背景 随着数字通信的快速发展&#xff0c;垃圾短信成为了一个普遍而烦人的问题。这些不请自来的消息不仅打扰了我们的日常生活&#xff0c;…

嵌入式单片机 TTL电平、232电平、485电平的区别和联系

一、简介 TTL、232和485是常见的串口通信标准&#xff0c;它们在电平和通信方式上有所不同&#xff0c; ①一般情况下TTL电平应用于单片机外设&#xff0c;属于MCU/CPU等片外外设&#xff1b; ②232/485电平应用于产品整体对外的接口&#xff0c;一般是片外TTL串口转232/485…

React 19 的新增功能:Action Hooks

React 是前端开发领域最流行的框架之一。我喜欢 React 是因为它背后的团队和社区对它的热情。当社区提出新功能和改进的需求时&#xff0c;团队会倾听&#xff0c;React 的未来是令人兴奋和有趣的。 让我们来看一下 React 19 中令开发人员提升开发效率的新特性。对于每个钩子&…

内网渗透系列-mimikatz的使用以及后门植入

内网渗透系列-mimikatz的使用以及后门植入 文章目录 内网渗透系列-mimikatz的使用以及后门植入前言mimikatz的使用后门植入 msf永久后门植入 &#xff08;1&#xff09;Meterpreter后门&#xff1a;Metsvc&#xff08;2&#xff09;Meterpreter后门&#xff1a;Persistence NC后…

Matlab隐式方程拟合【案例源码+视频教程】|隐函数拟合|非线性拟合|视频教程

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《复杂函数拟合案例分享》本专栏旨在提供 1.以案例的形式讲解各类复杂函数拟合的程序实现方法&#xff0c;并提供所有案例完整源码&#xff1b;2.…

Zookeeper和Kafka的部署

目录 一、Zookeeper的基本概念 1. Zookeeper定义 2. Zookeeper工作机制 3. Zookeeper特点 4. Zookeeper数据结构 5. Zookeeper应用场景 5.1 统一命名服务 5.2 统一配置管理 5.3 统一集群管理 5.4 服务器动态上下线 5.5 软负载均衡 6. Zookeeper 选举机制 6.1 第一…