open 函数到底做了什么

news2024/10/5 9:08:54
使用设备之前我们通常都需要调用 open 函数,这个函数一般用于设备专有数据的初始化,申请相关资源及进行设备的初始化等工作,对于简单的设备而言,open 函数可以不做具体的工作,你在应用层通过系统调用 open 打开设备时,如果打开正常,就会得到该设备的文件描述符,之后,我们就可以通过该描述符对设备进行 read write 等操作; open 函数到底做了些什么工作?下图中列出了 open 函数执行的大致过程。
户空间使用 open() 系统调用函数打开一个字符设备时 (int fd = open( dev/xxx , O_RDWR)) 大致
有以下过程:
在虚拟文件系统 VFS 中的查找对应与字符设备对应 struct inode 节点
遍历散列表 cdev_map ,根据 inod 节点中的 cdev_t 设备号找到 cdev 对象
创建 struct file 对象(系统采用一个数组来管理一个进程中的多个被打开的设备,每个文件
秒速符作为数组下标标识了一个设备对象)
初始化 struct file 对象,将 struct file 对象中的 file_operations 成员指向 struct cdev 对象中的 file_operations 成员( file->fops = cdev->fops
回调 file->fops->open 函数
我们使用的 open 函数在内核中对应的是 sys_open 函数, sys_open 函数又会调用 do_sys_open 函 数。在 do_sys_open 函数中,首先调用函数 get_unused_fd_flags 来获取一个未被使用的文件描述符 fd,该文件描述符就是我们最终通过 open 函数得到的值。紧接着,又调用了 do_filp_open 函数, 该函数通过调用函数 get_empty_filp 得到一个新的 file 结构体,之后的代码做了许多复杂的工作, 如解析文件路径,查找该文件的文件节点 inode 等,直接来到了函数 do_dentry_open 函数,如下 所示。
//do_dentry_open 函数(内核源码/fs/open.c)

static int do_dentry_open(struct file *f,struct inode *inode,int(*open)(struct inode *,             
                         struct file *),const struct cred *cred)
{
    ……
    f->f_op = fops_get(inode->i_fop);
    ……
    if (!open)
    open = f->f_op->open;
    if (open) {
        error = open(inode, f);
        if (error)
        goto cleanup_all;
    }
    ……
}
4 行:使用 fops_get 函数来获取该文件节点 inode 的成员变量 i_fop ,我们使用 mknod 创建字符设备文件时,将 def_chr_fops 结构体赋值给了该设备文件 inode i_fop 成 员。
7 行:到了这里,我们新建的 file 结构体的成员 f_op 就指向了 def_chr_fops
注:def_chr_fops 是字符设备通用的操作函数,类比于我们自己在写驱动程序时的file_operation
// def_chr_fops 结构体(内核源码/fs/char_dev.c)

const struct file_operations def_chr_fops = {
  .open = chrdev_open,
  .llseek = noop_llseek,
};
最终,会执行 def_chr_fops 中的 open 函数,也就是 chrdev_open 函数,可以理解为一个字符设备的通用初始化函数,根据字符设备的设备号,找到相应的字符设备,从而得到操作该设备的方法。

//chrdev_open 函数(内核源码/fs/char_dev.c)
static int chrdev_open(struct inode *inode, struct file *filp)
{
    const struct file_operations *fops;
    struct cdev *p;
    struct cdev *new = NULL;
    int ret = 0;
    spin_lock(&cdev_lock);
    p = inode->i_cdev;
    if (!p) {
         struct kobject *kobj;
         int idx;
         spin_unlock(&cdev_lock);
         kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
         if (!kobj)
             return -ENXIO;
         new = container_of(kobj, struct cdev, kobj);
         spin_lock(&cdev_lock);
         /* Check i_cdev again in case somebody beat us to it while
         we dropped the lock.*/
         p = inode->i_cdev;
         if (!p) {
             inode->i_cdev = p = new;
             list_add(&inode->i_devices, &p->list);
             new = NULL;
         } else if (!cdev_get(p))
             ret = -ENXIO;
     } else if (!cdev_get(p))
         ret = -ENXIO;
     spin_unlock(&cdev_lock);
     cdev_put(new);
     if (ret)
         return ret;

     ret = -ENXIO;
     fops = fops_get(p->ops);
     if (!fops)
     goto out_cdev_put;

     replace_fops(filp, fops);
     if (filp->f_op->open) {
         ret = filp->f_op->open(inode, filp);
         if (ret)
         goto out_cdev_put;
     }

     return 0;
    
     out_cdev_put:
     cdev_put(p);
     return ret;
 }

函数 chrdev_open 最终将创建的文件结构体 file 的成员 f_op 替换成了 cdev 对应的 ops 成员,并执行 ops 结构体中的 open 函数。

最后,调用上图的 fd_install 函数,完成文件描述符和文件结构体 file 的关联,之后我们使用对该文件描述符 fd 调用 read write 函数,最终都会调用 file 结构体对应的函数,实际上也就是调用cdev 结构体中 ops 结构体内的相关函数。
总结一下整个过程,当我们使用 open 函数,打开设备文件时,会根据该设备的文件的设备号找到相应的设备结构体,从而得到了操作该设备的方法。也就是说如果我们要添加一个新设备的话,我们需要提供一个设备号,一个设备结构体以及操作该设备的方法(file_operations 结构体)。

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

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

相关文章

Unity 修复Sentinel key not found (h0007)错误

这个问题是第二次遇到了,上次稀里糊涂的解决了,也没当回事,这次又跑出来了,网上找的教程大部分都是出自一个人。 1.删除这个路径下的文件 C:\ProgramData\SafeNet Sentinel,注意ProgramData好像是隐藏文件 2.在Windows…

04-19 周五 GitHub actions-runner 程序解释

04-19 周五 GitHub actions-runner 程序解释 时间版本修改人描述2024年4月19日17:26:17V0.1宋全恒新建文档 简介 本文主要描述了actions-runner-linux-x64-2.315.0.tar.gz这个github actions CI所需要的客户端安装包的重要文件和内容信息。有关GitHub actions 的配置&#xff…

利用matplotlib和networkx绘制有向图[显示边的权重]

使用Python中的matplotlib和networkx库来绘制一个有向图,并显示边的权重标签。 1. 定义了节点和边:节点是一个包含5个节点的列表,边是一个包含各个边以及它们的权重的列表。 2. 创建了一个有向图对象 G。 3. 向图中添加节点和边。 4. 设置了…

Elasticsearch:如何使用 Java 对索引进行 ES|QL 的查询

在我之前的文章 “Elasticsearch:对 Java 对象的 ES|QL 查询”,我详细介绍了如何使用 Java 来对 ES|QL 进行查询。对于不是很熟悉 Elasticsearch 的开发者来说,那篇文章里的例子还是不能单独来进行运行。在今天的这篇文章中,我来详…

外贸企业邮箱是什么?做外贸企业邮箱哪个好?

外贸企业邮箱是什么?外贸企业在进行跨国沟通时必不可少的工具就是外贸企业邮箱,外贸企业邮箱需要具备的条件就是海外邮件抵达率高、安全稳定、多语言沟通。而我们又怎么选择一个适合的外贸企业邮箱呢?小编今天带您一起了解。 一、外贸企业邮…

MySQL基础_5.多表查询

文章目录 一、多表连接1.1、笛卡尔积(或交叉连接) 二、多表查询(SQL99语法)2.1、内连接(INNER JOIN)2.2、内连接(INNER JOIN) 一、多表连接 多表查询,也称为关联查询,指两个或更多个表一起完成查询操作。 …

一款开源的原神工具箱,专为现代化 Windows 平台设计,旨在改善桌面端玩家的游戏体验

Snap.Hutao 胡桃工具箱是一款以 MIT 协议开源的原神工具箱,专为现代化 Windows 平台设计,旨在改善桌面端玩家的游戏体验。通过将既有的官方资源与开发团队设计的全新功能相结合,提供了一套完整且实用的工具集,且无需依赖任何移动设…

Django开发实战之登录用户鉴权登录界面实现

Django自带的鉴权系统非常的安全,大家可以放心使用,那么如何使用呢? 1、首先需要检查settings文件种的INSTALLED_APPS,有没有这两部分内容: 2、检查中间件,比如这两个中间件,一个是用于登录&a…

【探秘地球宝藏】矿产资源知多少?

当我们仰望高楼林立的城市,乘坐便捷的交通工具,享受各种现代生活的便利时,你是否曾想过这一切背后的支撑力量?答案就藏在我们脚下——矿产资源,这些大自然赋予的宝贵财富,正是现代社会发展的基石。今天&…

使用ThemeRoller快速实现前端页面风格美化

使用ThemeRoller快速实现前端页面风格美化 文章目录 使用ThemeRoller快速实现前端页面风格美化一、ThemeRoller二、使用方法1.基本操作面板介绍2.直接用现成的配色风格——Gallery画廊3.自定义风格——Roll Your Own4.下载风格包并应用到页面 一、ThemeRoller ThemeRoller是jQ…

基于矩阵乘法的GPU烤机python代码(pytorch版)

前言 测试gpu前需要安装Anaconda、pytorch、tmux、nvitop。 单gpu 代码 import numpy as np from tqdm import tqdmProject :gpu-test File :gpu_stress.py Author :xxx Date :2024/4/20 16:13import argparse import …

力扣153. 寻找旋转排序数组中的最小值

Problem: 153. 寻找旋转排序数组中的最小值 文章目录 题目描述思路复杂度Code 题目描述 思路 1.初始化左右指针left和right,指向数组的头和尾; 2.开始二分查找: 2.1.定义退出条件:当left right时退出循环; 2.2.当nums…

Java新手必看:快速上手FileOutPutStream类

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一…

怎么给word文件名批量替换部分文字?word设置批量替换文字教程

批量替换Word文件名中的几个字,对于经常处理大量文件的人来说,是一项非常实用的技能。以下是一个详细的步骤指南,帮助你快速完成这项任务。 首先,你需要准备一个可以批量重命名文件的工具。市面上有很多这样的工具可供选择&#x…

虚拟化界的强强联手:VirtIO与GPU虚拟化的完美结合

近距离了解 VirtIO 和 GPU 虚拟化 这是一篇 Linaro 开发团队项目组的科普文章。我们在处理器虚拟化项目中,经常会遇到 VirtIO 相关的问题;比如运行 Andriod 系统的时候需要运行 VirtIO 组件。‍‍‍随着 Cassini 项目和 SOAFEE(嵌入式边缘可扩…

电源管理芯片该如何测试?

电源管理芯片作为电子产品的重要组成部分,其性能测试必不可少。通过各项指标测试,评估电源管理芯片是否符合设计规范,及其稳定性和可靠性。 可通过检测以下指标参数来评估电源芯片的性能: 输入/出电压范围、输出纹波、电压调整率、…

Java openrasp记录-02

主要分析以下四个部分: 1.openrasp agent 这里主要进行插桩的定义,其pom.xml中定义了能够当类重新load时重定义以及重新转换 这里定义了两种插桩方式对应之前安装时的独立web的jar的attach或者修改启动脚本添加rasp的jar的方式 其中init操作则需要将ras…

贪吃蛇项目(小白保姆级教程)

游戏介绍 游戏背景: 贪吃蛇游戏是经典的游戏项目之一,也是很简单的小游戏 实现背景: 这里我们是基于32位的Win32_API进行实现的 需要的知识点: C语言函数、枚举、结构体、动态内存管理、预处理指令、链表、Win32_API等 适合人群&a…

【学习AI-相关路程-工具使用-自我学习-cudavisco-开发工具尝试-基础样例 (2)】

【学习AI-相关路程-工具使用-自我学习-cuda&visco-开发工具尝试-基础样例 (2)】 1、前言2、环境说明3、总结说明4、工具安装0、验证cuda1、软件下载2、插件安装 5、软件设置与编程练习1、创建目录2、编译软件进入目录&创建两个文件3、编写配置文…

OpenNJet评测,探寻云原生之美

在信息时代的大海上,云原生应用引擎如一艘航行于波涛之间的帆船,承载着创新的梦想和数字化的未来。本文将带领您登上这艘船,聚焦其中之一的OpenNJet,一同探寻其中的奥秘和精妙,领略其独特之美。 OpenNJet 内容浅析 O…