字符设备驱动程序框架

摘要:
用于调用文件later_Operation接口的设备号(dev_t)//第一种方法是静态定义structcdev*cdev_ alloc(void);//第二种方法是动态请求返回structcdev类型的指针//从用于指定字符设备的起始设备号的t类型kernel_变量中删除字符设备dev,指向t类型数据的dev_指针变量,用于指定要分配的设备号数量和设备名称。

字符设备驱动程序框架第1张
字符设备包含:设备号(dev_t),设备(cdev),file_operation 。
创建一个字符设备的流程:

0 创建一个字符设备,可以是静态定义或者动态申请;
1 首先要得到一个设备号,可以是静态定义或者动态申请;
2 把写好的file_operation 并保存到 cdev,实现cdev的初始化;
3 使用cdev_add()注册cdev,告诉内核;
4 创建设备节点,以便后面调用 file_operation接口;

0 创建一个字符设备

static struct cdev chrdev;     //第一种方式  静态定义 
struct cdev *cdev_alloc(void); //第二种方式  动态申请 返回一个struct cdev类型的指针
//从内核中移除某个字符设备,
void cdev_del(struct cdev *p)

1 得到一个设备号

要得到一个设备号,也分为静态定义或者动态申请,但是涉及函数比较多。
有:register_chrdev_region,alloc_chrdev_region,register_chrdev。

register_chrdev_region-静态定义

//内核源码/fs/char_dev.c
int register_chrdev_region(dev_t from, unsigned count, const char *name); 

参数:

  • from:dev_t 类型的变量,用于指定字符设备的起始设备号,(缺点)如果要注册的设备号已经被其他的设备注册了,那么就会导致注册失败。
  • count:指定要申请的设备号个数,count 的值不可以太大,否则会与下一个主设备号重叠。
  • name:用于指定该设备的名称,我们可以在/proc/devices 中看到该设备。
    返回值:返回 0 表示申请成功,失败则返回错误码。

alloc_chrdev_region-动态分配

//内核源码/fs/char_dev.c
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char name);

相较于静态定义,需要自己管理设备号,可能造成设备号冲突得缺点,动态分配由内核自己分配一个尚未使用得主设备号,可以解决这个问题。

参数:

  • dev:指向 dev_t 类型数据的指针变量,用于存放分配到的设备编号的起始值;
  • baseminor:次设备号的起始值,通常情况下,设置为 0;
  • count、name:同 register_chrdev_region 类型,用于指定需要分配的设备编号的个数以及设备的名称。

我们可以通过命令“cat /proc/devices”查询内核分配的主设备号。

register_chrdev 函数

该函数是一个内联函数,它不仅支持静态申请设备号,也支持动态申请设备号,并将主设备号返回,函数原型如下所示:

//内核源码/include/linux/fs.h 文件
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
	return __register_chrdev(major, 0, 256, name, fops);
}

此函数的参数功能包含了,申请设备号,动态申请cdev,把file_operations保存到cdev。可以说,一个函数完成了这么多步骤。但是同一类字符设备(即主设备号相同),会在内核中申请了 256 个,通常情况下,我们不需要用到这么多个设备,这就造成了极大的资源浪费。

unregister_chrdev 函数

使用 register_chrdev 函数申请的设备号,则应该使用 unregister_chrdev 函数进行注销。

//内核源码/include/linux/fs.h
static inline void unregister_chrdev(unsigned int major, const char *name)
{
	__unregister_chrdev(major, 0, 256, name);
}

unregister_chrdev_region 函数

对于使用 register_chrdev_region 函数以及 alloc_chrdev_region 函数分配得到的设备编号,可以使用 unregister_chrdev_region 函数把分配的设备编号交还给内核。

2 cdev的初始化

编写一个字符设备最重要的是实现file_operations中的函数。
实现之后,将改结构体和字符设备关联起来。

cdev_init 函数

//内核源码/fs/char_dev.c
void cdev_init(struct cdev *cdev, const struct file_operations *fops)

函数参数和返回值如下:
参数:

  • cdev:struct cdev 类型的指针变量,指向需要关联 的字符设备结构体;
  • fops:file_operations 类型的结构体指针变量,一般将实现操作该设备的结构体 file_operations 结构体作
    为实参。

3 cdev注册和注销

cdev_add 函数

//内核源码/fs/char_dev.c
//cdev_add 函数用于向内核的 cdev_map 散列表添加一个新的字符设备
int cdev_add(struct cdev *p, dev_t dev, unsigned count)

函数参数和返回值如下:
参数:

  • p:struct cdev 类型的指针,用于指定需要添加的字符设备;
  • dev:dev_t 类型变量,用于指定设备的起始编号;
  • count:指定注册多少个设备。
  • 返回值:错误码

从系统中删除 cdev,cdev 设备将无法再打开,但任何已经打开的 cdev 将保持不变,即使在 cdev_del 返回后,
它们的 FOP 仍然可以调用。

//内核源码/fs/char_dev.c
void cdev_del(struct cdev *p)

4 设备节点的创建和销毁

创建一个设备并将其注册到文件系统

//定义一个类指针
static struct class *chr_dev_class;

//内核源码/drivers/base/core.c
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)

函数参数和返回值如下:
参数:

  • class:指向这个设备应该注册到的类的指针;
  • parent:指向此新设备的父结构设备(如果有)的指针;
  • devt:要添加的 char 设备的开发;
  • drvdata:要添加到设备进行回调的数据;
  • fmt:输入设备名称。

返回值:成功时返回 struct device 结构体指针, 错误时返回 ERR_PTR().

删除使用 device_create 函数创建的设备

//内核源码/drivers/base/core.c
void device_destroy(struct class *class, dev_t devt)

除了使用代码创建设备节点,还可以使用 mknod 命令创建设备节点。这里不展开。

如:mkmod /dev/test c 2 0
创建一个字符设备/dev/test,其主设备号为 2,次设备号为 0。

免责声明:文章转载自《字符设备驱动程序框架》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇mssql 发布库 Invalid object name 'syspublications'XSS攻击的解决方法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Git 常用命令 连接GitHub

1、如果插入出现下面的错误可以尝试 $ git push -u origin master To https://github.com/binglong180/loginNew.git ! [rejected] master -> master (fetch first) error: failed to push some refs...

kaldi 三个脚本cmd.sh path.sh run.sh

参考 kaldi的全部资料_v0.4 cmd.sh 脚本为: 可以很清楚的看到有 3 个分类分别对应 a,b,c。a 和 b 都是集群上去运行这个样子, c 就是我们需要的。我们在虚拟机上运行的。你需要修改这个脚本 # "queue.pl"uses qsub. The options to it are # options to qsub. If yo...

Windows异常处理机制介绍

转自:http://hi.baidu.com/zwegpcwvtybivxq/item/a8b7e6c15e8b15155150581f 最近做了一个Windows下的异常处理模块,查阅了一些新的资料,结合我自己的理解,将一些点滴记录如下,希望对兄弟们有所帮助。 一、C++标准异常 也就是try、throw、catch这三个关键字。 try{    …… ...

dd命令详解

【基本参数详解】 dd 复制    if 源 if=/dev/zero不产生IO of 目的 of=/dev/null不产生IO bs 块大小    count 读写块的数量    iflag/oflag dd做读写测试时,要加两个参数 iflag=nocache 和 oflag=direct 参数。 没有的话dd有...

linux下重新安装grub

介绍 本篇指南会告诉你如果不知什么缘故丢失了GRUB后如何用一张Arch安装光盘重装GRUB。 注意 在这篇指南,我将使用sda1作为我的根udev类型references。如果你使用0.7.1以前的安装光盘,你应该将udev references换成devfs references。如果不明白请参见Technical_Terms#Hard_Drives。...

部署GlusterFS及Heketi

一、前言及环境 在实践kubernetes的StateFulSet及各种需要持久存储的组件和功能时,通常会用到pv的动态供给,这就需要用到支持此类功能的存储系统了。在各类支持pv动态供给的存储系统中,GlusterFS的设定比较简单,且数据安全性比较有保障,相较于ceph和NFS。 环境(gluster-server之间互信): 二、部署Gluste...