字符设备驱动注册的本质及注册注销步骤,struct inode/file结构体作用

news2024/9/25 11:10:51

一、字符设备注册的本质及注册注销步骤

字符设备驱动注册的本质

        只要某个信息存在于操作系统上,在操作系统中一定存在一个描述这个信息的对象,字符设备驱动注册进内核,在内核中一定会存在一个字符设备驱动对象保存当前的字符设备驱动的信息。

字符设备驱动对象结构体分析 struct cdev

struct dev{

        struct kobject kobj;  //内核基类的对象

        struct module *owner;  //指向这个字符设备驱动对象的指针 THIS_MODULE

        const struct file_operations *ops;  //操作方法结构体

        dev_t dev;  //设备号的起始值

        unsigned int countl;  //设备数量

};

字符设备驱动注册、注销的过程

/********************注册过程***************/

1.申请一个字符设备驱动对象

2.为字符设备驱动对象初始化赋值

3.向内核申请使用的设备号和指定数量的设备资源

4.将字符设备驱动对象注册进内核

/********************注销过程*****************/

1.注销字符设备对象
2.释放申请的设备号

3.释放字符设备驱动对象空间

字符设备驱动注册、注销相关API

*******************注册过程**************

#include <linux/cdev.h>

1.申请字符设备驱动对象

a:struct cdev cdev;

b:struct cdev *cdev = cdev_alloc();

/*

struct cdev *cdev_alloc(void)

功能:向内核申请一个struct cdev对象

返回值:成功返回申请到的对象空间首地址,失败返回NULL

2.字符设备驱动对象的初始化

void cdev_init(struct cdev *cdev, const struct file_operation *fops)
功能:实现字符设备驱动对象成员的部分初始化

参数: cdev :字符设备对象指针
         fops:操作方法指针


3.向内核申请使用的设备号和指定数量的设备资源

3.1  静态申请设备号
        int register_chrdev_region(dev_t from, unsigned count, const char *name)

        参数:from:要申请的设备号的起始值

                   count:要申请的设备资源数量

                   name:驱动名

3.2  动态申请设备号

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

        参数:dev:保存申请到的设备号的空间首地址
                   baseminor:要申请的次设备号的起始值
                   count:申请的设备资源的数量
                   name:驱动名

        返回值:成功返回0, 失败返回错误码

4.注册字符设备驱动对象
        int cdev_add(struct cdev *p, dev_t dev, unsigned cout)

        参数:p :字符设备驱动对象指针
                   dev:申请的设备号起始值
                   count:申请的设备资源的数量


/*******************************注销过程******************************/

1.注销字符设备驱动对象

        void cdev_del(struct cdev *p)

        参数:字符设备驱动对象指针
2.释放申请的设备号
        void unregister_chrdev_region(dev_t from, unsigned count)

        参数:from:设备号的起始值

                   count:设备资源的数量
3.释放字符设备驱动对象空间

        void kfree(struct cdev *p)
        参数:p:字符设备驱动对象指针

字符设备驱动对象分布注册注销示例

驱动代码

#include <linux/init.h>
#include <linux/module.h>

static int __init mycdev_init(void)
{
    //1.申请一个对象空间cdev_alloc
    //2.初始化对象cdev_init
    //3.申请设备号  register_chrdev_region()/alloc_chrdev_region()
    //4.注册驱动对象  cdev_add
    //5.向上提交目录  class_create
    //6.向上提交设备节点信息  device_create
    return 0;
}
static void __exit mycdev_exit(void)
{
    //1.销毁设备节点信息
    //2.销毁目录
    //3.注销字符设备驱动对象
    //4.释放设备号
    //5.释放申请到的字符设备驱动对象空间

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

应用层代码

#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<linux/cdev.h>
#include<linux/slab.h>



struct cdev *cdev;
unsigned int major=0;
unsigned int minor=0;
dev_t devno;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
   
     printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 定义操作方法结构体变量并赋值
struct file_operations fops = {

    .open = mycdev_open,
    .unlocked_ioctl = mycdev_ioctl,
    .release = mycdev_close,
};
static int __init mycdev_init(void)
{
    //1.申请一个对象空间cdev_alloc
    int ret;
    cdev= cdev_alloc();
    if(cdev==NULL)
    {
        printk("申请字符设备驱动对象失败\n");
        ret=-EFAULT;
        goto out1;

    }
    printk("字符设备驱动对象申请成功\n");
    //2.初始化对象cdev_init
    cdev_init(cdev,&fops);
    //3.申请设备号  register_chrdev_region()/alloc_chrdev_region()
    if(major==0)//动态申请
    {
        ret=alloc_chrdev_region(&devno,minor,3,"mychrdev");
        if(ret)
        {
            printk("动态申请设备号失败\n");
            goto out2;
        }
        major=MAJOR(devno);//根据设备号获取主设备号
        minor=MINOR(devno);//根据设备号获取次设备号
    }
    else
    {
        ret=register_chrdev_region(MKDEV(major,minor),3,"mychrdev");
        if(ret)
        {
            printk("静态指定设备号失败\n");
            goto out2;
        }
    }
    printk("设备号申请成功\n");
    //4.注册驱动对象  cdev_add
    ret=cdev_add(cdev,MKDEV(major,minor),3);
    if(ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out3;
    }
    printk("注册字符设备驱动对象成功\n");
    //5.向上提交目录  class_create
    cls=class_create(THIS_MODULE,"mychrdev");
    if(IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        goto out4;
    }
    printk("向上提交目录成功\n");
    //6.向上提交设备节点信息  device_create
    int i;
    for(i=0;i<3;i++)
    {
        dev=device_create(cls,NULL,MKDEV(major,i),NULL,"mycdev%d",i);
        if(IS_ERR(dev))
        {
            printk("向上提交设备节点失败\n");
            goto out5;
        }
    }
    printk("向上提交设备节点信息成功\n");
    return 0;
out5:
//将提交成功的节点信息释放
    for(--i;i>=0;i--)
    {
        device_destroy(cls,MKDEV(major,i));
    }
    //销毁目录
    class_destroy(cls);
out4:
    cdev_del(cdev);
out3:
    unregister_chrdev_region(MKDEV(major,minor),3);
out2:
    kfree(cdev);
out1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    //1.销毁设备节点信息
    int i;
    for(i=0;i<3;i++)
    {
        device_destroy(cls,MKDEV(major,i));
    }
    //2.销毁目录
    class_destroy(cls);
    //3.注销字符设备驱动对象
    cdev_del(cdev);
    //4.释放设备号
    unregister_chrdev_region(MKDEV(major,minor),3);
    //5.释放申请到的字符设备驱动对象空间
    kfree(cdev);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

二、struct inode/file结构体的作用

struct inode结构体的作用

1.inode的作用

        只要文件存放在操作系统中,操作系统内核中就一定会有一个struct inode结构体对象,保存当前文件的相关信息,每一个文件都有一个自己特定的标识,叫做 inod号,inode号同时也是文件对应的inode结构体的索引号。

struct inode{

        umode_t        i_mode;  //文件的权限

        unsigned short        i_opflags;

        kuid_t        i_uid;    //文件的用户ID

        kuid_t        i_gid; //组ID

        unsigned int         i_flags;

        dev_t        i_rdev;  //设备号

        union{

                struct block_device *i_bdev;  //块设备

                struct cdeb        *i_cdv;  //字符设备

        };

        void *i_private;  //当前文件的私有数据,可以用于函数数值传递

};

2.open函数如何根据文件路径调到驱动的操作方法

struct file结构体的作用

1.文件描述符的作用

        当我们在一个进程中打开一个文件时,open函数打开成功会返回一个非负的整数,这个整数就是文件描述符。在一个进程中最多可以分配1024个文件描述符(文件描述符本质是数组下标0-1023)。每一个进程都会有自己独立的一套文件描述符,文件描述符依赖于进程存在。所以想要知道文件描述符的作用,就要知道文件描述符在进程中的位置。

        只要进程存在操作系统中,在操作系统内核一定会存在一个struct task_struct对象,用来保存进程的相关信息

struct task_struct {

        volatile long        state;  //进程状态

        int        on_cpu;  //标识进程在哪个cpu上z执行

        int        prio;  //进程优先级

        pid_t        pid;  //进程号

        struct task_struct  __rcl        *real_parent;  //父进程

        struct files_struct        *files;  //打开的文件相关结构体指针

};

在struct task_struct对象中,有一个打开的文件相关结构体指针

struct files_struct {

        struct file __rcu *fd_array[NR_OPEN_DEFULT];  //结构体指针数组,存放的是struct file对象的地址(struct file对象存放的是打开的文件的相关信息)

};//文件描述符就是这个数组的下标

struct file结构体功能

        当我们在进程中打开一个文件,在内核中会申请一个struct file类型的结构体空间,这个空间保存的打开的文件信息。这个struct file对象的地址会保存到内核中的一个数组fd_array中,而文件描述符就是这个数组对应位置的下标

struct file {

        struct path        f_path;  //文件路径

        struct inode        *f_inode;

        const struct file_operations        *f_op;  //操作方法结构体

        unsigned int        f_flags;  //open函数的第二个参数赋值给f_flags

        fmode_t        f_mode;  /打开的文件的权限

        void        *private_date;  //私有数据,可以实现函数间数据的传递

}; 

系统调用函数如何通过文件描述符找到驱动中对应的操作方法
fd文件描述符申请和生成struct file结构体的过程
在驱动程序中完成设备文件和具体设备的绑定

方法1:

1.属于同一个类型的设备,不同设备之间次设备号不同,可以将不同设备文件与具体设备绑定

2.在open方法中参数是struct inode*和struct file*结构体对象指针,

3.在struct inode对象中有一个成员设备号 i_rdev, 可以通过int min = MINOR(inode->i_rdev获取到次设备号。

4.将获取到的次设备号min 赋值给 struct file对象的私有数据成员(void *private,可以用来作为函数间的数据传递), file->private = (void *)min

5.在ioctl/read等操作方法中也有一个struct file*对象指针,int min = (int)(file->private);  通过判断私有数据成员,来进行判断设备文件对应哪个具体设备。

方法2:在ioctl/read等操作方法中有一个struct file*对象指针,struct file对象中有struct inode*对象指针,在struct inode对象中有一个成员设备号 i_rdev, 可以通过int min = MINOR(inode->i_rdev获取到次设备号。

int min = MINOR(file->inode->i_rdev);

代码示例

int mycdev_open(struct inode *inode, struct file *file)
{
    int min=MINOR(inode->i_rdev);//获取打开的的文件的次设备号
    file->private_data= (void *)min;
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int min=(int)file->private_data;//获取到文件的次设备号
   switch(min)
   {
        case 0://操作LED1
            switch(cmd)
            {
                case LED_ON://开灯
                    break;
                case LED_OFF:
                    //关灯
                    break;
            }
            break;
         case 1://操作LED2
            switch(cmd)
            {
                case LED_ON://开灯
                    break;
                case LED_OFF:
                    //关灯
                    break;
            }
            break;
         case 2://操作LED3
            switch(cmd)
            {
                case LED_ON://开灯
                    break;
                case LED_OFF:
                    //关灯
                    break;
            }
            break;
   }
     printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

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

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

相关文章

【Unity】【VR】如何用键鼠模拟VR输入

【项目设置】 建立一个3D项目 Project Settings下最下方安装PlugIn Management&#xff0c;选中HMD 打开Windows->Package Manager->Unity Registry&#xff0c;列表下方XR Interaction Toolkit&#xff0c;点击Install&#xff0c;安装完成后再安装Sample&#xff0c…

北京等保测评:携手守护网络安全!

等保流程可以分为以下几个步骤&#xff1a;定级备案、初测及整改、测评和监督。 一、等保定级备案 根据《信息系统安全等级保护定级指南》和《信息安全等级保护管理办法》的要求&#xff0c;进行等保定级备案。包括以下几个步骤&#xff1a; 写信息安全管理制度&#xff0c;包括…

【Python学习】—Python基础语法(五)

【Python学习】—Python基础语法&#xff08;五&#xff09; 一、循环的嵌套使用 二、九九乘法表 #外层循环表示行数 for i in range(1,10):#内层循环表示每一行的数据for j in range(1,i1):#输出每一行的内容print(f"{i} * {j} {i * j} \t",end"") #外层…

2023 年最佳开源软件

InfoWorld 公布了 2023 年最佳开源软件榜单&#xff0c;旨在表彰年度领先的软件开发、数据管理、分析、人工智能和机器学习开源工具。 上榜的 25 个软件涵盖编程语言、运行时、应用程序框架、数据库、分析引擎、机器学习库、大型语言模型 (LLM)、用于部署 LLM 的工具等领域 Ap…

提升技能,挑战自我——一站式在线题库小程序

在这个信息爆炸的时代&#xff0c;我们总是在寻找一种方式&#xff0c;让自己在众多的知识海洋中快速提升技能&#xff0c;挑战自我。今天&#xff0c;我要向大家推荐一款全新的在线题库小程序KD蝌蚪阿坤&#xff0c;它将帮助你实现这个目标。 KD蝌蚪阿坤是一款全面的在线题库…

长城首款MPV上市,能否迎来「高山」时刻?

作者 | Amy 编辑 | 德新 去年下半年起&#xff0c;自主品牌接二连三抢占高端MPV市场&#xff0c;腾势D9、岚图梦想家、极氪009以及传祺E9等车型接连亮相。 国泰君安证券研究报告显示&#xff0c;新能源MPV 2021年、2022年年销量分别为4.4万和9.2万辆&#xff0c;今年上半年销…

Tp框架如何使用事务和锁,还有查询缓存

1.事务 在ThinkPHP框架中&#xff0c;可以使用think\db\Transaction类来实现事务。 use think\Db; use think\db\Transaction;// 开始事务 Db::startTrans();try {// 执行数据库操作Db::table(user)->where(id, 1)->update([name > John]);// 提交事务Db::commit(); }…

5、计算电机速度【51单片机控制步进电机-TB6600系列】

摘要&#xff1a;本节介绍用定时器定时的方式&#xff0c;精准控制脉冲时间&#xff0c;从而控制步进电机速度。 一、计算过程 电机每一步的角速度等于走这一步所花费的时间&#xff0c;走一步角度等于步距角&#xff0c;走一步的时间等于一个脉冲的时间&#xff1a; TB6600及…

Java中ThreadLocal对象的使用

目录 1、Threadlocal简介 2、ThreadLocal的主要方法&#xff1a; 2.1 initialValue()&#xff1a;初始化ThreadLocal变量的值 2.2 set()&#xff1a;为当前线程设置ThreadLocal变量的值 2.3 get()&#xff1a;获取当前线程中ThreadLocal变量的值 2.4 remove()&#xff1a…

精简版STC单片机串口程序(只有初始化和sendbyte)

摘要&#xff1a;本文分享两个函数构成的STC单片机串口发送程序&#xff0c;代码占用空间极小。不想调用stdio.h和printf但是还想用串口发送简单的调试信息&#xff1f;那就试试它吧&#xff01; 直接上代码 &#xff0c;核心函数只有2个&#xff0c;如下所示 void UartInit(v…

newstar week3 pwn

newstar week3 pwn 巩固知识&#xff0c;如有错误记得纠正&#xff0c;感谢师傅们的评阅 puts or system? Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)int __cdecl main(int argc, const…

springboot-scanBasePackages包扫描

目录 原因&#xff1a; 方式一&#xff1a; 方式二&#xff1a; 原因&#xff1a; 由于对rocketMq进行了一次封装&#xff0c;mq模块里面引用了RocketMQTemplate的bean&#xff0c;如果只引入jar包的依赖&#xff0c;启动的时候不会报错&#xff0c;但是在调用到 RocketMQT…

【OpenCV实现图像阈值处理】

文章目录 概要简单阈值调整自适应阈值调整大津(Otsus)阈值法Otsus 二值化是如何工作的 概要 OpenCV库中的图像处理技术&#xff0c;主要分为几何变换、图像阈值调整和平滑处理三个部分。 在几何变换方面&#xff0c;OpenCV提供了cv.warpAffine和cv.warpPerspective函数&#…

4、让电机转起来【51单片机控制步进电机-TB6600系列】

摘要&#xff1a;本节介绍用简单的方式&#xff0c;让步进电机转起来。其目的之一是对电机转动有直观的感受&#xff0c;二是熟悉整个开发流程。 本系列教程必要的51单片机基础包括IO口操作、中断、定时器三个部分&#xff0c;相关基础教程网上很多&#xff0c;可以自行学习 一…

VMware Ubuntu 关闭自动更新

##1. VMware 17Pro&#xff0c;ubuntu16.04 关闭自动更新 1.1 编辑–》 首选项–》更新–》启动时检查产品更新 2. 这里关了还不够&#xff0c;第二天打开的时候还是提醒系统更新&#xff0c;需要关闭另外的地方 3. 关闭更新检查&#xff0c;默认的是隔天检查一次&#xff0c;…

怎么修复vcomp140.dll丢失问题?5个详细的修复方法分享

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“vcomp140.dll丢失”。那么&#xff0c;vcomp140.dll是什么&#xff1f;它丢失会造成哪些问题呢&#xff1f;小编将从以下几个方面进行详细阐述。 一、vcomp140.dll是什么&#xff1f; vco…

如何使用透明贴图实现火焰效果

1、透明贴图的原理 透明贴图是一种纹理贴图&#xff0c;用于模拟物体部分或全部的透明效果。其原理基于透明度和混合技术。 在计算机图形中&#xff0c;如何显示透明的物体是一个具有挑战性的问题。这是因为透明物体不会像不透明物体那样完全遮挡后面的物体&#xff0c;而是允…

【REDIS】redis-命令大全

【REDIS】redis-命令大全 redis-命令的官方文档 键命令 序号命令及描述1DEL key 该命令用于在 key 存在时删除 key。2DUMP key 序列化给定 key &#xff0c;并返回被序列化的值。3EXISTS key 检查给定 key 是否存在。4EXPIRE key seconds 为给定 key 设置过期时间&#xf…

Python爬虫核心模块urllib的学习

​ 因为在玩Python challenge的时候&#xff0c;有用过这个模块&#xff0c;而且学习这个模块之后也对系统学习网络爬虫有用。 ​ 当时查了各种资料学习&#xff0c;没有碰官网文档&#xff08;因为还是对英语有抗拒性&#xff09;&#xff0c;但是还是官方的文档最具权威和学…

使用Java做业务开发,如何做好一个定时任务的技术选型?

1. 轻量级任务调度 Quartz Scheduler 适用场景: 单机或简单的分布式任务调度特点: 提供丰富的调度选项&#xff0c;如Cron表达式、固定间隔等&#xff1b;支持持久化&#xff0c;能够在应用重启后恢复任务&#xff1b;支持任务监听和触发器监听。建议: 如果你的应用是基于Spr…