• Linux字符设备中的两个重要结构体(file、inode}【转】


    转载自:https://www.cnblogs.com/chen-farsight/p/6177870.html

    对于Linux系统中,一般字符设备和驱动之间的函数调用关系如下图所示

     上图描述了用户空间应用程序通过系统调用来调用程序的过程。一般而言在驱动程序的设计中,会关系 struct file 和 struct inode 这两个结构体。

    用户空间使用open()系统调用函数打开一个字符设备时( int fd = open("dev/demo", O_RDWR) )大致有以下过程:

    1.在虚拟文件系统VFS中的查找对应与字符设备对应 struct inode节点

    2.遍历字符设备列表(chardevs数组),根据inod节点中的 cdev_t设备号找到cdev对象

    3.创建struct file对象(系统采用一个数组来管理一个进程中的多个被打开的设备,每个文件描述符作为数组下标标识了一个设备对象)

    4.初始化struct file对象,将 struct file对象中的 file_operations成员指向 struct cdev对象中的 file_operations成员(file->fops =  cdev->fops)

    5.回调file->fops->open函数

    1.inode结构体

      VFS inode 包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。它是Linux 管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁。

      内核使用inode结构体在内核内部表示一个文件。因此,它与表示一个已经打开的文件描述符的结构体(即file 文件结构)是不同的,我们可以使用多个file 文件结构表示同一个文件的多个文件描述符,但此时,所有的这些file文件结构全部都必须只能指向一个inode结构体

       inode结构体包含了一大堆文件相关的信息,但是就针对驱动代码来说,我们只要关心其中的两个域即可:

    1. dev_t i_rdev;     表示设备文件的结点,这个域实际上包含了设备号。
    2. struct cdev *i_cdev;  struct cdev是内核的一个内部结构,它是用来表示字符设备的,当inode结点指向一个字符设备文件时,此域为一个指向inode结构的指针。
    inode结构体

     inode的相关操作函数:

    /* 内核函数从inode中提取设备号 */
    
    /* 提取主设备号 */
    static inline unsigned imajor(const struct inode *inode)
    {
      return MAJOR(inode->i_rdev);
    }
    /* 提取次设备号 */
    static inline unsigned iminor(const struct inode *inode)
    {
      return MINOR(inode->i_rdev);
    }

    2.file文件结构体

      在设备驱动中,这也是个非常重要的数据结构,必须要注意一点,这里的file与用户空间程序中的FILE指针是不同的,用户空间FILE是定义在C库中,从来不会出现在内核中。而struct file,却是内核当中的数据结构,因此,它也不会出现在用户层程序中。

      file结构体指示一个已经打开的文件(设备对应于设备文件),其实系统中的每个打开的文件在内核空间都有一个相应的struct file结构体,它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数,直至文件被关闭。如果文件被关闭,内核就会释放相应的数据结构。

      在内核源码中,struct file要么表示为file,或者为filp(意指“file pointer”), 注意区分一点,file指的是struct file本身,而filp是指向这个结构体的指针。

      下面是几个重要成员:

    1、fmode_t f_mode;

      此文件模式通过 FMODE_READ ,  FMODE_WRITE 识别了文件为可读的,可写的,或者是二者。在open或ioctl函数中可能需要检查此域以确认文件的读/写权限,你不必直接去检测读或写权限,因为在进行octl等操作时内核本身就需要对其权限进行检测。

    2、 loff_t f_pos;

      当前读写文件的位置。为64位。如果想知道当前文件当前位置在哪,驱动可以读取这个值而不会改变其位置。对read,write来说,当其接收到一个loff_t型指针作为其最后一个参数时,他们的读写操作便作更新文件的位置,而不需要直接执行filp ->f_pos操作。

    3、unsigned int f_flags;

      文件标志,如 O_RDONLY ,  O_NONBLOCK 以及 O_SYNC 。在驱动中还可以检查O_NONBLOCK标志查看是否有非阻塞请求。其它的标志较少使用。特别地注意的是,读写权限的检查是使用f_mode而不是f_flog。所有的标量定义在头文件中

    4、struct file_operations *f_op;

      与文件相关的各种操作。当文件需要迅速进行各种操作时,内核分配这个指针作为它实现文件打开,读,写等功能的一部分。filp->f_op 其值从未被内核保存作为下次的引用,即你可以改变与文件相关的各种操作,这种方式效率非常高。

       file_operation 结构体解析如下:Linux字符设备驱动file_operations

    5、 void *private_data;

      在驱动调用open方法之前,open系统调用设置此指针为NULL值。你可以很自由的将其做为你自己需要的一些数据域或者不管它,如,你可以将其指向一个分配好的数据,但是你必须记得在file struct被内核销毁之前在release方法中释放这些数据的内存空间。private_data用于在系统调用期间保存各种状态信息是非常有用的。

    3.chardevs数组

      前面对用户层open()的分析提到,通过数据结构 struct inode{...} 中的 i_cdev 成员可以找到cdev,而所有的字符设备都在 chrdevs 数组中,chrdevs具体是什么样的呢

      下面先看一下 chrdevs 的定义:

    #define CHRDEV_MAJOR_HASH_SIZE 255  
    static DEFINE_MUTEX(chrdevs_lock);
    
    static struct char_device_struct {  
      struct char_device_struct *next; // 结构体指针  
      unsigned int major;              // 主设备号  
      unsigned int baseminor;          // 次设备起始号  
      int minorct;                     // 次备号个数  
      char name[64];  
      struct cdev *cdev; /* will die */  
    } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];// 只能挂255个字符主设备<span style="font-family: Arial, Helvetica, sans-serif; rgb(255, 255, 255);">  </span>
  • 相关阅读:
    机器学习经验帖
    C++面试问题收集
    js事件
    js数据类型
    js鼠标拖拽事件
    BOM事件浏览器滚动条
    IIS发布.netcore需要安装dotnethosting
    电脑访问小米路由器
    win11设置默认浏览器(适用于「按链接」和「按文件类型」,可解决从阿里旺旺中打开时用edge打开页面的问题)
    uniapp自定义基座或发行中出现SDK版本不匹配弹框处理
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/12100850.html
Copyright © 2020-2023  润新知