Linux驱动|cdev_init、cdev_alloc区别
这两个函数是字符设备初始化相关的内核函数。
要想了解这两个函数,必须要知道字符设备的架构,以及字符设备创建的流程。
关于字符设备可以参考下面这篇文章
《手把手教Linux驱动3-之字符设备架构详解,有这篇就够了》
一、字符设备架构
下面我们以两个设备:LED、MPU6050为例来讲解字符设备的架构

由上图所示:
1、硬件
外设有MPU6050、LED两个设备,他们通过外设电路连接到SOC的对应的引脚上。
程序要操作外设,就要通过设置soc中对应的SFR来与外设交互。
2、驱动层
- 每一个字符设备都必须首先定义一个结构体变量struct cdev,并注册到内核中
- 所有的该变量在内核中会通过链表进程管理,其中成员list用于将所有链表串接起来
- 用于操作外设的功能函数全部被封装在struct file_operations中,包括read、write等
- 每一个字符设备都必须要有一个设备号,保存在成员dev中,
- 主、次设备号只能被分配一次
- 所有的字符设备号,都由数组chrdevs统一管理
- chrdevs是一个指针数组,成员类型为**struct char_device_struct ***,下标与字符设备号有一定的对应关系,
- **struct char_device_struct **中有成员:
unsigned int major;
struct cdev *cdev;
major : 是主设备号
cdev : 指向该字符设备号对应的cdev结构体
3、应用层、VFS层
- 用户如果想操作硬件,必须调用内核中的struct file_operations中的操作函数,
- 那么如何才能找到该结构体呢?
必须要依赖文件节点来查找,可以通过以下命令来创建
mknod /dev/led c 250 0
mknod 创建设备文件,可以使字符设备,也可以是块设备
/dev/led 设备文件名
c 字符设备
250 主设备号
0 次设备号
字符设备文件属性中最重要的属性就是字符设备号,该设备号和chedevs的下标有一定对应关系
通过mknod创建的文件,VFS层会分配一个结构体变量来维护该文件,类型为struct inode
每新建1个文件内核都会创建不同的结构体变量与之对应
应用程序要操作某个字符设备,那么必须先通过系统调用open()来打开该字符设备
该函数会返回一个唯一的整型文件描述符,同时内核中会分配结构体变量,类型为struct file,并与文件描述符一一对应,该结构体维护在struct task_struct中
每次打开某个文件,都会分配不同的文件描述符,所以需要用不同的变量来保存文件描述符
二、字符设备创建的流程
了解了架构之后,那么我们来看一下内核中完整的创建字符设备的流程及对应的函数调用关系:
如下图所示,字符设备的创建主要包括以下三个步骤:
- 申请设备号
- 初始化cdev
- 注册cdev
调用的函数见右侧

下面是一个最简单的额字符设备创建的实例
/*
*一口Linux
*2021.6.21
*version: 1.0.0
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
static int major = 237;
static int minor = 0;
static dev_t devno;
static struct cdev cdev;
static int hello_open (struct inode *inode, struct file *filep)
{
printk("hello_open()\n");
return 0;
}
static struct file_operations hello_ops =
{
.open = hello_open,
};
static int hello_init(void)
{
int result;
int error;
printk("hello_init \n");
devno = MKDEV(major,minor);
result = register_chrdev_region(devno, 1, "test");
if(result<0)
{
printk("register_chrdev_region fail \n");
return result;
}
cdev_init(&cdev,&hello_ops);
error = cdev_add(&cdev,devno,1);
if(error < 0)
{
printk("cdev_add fail \n");
unregister_chrdev_region(devno,1);
return error;
}
return 0;
}
static void hello_exit(void)
{
printk("hello_exit \n");
cdev_del(cdev);
unregister_chrdev_region(devno,1);
return;
}
module_init(hello_init);
module_exit(hello_exit);
该实例代码主要功能:
- 申请了字符设备号237
- 初始化cdev,并注册了cdev
应用程序如果要想使用,还必须创建字符设备节点
mknod /dev/test c 237 0
这样应用程序就可以通过设备节点/dev/test 调用到对应的内核操作函数.open = hello_open,
/*
*一口Linux
*2021.6.21
*version: 1.0.0
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
main()
{
int fd;
fd = open("/dev/test",O_RDWR);
if(fd<0)
{
perror("open fail \n");
return;
}
printf("open ok \n ");
}
三、函数功能和定义
搞懂上面字符设备创建步骤之后,我们就可以来真正分析cdev_init、cdev_alloc这两个函数了
1. cdev_init()
原型
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
功能
用于初始化cdev结构体,并填充其成员ops
参数
cdev:字符设备
fops :驱动操作函数集合
返回值
无
该函数实现如下:
/**
* cdev_init() - initialize a cdev structure
* @cdev: the structure to initialize
* @fops: the file_operations for this device
*
* Initializes @cdev, remembering @fops, making it ready to add to the
* system with cdev_add().
*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
2. cdev_alloc
原型
struct cdev *cdev_alloc(void)
功能
用于分配cdev结构体,并添加到内核中
参数
返回值
成功:返回分配的cdev结构体变量指针
失败: 返回NULL
该函数实现如下:
/**
* cdev_alloc() - allocate a cdev structure
*
* Allocates and returns a cdev structure, or NULL on failure.
*/
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
}
return p;
}
注意,该函数分配的cdev需要free掉
该函数没有初始化cdev->ops成员
四、cdev_alloc()的使用
该函数主要用于让用户省去操作cdev的操作,只需要提供**struct file_operations **变量就可以通过以下函数注册字符设备
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
其中函数__register_chrdev()定义如下:
/**
* __register_chrdev() - create and register a cdev occupying a range of minors
* @major: major device number or 0 for dynamic allocation
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: name of this range of devices
* @fops: file operations associated with this devices
*
* If @major == 0 this functions will dynamically allocate a major and return
* its number.
*
* If @major > 0 this function will attempt to reserve a device with the given
* major number and will return zero on success.
*
* Returns a -ve errno on failure.
*
* The name of this device has nothing to do with the name of the device in
* /dev. It only helps to keep track of the different owners of devices. If
* your module name has only one type of devices it's ok to use e.g. the name
* of the module here.
*/
int __register_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name,
const struct file_operations *fops)
{
struct char_device_struct *cd;
struct cdev *cdev;
int err = -ENOMEM;
cd = __register_chrdev_region(major, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
cdev = cdev_alloc();
if (!cdev)
goto out2;
cdev->owner = fops->owner;
cdev->ops = fops;
kobject_set_name(&cdev->kobj, "%s", name);
err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
if (err)
goto out;
cd->cdev = cdev;
return major ? 0 : cd->major;
out:
kobject_put(&cdev->kobj);
out2:
kfree(__unregister_chrdev_region(cd->major, baseminor, count));
return err;
}
可以看到该函数,复用了cdev_alloc()、cdev_add(),我们只需要提供以下3个参数即可:
unsigned int major 主设备号
const char *name 设备号名字
const struct file_operations *fops 驱动操作函数集合
五、结论
cdev_alloc()函数相当于
struct cdev cdev;
cdev_init($cdev,&hello_ops)
Linux驱动|cdev_init、cdev_alloc区别的更多相关文章
- Linux 驱动开发
linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...
- linux驱动面试题整理
1.字符型驱动设备你是怎么创建设备文件的,就是/dev/下面的设备文件,供上层应用程序打开使用的文件? 答:mknod命令结合设备的主设备号和次设备号,可创建一个设备文件. 评:这只是其中一种方式,也 ...
- Linux驱动面试题
1. Linux设备中字符设备与块设备有什么主要的区别?请分别列举一些实际的设备说出它们是属于哪一类设备. 字符设备:字符设备是个能够像字节流(类似文件)一样被访问的设备,由字符设备驱动程序来实现这种 ...
- ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程
ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程 原文地址:http://www.cnblogs.com/NickQ/p/9026545.html 一.开发板与ds18b20的入门 ...
- 超简单易用的 “在 pcduino 开发板上写 Linux 驱动控制板载 LED 的闪烁”
版权声明:本文为博主原创文章,未经博主同意不得转载.转载联系 QQ 30952589,加好友请注明来意. https://blog.csdn.net/sleks/article/details/251 ...
- Linux 驱动框架---cdev字符设备驱动和misc杂项设备驱动
字符设备 Linux中设备常见分类是字符设备,块设备.网络设备,其中字符设备也是Linux驱动中最常用的设备类型.因此开发Linux设备驱动肯定是要先学习一下字符设备的抽象的.在内核中使用struct ...
- linux驱动初探之字符驱动
关键字:字符驱动.动态生成设备节点.helloworld linux驱动编程,个人觉得第一件事就是配置好平台文件,这里以字符设备,也就是传说中的helloworld为例~ 此驱动程序基于linux3. ...
- linux 驱动学习笔记01--Linux 内核的编译
由于用的学习材料是<linux设备驱动开发详解(第二版)>,所以linux驱动学习笔记大部分文字描述来自于这本书,学习笔记系列用于自己学习理解的一种查阅和复习方式. #make confi ...
- 嵌入式linux驱动开发之给linux系统添加温度传感器模块
忙了几天,终于可以让ds18b20在自己的开发板的linux系统上跑了!虽然ds18b20不是什么新鲜玩意,但是想想知己可以给linux系统添加模块了还是有点小鸡冻呢! 虽然说现在硬件的资源非常丰富而 ...
- Linux驱动设计—— 中断与时钟
中断和时钟技术可以提升驱动程序的效率 中断 中断在Linux中的实现 通常情况下,一个驱动程序只需要申请中断,并添加中断处理函数就可以了,中断的到达和中断函数的调用都是内核实现框架完成的.所以程序员只 ...
随机推荐
- IPv6地址的文本表示规范
背景 随着IPv6越来越普及,经常要跟IPv6地址打交道,迫切需要一个统一的IPv6地址文本表示规范. RFC4291简单的说明了如何将IPv6地址表示成文本形式,但有很多有歧义和不周全的地方. RF ...
- Java 面向对象编程之InstanceOf关键词和多态
InstanceOf关键字使用,什么是多态 InstanceOf关键字 是Java的一个二元操作符(运算符),也是Java的保留关键字 语法 //如果该object 是该class的⼀个实例,那⼀个实 ...
- Sql Client Show All Conten Of A Field
Terminate the query with \G in place of ; For example: SELECT content_txt FROM sometable\G
- react 拖拽组件 自由拖拽,垂直水平拖拽
react拖拽组件 推荐几个不错的开源拖拽组件以及使用方法 第一个拖拽组件 antd的Tree组件 这个拖拽组件经常用于层级关系的拖拽组件 可以动态的增删改 (排序,添加子层级~父层级,修改等). i ...
- 构建基于Java Spring Boot和Uniapp的心理小程序:从零到一的完整指南
构建基于Java Spring Boot和Uniapp的心理小程序:从零到一的完整指南 前言 大家好,今天我们来聊聊如何使用Java Spring Boot和Uniapp构建一个心理小程序.这个项目不 ...
- WorPress基础之谷歌GSC与GA统计代码安装
本篇文章讲介绍什么是GSC和GA以及如何安装. 什么是GSC GSC,全称为Google Search Console,由谷歌官方提供的网站管理工具,可帮助监控和维护网站在Google 搜索结果中的展 ...
- 初看vue3源码
因为工作的原因又回到了vue的领域,需要加深对vue和vue生态的了解 也许平时比较多人手机看别人解析怎么看vue源码的,自己动手看vue源码的还是比较少,这次我想自己动手看看 首先 吧代码获取到本地 ...
- 15、SpringMVC之常用组件及执行流程
15.1.常用组件 15.1.1. DispatcherServlet DispatcherServlet 是前端控制器,由框架提供,不需要工程师开发: 作用:统一处理请求和响应,整个流程控制的中心, ...
- 【Windows】(USB热点连接)使用手机给主机提供热点连网
1.问题起源 昨天跟和几个哥们一起装机,发现安装好的系统, 直连网卡提示安装成功,但是网络设置显示未连接 找不到其他原因的办法下,我们看能不能使用手机对电脑进行连网 2.解决过程 我想到的是,先从手机 ...
- 【IDEA】找不到类资源
报错问题描述: 找不到这个实例调用的方法或者方法缺失重载 找不到这个声明的类资源 解决情况一 import声明缺失,IDEA智能导包提示可以解决 注意,如果存在了重名的类资源,导入了错误的资源,实例引 ...