10.2手动推导linux中file, cdev, inode之间的关系
是时候可以手动推导一下linux里面基类父类和子类的关系了
代码放最后把
简单说明版
详细流程
第一步注册驱动
cdev结构体能看做是一个基类,那么链表里面都是字符设备驱动的cdev连载一起,啥串口,lcd的,通过cdev->list_head连接
那cdev结构体里有主次设备号
第一步 使用register_chrdev 在内核创建了新的cdev基类,同时把驱动的file_operation和主次设备号保存在cdev中
此时把cdev放入整体的链表中 : 链表里面都是字符设备驱动的cdev连载一起,啥串口,lcd的
我们的驱动中:
register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
struct cdev *cdev;//创造了cdev
cdev = cdev_alloc();
cdev->owner = fops->owner;
cdev->ops = fops;//cdev记录下f_op
kobject_set_name(&cdev->kobj, "%s", name); //给cdev的kobj赋值名字,也就是传入的名字
cdev_add(cdev, MKDEV(cd->major, baseminor), count);//cdev加入cdev链表cdev->dev = dev; //cdev保存主次设备号cdev->count = count;//cdev保存个设备的个数kobj_map(cdev_map, dev, count, NULL, //把这个新的dev注册进 kobj_map类型的表cdev_map中,map后续还能再升入一下exact_match, exact_lock, p); //cdev_map中就有了我们的cdevstruct probe *p;p = kmalloc_array(n, sizeof(struct probe), GFP_KERNEL); //创建新的probe来保存cdevp->owner = module;p->dev = dev;p->data = data;//cdev赋值成功
kobj_map类型 起始就是包含了一堆probe 也能叫一个probe链表
一个porbe指向另一个probe,
struct kobj_map {struct probe {struct probe *next;dev_t dev;unsigned long range;struct module *owner;kobj_probe_t *get;int (*lock)(dev_t, void *);void *data;//就保存了上面的cdev指针,这样在这个map中,cedv就连在一起了} *probes[255];struct mutex *lock;
};
第二步创建用户空间能看见的文件,也就是初始化inode节点
inode 对象:描述文件系统中的某个文件,元数据(权限,类型是否为字符设备文件这种.创建时间)
也能理解为ls -l出来的信息
crw–w---- 1 root tty 4, 1 10月 3 23:09 tty1
struct inode {
umode_t i_mode;//模式,如ls-l 出来的 crwx-rx,能区分字符设备和普通文件
kuid_t i_uid;
kgid_t i_gid;
dev_t i_rdev;//如果文件时字符设备,inode里面就有设备号,//如上面的 4 1
const struct file_operations *i_fop;
}
这里图上有两种办法1 驱动初始化后,使用mknode 手动创建一个设备节点
mknode 需要带主次设备号 是为了构造一个inode节点
创建字符设备最后会调用到init_spectial_inode()
fs/inode.c
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev);//传入了inode节点,但是为字符设备,要继续构造inode->i_mode = mode;if (S_ISCHR(mode)) //因为是字符设备 crw--w---- 的cinode->i_fop = &def_chr_fops;//对这个inode节点的fop赋值默认的char_fop.open = chrdev_open,inode->i_rdev = rdev;
另一个是cdev_add的时候使用kobj_add这些进行增加inode节点,后面找个地方细聊kobj基类,这次没看见kobj_add等函数
这一步就是为了构造出 /dev/led这个文件
第三步应用程序打开文件,第四部关联驱动的file_operation
进程打开文件 内核空间的vfs层就会生成一个struct file 对象
struct file{
struct path f_path; //文件的路径
const struct file_operations *f_op;//f_op!!!
struct inode *f_inode; //还偷偷藏了inode
unsigned int f_flags;//标志
fmode_t f_mode;//模式
loff_t f_pos;//偏移
void *private_data;//万能指针
}
同时把这个file结构体放在 fd_table中,每个file结构体对应了这个fd_table索引号,成功后返回这个索引号
此时应用空间调用fd = open(“dev/led”, O_RDWR(标志),0666(模式)); 返回来的fd数值,也就是这个应用程序在vfs空间中对应的fd值
通过 fd_table保存了该进程打开的所有文件
应用程序:
fd = open("dev/led", O_RDWR(标志),0666(模式));
调用到glibc的open() 触发系统调用 产生0x80中断 进入内核层
vfs层:open.c
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
上面这个系统调用的宏定义刚好就是说,建了一个系统调用的函数叫sys_open同时还传来三个参
这不就对应上了嘛
do_sys_open(AT_FDCWD, filename, flags, mode);fd = get_unused_fd_flags(flags);//找一个没有用过的fd给刚打开的文件进行分配fdstruct file *f = do_filp_open(dfd, tmp, &op);//创建了一个file结构体指针do_file_open()//pathname一层一层解析文件路径,可参考上次写的文件系统分析// 最后得到要打开文件的dentry和vfsmount,保存到结构体struct path中,// 而结构体struct path保存在struct nameidata nd变量中,nd变量也是path_openat的入参set_nameidata(&nd, dfd, pathname);// 用于根据nd寻找文件节点,并打开该文件注册的open函数filp = path_openat(&nd, op, flags | LOOKUP_RCU);//初始化filefile = alloc_empty_file(op->open_flag, current_cred());//创建一个filedo_last(nd, file, op)) > 0error = lookup_open(nd, &path, file, op, got_write);//终于寻找你的open了dentry_open(const struct path *path, int flags,const struct cred *cred)vfs_open(path, f);do_dentry_open(struct file *f, struct inode *inode,int (*open)(struct inode *, struct file *))f->f_op = fops_get(inode->i_fop);//获取inode保存的fop给file的fopopen = f->f_op->open;open(inode, f);//执行open了,这里也看得出来入参为啥是inode节点和file文件了.open = chrdev_open,//这里还有点复杂,反正最后调用了inoded的fop->open,也就是char_dev.c 的open,也是第二步赋值过来的openchrdev_open(struct inode *inode, struct file *filp)kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);//从cdev链表中,找到了kobj,关系为 kobj_map->data(cdev)->kobjcdev *new = container_of(kobj, struct cdev, kobj);//顺带就把驱动注册的cdev找出来了inode->i_cdev = p = new;//同时还把inode节点里cdev一起赋值,相当于inode也知道驱动的cdevfops = fops_get(p->ops);//拿到了驱动的fopsreplace_fops(filp, fops);//替换file的fopsret = filp->f_op->open(inode, filp);//执行驱动中的fop->open函数//进入我们的驱动里.传来的inode是这个模块的ionde,file是进程打开的这个fileint chrdevbase_open(struct inode *inode, struct file *filp)fd_install(fd, f);//关联file文件和这个fd,那我我们进程的数fd_arry中就把这个文件记录了,也把fop_改了
第五步,执行其他的write等函数
应用执行了ioctrl(fd,cmd,arg);
调用glibc的ioctrl
系统调用
0x80中断
进入内核:
VFS层 fs/ioctrl.c
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg);
ksys_ioctl(fd, cmd, arg);struct fd f = fdget(fd);//根据应用层传递的fd编号,在数组中找到file结构体do_vfs_ioctl(f.file, fd, cmd, arg);//传入file结构体,和对应的指令//从file中取出inode节点//file->dentry->inodestruct inode *inode = file_inode(filp);if (S_ISREG(inode->i_mode)) //i_noded文件类型,如果是普通文件,S_isreg regular常规的error = file_ioctl(filp, cmd, arg);else //那就是字符文件了error = vfs_ioctl(filp, cmd, arg);error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
//因为file的operation在open的时候被改成我们的驱动里的了
static struct file_operations chrdevbase_fops = {unlocked_ioctl= ioctl,
}//现在就到我们驱动内部
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
代码块
driver
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : chrdevbase.c
作者 : 左忠凯
版本 : V1.0
描述 : chrdevbase驱动文件。
其他 : 无
论坛 : www.openedv.com
日志 : 初版V1.0 2019/1/30 左忠凯创建
***************************************************************/#define CHRDEVBASE_MAJOR 200 /* 主设备号 */
#define CHRDEVBASE_NAME "chrdevbase" /* 设备名 */static char readbuf[100]; /* 读缓冲区 */
static char writebuf[100]; /* 写缓冲区 */
static char kerneldata[] = {"kernel data!"};/** @description : 打开设备* @param - inode : 传递给驱动的inode* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量* 一般在open的时候将private_data指向设备结构体。* @return : 0 成功;其他 失败*/
static int chrdevbase_open(struct inode *inode, struct file *filp)
{//printk("chrdevbase open!\r\n");return 0;
}/** @description : 从设备读取数据 * @param - filp : 要打开的设备文件(文件描述符)* @param - buf : 返回给用户空间的数据缓冲区* @param - cnt : 要读取的数据长度* @param - offt : 相对于文件首地址的偏移* @return : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int retvalue = 0;/* 向用户空间发送数据 */memcpy(readbuf, kerneldata, sizeof(kerneldata));retvalue = copy_to_user(buf, readbuf, cnt);if(retvalue == 0){printk("kernel senddata ok!\r\n");}else{printk("kernel senddata failed!\r\n");}//printk("chrdevbase read!\r\n");return 0;
}/** @description : 向设备写数据 * @param - filp : 设备文件,表示打开的文件描述符* @param - buf : 要写给设备写入的数据* @param - cnt : 要写入的数据长度* @param - offt : 相对于文件首地址的偏移* @return : 写入的字节数,如果为负值,表示写入失败*/
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue = 0;/* 接收用户空间传递给内核的数据并且打印出来 */retvalue = copy_from_user(writebuf, buf, cnt);if(retvalue == 0){printk("kernel recevdata:%s\r\n", writebuf);}else{printk("kernel recevdata failed!\r\n");}//printk("chrdevbase write!\r\n");return 0;
}/** @description : 关闭/释放设备* @param - filp : 要关闭的设备文件(文件描述符)* @return : 0 成功;其他 失败*/
static int chrdevbase_release(struct inode *inode, struct file *filp)
{//printk("chrdevbase release!\r\n");return 0;
}/** 设备操作函数结构体*/
static struct file_operations chrdevbase_fops = {.owner = THIS_MODULE, .open = chrdevbase_open,.read = chrdevbase_read,.write = chrdevbase_write,.release = chrdevbase_release,
};/** @description : 驱动入口函数 * @param : 无* @return : 0 成功;其他 失败*/
static int __init chrdevbase_init(void)
{int retvalue = 0;/* 注册字符设备驱动 */retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);if(retvalue < 0){printk("chrdevbase driver register failed\r\n");}printk("chrdevbase init!\r\n");return 0;
}/** @description : 驱动出口函数* @param : 无* @return : 无*/
static void __exit chrdevbase_exit(void)
{/* 注销字符设备驱动 */unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);printk("chrdevbase exit!\r\n");
}/* * 将上面两个函数指定为驱动的入口和出口函数 */
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);/* * LICENSE和作者信息*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");
相关文章:
10.2手动推导linux中file, cdev, inode之间的关系
是时候可以手动推导一下linux里面基类父类和子类的关系了 代码放最后把 简单说明版 详细流程 第一步注册驱动 cdev结构体能看做是一个基类,那么链表里面都是字符设备驱动的cdev连载一起,啥串口,lcd的,通过cdev->list_head连接 那cdev结构体里有主次设备号 第一步 使用r…...
JavaScript基础知识13——运算符:一元运算符,二元运算符
哈喽,大家好,我是雷工。 JavaScript的运算符可以根据所需表达式的个数,分为一元运算符、二元运算符、三元运算符。 一、一元运算符 1、一元运算符:只需要一个表达式就可以运算的运算符。 示例:正负号 一元运算符有两…...
异步使用langchain
文章目录 一.先利用langchain官方文档的AI功能问问二.langchain async api三.串行,异步速度比较 一.先利用langchain官方文档的AI功能问问 然后看他给的 Verified Sources 这个页面里面虽然有些函数是异步函数,但是并非专门讲解异步的 二.langchain asy…...
抖音开放平台第三方代小程序开发,授权事件、消息与事件通知总结
大家好,我是小悟 关于抖音开放平台第三方代小程序开发的两个事件接收推送通知,是开放平台代小程序实现业务的重要功能。 授权事件推送和消息与事件推送类型都以Event的值判断。 授权事件推送通知 授权事件推送包括:推送票据、授权成功、授…...
华为9.20笔试 复现
第一题 丢失报文的位置 思路:从数组最小索引开始遍历 #include <iostream> #include <vector> using namespace std; // 求最小索引值 int getMinIdx(vector<int> &arr) {int minidx 0;for (int i 0; i < arr.size(); i){if (arr[i] …...
二十五、【色调调整基础】
文章目录 1、亮度/对比度a、亮度b、对比度 2、曝光度3、阈值4、色阶5、反相6、黑白7、渐变映射 1、亮度/对比度 a、亮度 亮度是指画面的明亮程度 b、对比度 对比度指的是一幅图像中,明暗区域最亮和最暗之间不同亮度层级的测量,如下图所示࿰…...
Android Studio SDK manager加载packages不全
打开Android Studio里的SDK manager,发现除了已安装的,其他的都不显示。 解决方法: 设置代理: 方便复制> http://mirrors.neusoft.edu.cn/ 重启Android Studio...
[esp32-wroom]基础开发
1、点亮LED灯 int led_pin2; void setup() {// put your setup code here, to run once:pinMode(led_pin,OUTPUT);}void loop() {// put your main code here, to run repeatedly:digitalWrite(led_pin,HIGH);delay(1000);digitalWrite(led_pin,LOW);delay(1000); } 2、LED流…...
利用Docker 实现 MiniOB环境搭建
官方文档有,但是感觉写的跟shift一样(或者是我的阅读理解跟shift一样 下面是自己的理解 一.下载docker 这个去官网下载安装,没什么说的 Docker: Accelerated Container Application Development 二.用docker下载MiniOB环境 1.打开powershell ( win r ,然后输入powershell…...
【DB2】—— 数据库表查询一直查不出来数据
问题描述 近日,数据库的测试环境中有一个打印日志表,一共有将近50w的数据,Java程序在查询的时候一直超时。 在DBvisualizer中查询数据无论是使用select * 还是 select count(*)查询的时候都是一直在执行,就是查询不到结果。 排查…...
【教程】使用vuepress构建静态文档网站,并部署到github上
官网 快速上手 | VuePress (vuejs.org) 构建项目 我们跟着官网的教程先构建一个demo 这里我把 vuepress-starter 这个项目名称换成了 howtolive 创建并进入一个新目录 mkdir howtolive && cd howtolive使用你喜欢的包管理器进行初始化 yarn init 这里的问题可以一…...
python 机器视觉 车牌识别 - opencv 深度学习 机器学习 计算机竞赛
1 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于python 机器视觉 的车牌识别系统 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分工作量:3分创新点:3分 🧿 更多资…...
Hadoop3教程(十二):MapReduce中Shuffle机制的概述
文章目录 (95) Shuffle机制什么是shuffle?Map阶段Reduce阶段 参考文献 (95) Shuffle机制 面试的重点 什么是shuffle? Map方法之后,Reduce方法之前的这段数据处理过程,就叫做shuff…...
MySQL为什么用b+树
索引是一种数据结构,用于帮助我们在大量数据中快速定位到我们想要查找的数据。 索引最形象的比喻就是图书的目录了。注意这里的大量,数据量大了索引才显得有意义,如果我想要在[1,2,3,4]中找到4这个数据,直接对全数据检索也很快&am…...
浅谈机器学习中的概率模型
浅谈机器学习中的概率模型 其实,当牵扯到概率的时候,一切问题都会变的及其复杂,比如我们监督学习任务中,对于一个分类任务,我们经常是在解决这样一个问题,比如对于一个n维的样本 X [ x 1 , x 2 , . . . .…...
MySQL 函数 索引 事务 管理
目录 一. 字符串相关的函数 二.数学相关函数 编辑 三.时间日期相关函数 date.sql 四.流程控制函数 centrol.sql 分页查询 使用分组函数和分组字句 group by 数据分组的总结 多表查询 自连接 子查询 subquery.sql 五.表的复制 六.合并查询 七.表的外连接 …...
Flink如何基于事件时间消费分区数比算子并行度大的kafka主题
背景 使用flink消费kafka的主题的情况我们经常遇到,通常我们都是不需要感知数据源算子的并行度和kafka主题的并行度之间的关系的,但是其实在kafka的主题分区数大于数据源算子的并行度时,是有一些注意事项的,本文就来讲解下这些注…...
总结:JavaEE的Servlet中HttpServletRequest请求对象调用各种API方法结果示例
总结:JavaEE的Servlet中HttpServletRequest请求对象调用各种API方法结果示例 一方法调用顺序是按照英文字母顺序从A-Z二该示例可以用作servlet中request的API参考,从而知道该如何获取哪些路径参数等等三Servlet的API版本5.0.0、JSP的API版本:…...
ChatGPT AIGC 完成Excel跨多表查找操作vlookup+indirect
VLOOKUP和INDIRECT的组合在Excel中用于跨表查询,其中VLOOKUP函数用于在另一张表中查找数据,INDIRECT函数则用于根据文本字符串引用不同的工作表。具体操作如下: 1.假设在工作表1中,A列有你要查找的值,B列是你希望查询的工作表名称。 2.在工作表1的C列输入以下公式:=VLO…...
Linux系统conda虚拟环境离线迁移移植
本人创建的conda虚拟环境名为yys(每个人的虚拟环境名不一样,替换下就行) 以下为迁移步骤: 1.安装打包工具将虚拟环境打包: conda install conda-pack conda pack -n yys -o yys.tar.gz 2.将yys.tar.gz上传到服务器&…...
Vue16 绑定css样式 style样式
绑定样式: 1. class样式写法:class"xxx" xxx可以是字符串、对象、数组。字符串写法适用于:类名不确定,要动态获取。对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。数组写法适用于&…...
[Spring] SpringMVC 简介(三)
目录 九、SpringMVC 中的 AJAX 请求 1、简单示例 2、RequestBody(重点关注“赋值形式”) 3、ResponseBody(经常用) 4、为什么不用手动接收 JSON 字符串、转换 JSON 字符串 5、RestController 十、文件上传与下载 1、Respo…...
kettle应用-从数据库抽取数据到excel
本文介绍使用kettle从postgresql数据库中抽取数据到excel中。 首先,启动kettle 如果kettle部署在windows系统,双击运行spoon.bat或者在命令行运行spoon.bat 如果kettle部署在linux系统,需要执行如下命令启动 chmod x spoon.sh nohup ./sp…...
Git Commit Message规范
概述 Git commit message规范是一种良好的实践,可以帮助开发团队更好地理解和维护代码库的历史记录。它可以提高代码质量、可读性和可维护性。下面是一种常见的Git commit message规范,通常被称为"Conventional Commits"规范: 一…...
Linux网络编程系列之UDP广播
Linux网络编程系列 (够吃,管饱) 1、Linux网络编程系列之网络编程基础 2、Linux网络编程系列之TCP协议编程 3、Linux网络编程系列之UDP协议编程 4、Linux网络编程系列之UDP广播 5、Linux网络编程系列之UDP组播 6、Linux网络编程系列之服务器编…...
spring中事务相关面试题(自用)
1 什么是spring事务 Spring事务管理的实现原理是基于AOP(面向切面编程)和代理模式。Spring提供了两种主要的方式来管理事务:编程式事务管理和声明式事务管理。 声明式事务管理: Spring的声明式事务管理是通过使用注解或XML配置来…...
09 | JpaSpecificationExecutor 解决了哪些问题
QueryByExampleExecutor用法 QueryByExampleExecutor(QBE)是一种用户友好的查询技术,具有简单的接口,它允许动态查询创建,并且不需要编写包含字段名称的查询。 下面是一个 UML 图,你可以看到 QueryByExam…...
Linux命令(93)之su
linux命令之su 1.su介绍 linux命令su用于变更为其它使用者的身份,如root用户外,需要输入使用者的密码 2.su用法 su [参数] user su参数 参数说明-c <command>执行指定的命令,然后切换回原用户-切换到目标用户的环境变量 3.实例 3…...
1.HTML-HTML解决中文乱码问题
题记 下面是html文件解决中文乱码的方法 方法一 在 HTML 文件的 <head> 标签中添加 <meta charset"UTF-8">,确保文件以 UTF-8 编码保存 <head> <meta charset"UTF-8"> <!-- 其他标签和内容 --> </head> --…...
Vue3 + Nodejs 实战 ,文件上传项目--实现拖拽上传
目录 1.拖拽上传的剖析 input的file默认拖动 让其他的盒子成为拖拽对象 2.处理文件的上传 处理数据 上传文件的函数 兼顾点击事件 渲染已处理过的文件 测试效果 3.总结 博客主页:専心_前端,javascript,mysql-CSDN博客 系列专栏:vue3nodejs 实战-…...
用电脑做网站/app推广拉新工作可靠吗
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 1. 为什么要有工厂模式? "Talk is cheap,show me the code". 想要找到这个问题的答案࿰…...
网站导航固定/网页制作工具有哪些
C学习 十四、类的进阶(5)动态内存前言类与动态内存动态内存与析构函数类的特殊成员函数默认构造函数默认析构函数复制构造函数默认复制构造函数复制构造函数,浅拷贝和深拷贝赋值运算符上述示例代码后记前言 本片继续类的进阶学习,…...
什么网站做优化最好?/淘宝运营培训课程免费
正题 这题的目的很明了: 求。 首先我们要知道 ,关于证明可以参考我的博客。 然后换进去: 交换枚举顺序: 然后就可以发现后面的东西可以直接设为,而设。 那么。 预处理G函数,和H函数,H函数的上面…...
合肥网站制作哪家有名/网站设计公司模板
该书籍PDF下载地址:http://download.csdn.net/download/muyeju/10001473 52.推荐使用String直接赋值 通过String直接创建字符串的过程:创建一个字符串时,首先检查字符串常量池中有没有与字面值相等的字符串,如果有,就不…...
建设网站怎么设置网站页面大小/东莞seo技术培训
转载地址:http://blog.csdn.net/dxl342/article/details/53507673 以下是对Linux中top命令的用法进行了详细的介绍,需要的朋友可以过来参考下查看多核CPU命令 mpstat -P ALL 和 sar -P ALL 说明:sar -P ALL > aaa.txt 重定向输出内容…...
网站互动怎么做/培训seo去哪家机构最好
akuna的电面题 脑子晕了没想出标算/// Permutation: all possible result of permute a list of numbers , for example [1,3,5] → [1,3,5],[1,5,3],[3,5,1],[3,1,5],[5,1,3],[5,3,1] /// [1,1,5] -> [1,5,1], [5,1,1], [1,1,5] 可用置换的方式,从小往大推&…...