https://blog.csdn.net/zqixiao_09/article/details/50850004
下图描述了linux中虚拟文件系统,一般的设备文件与设备驱动程序间的函数调用关系

上图展现了一个应用程序调用字符设备驱动的过程,在设备驱动程序的设计中,一般而言,会关心 file 和 inode 这两个结构体
用户空间使用 open() 函数打开一个字符设备 fd = open("/dev/hello",O_RDWR) , 这一函数会调用两个数据结构 struct inode{...}与struct file{...} ,二者均在虚拟文件系统VFS处,下面对两个数据结构进行解析:

1、file文件结构体
(1)在设备驱动中,这是非常重要的数据结构
(2)这里的file与用户空间出现中的FILE指针式不同的,用户空间FILE是定义在C库中,从来不会出现在内核中。而struct file,确是内核中的数据结构,因此,它也不会出现在用户层程序中。
(3)file结构体指示一个已经打开的文件(设备对应设备文件),其实系统中每个打开的文件在内核空间都有一个相应的struct file结构体,它由内核在打开文件时创建,并传递给在文件上进程操作的任何函数,直至文件被关闭。如果文件被关闭,内核就会释放相应的数据结构。
(4)在内核源码中,struct file要么表示为file,或者为filp(意指“file pointer”), 注意区分一点,file指的是struct file本身,而filp是指向这个结构体的指针。

下面是几个重要成员
a -- fmode_t f_mode;
此文件模式通过FMODE_READ, FMODE_WRITE识别了文件为可读的,可写的,或者是二者。在open或ioctl函数中可能需要检查此域以确认文件的读/写权限,你不必直接去检测读或写权限,因为在进行octl等操作时内核本身就需要对其权限进行检测。
b -- loff_t f_pos;
当前读写文件的位置。为64位。如果想知道当前文件当前位置在哪,驱动可以读取这个值而不会改变其位置。对read,write来说,当其接收到一个loff_t型指针作为其最后一个参数时,他们的读写操作便作更新文件的位置,而不需要直接执行filp ->f_pos操作。而llseek方法的目的就是用于改变文件的位置。
c -- unsigned int f_flags;
文件标志,如O_RDONLY, O_NONBLOCK以及O_SYNC。在驱动中还可以检查O_NONBLOCK标志查看是否有非阻塞请求。其它的标志较少使用。特别地注意的是,读写权限的检查是使用f_mode而不是f_flog。所有的标量定义在头文件中
d -- struct file_operations *f_op;
与文件相关的各种操作。当文件需要迅速进行各种操作时,内核分配这个指针作为它实现文件打开,读,写等功能的一部分。filp->f_op 其值从未被内核保存作为下次的引用,即你可以改变与文件相关的各种操作,这种方式效率非常高。

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

2、inode结构体
VFS inode包含文件访问权限,属主,组,大小,生成时间,访问时间,最后修改时间等信息。它是linux管理文件系统中的最基本单位,也是文件系统连接任何子目录,文件的桥梁
内核使用inode结构体在内核内部表示一个文件。因此,它与表示一个已经打开的文件描述符的结构体(即file文件结构)是不同的, 我们可以使用多个file 文件结构表示同一个文件的多个文件描述符,但此时,所有的这些file文件结构全部都必须只能指向一个inode结构体。
inode结构体包含了一大堆文件相关的信息,但是就针对驱动代码来说,我们只要关心其中的两个域即可:

(1) dev_t i_rdev;
表示设备文件的结点,这个域实际上包含了设备号。

(2) struct cdev *i_cdev;
struct cdev是内核的一个内部结构,它是用来表示字符设备的,当inode结点指向一个字符设备文件时,此域为一个指向inode结构的指针。

  1. struct inode {
  2. struct hlist_node i_hash;
  3. struct list_head i_list;
  4. struct list_head i_sb_list;
  5. struct list_head i_dentry;
  6. unsigned long i_ino;
  7. atomic_t i_count;
  8. unsigned int i_nlink;
  9. uid_t i_uid;//inode拥有者id
  10. gid_t i_gid;//inode所属群组id
  11. dev_t i_rdev;//若是设备文件,表示记录设备的设备号
  12. u64 i_version;
  13. loff_t i_size;//inode所代表大少
  14. #ifdef __NEED_I_SIZE_ORDERED
  15. seqcount_t i_size_seqcount;
  16. endif

  17. struct timespec i_atime;//inode最近一次的存取时间
  18. struct timespec i_mtime;//inode最近一次修改时间
  19. struct timespec i_ctime;//inode的生成时间
  20. unsigned int i_blkbits;
  21. blkcnt_t i_blocks;
  22. unsigned short i_bytes;
  23. umode_t i_mode;
  24. spinlock_t i_lock;
  25. struct mutex i_mutex;
  26. struct rw_semaphore i_alloc_sem;
  27. const struct inode_operations *i_op;
  28. const struct file_operations *i_fop;
  29. struct super_block *i_sb;
  30. struct file_lock *i_flock;
  31. struct address_space *i_mapping;
  32. struct address_space i_data;
  33. ifdef CONFIG_QUOTA

  34. struct dquot *i_dquot[MAXQUOTAS];
  35. endif

  36. struct list_head i_devices;
  37. union {
  38. struct pipe_inode_info *i_pipe;
  39. struct block_device *i_bdev;
  40. struct cdev *i_cdev;//若是字符设备,对应的为cdev结构体
  41. };

3、chardevs 数组
从图中可以看出,通过数据结构 struct inode{...} 中的 i_cdev 成员可以找到cdev,而所有的字符设备都在 chrdevs 数组中

  1. define CHRDEV_MAJOR_HASH_SIZE 255

  2. static DEFINE_MUTEX(chrdevs_lock);
  3. static struct char_device_struct {
  4. struct char_device_struct *next; // 结构体指针  
  5. unsigned int major;              // 主设备号  
  6. unsigned int baseminor;          // 次设备起始号  
  7. int minorct;                     // 次备号个数  
  8. char name[64];  
  9. struct cdev *cdev; /* will die */  
  10. } *chrdevs[CHRDEV_MAJOR_HASH_SIZE]; // 只能挂255个字符主设备
    可以看到全局数组 chrdevs 包含了255(CHRDEV_MAJOR_HASH_SIZE 的值)个 struct char_device_struct的元素,每一个对应一个相应的主设备号。
    如果分配了一个设备号,就会创建一个 struct char_device_struct 的对象,并将其添加到 chrdevs 中;这样,通过chrdevs数组,我们就可以知道分配了哪些设备号。

相关函数,(这些函数在上篇已经介绍过,现在回顾一下:
register_chrdev_region分配指定的设备号范围
alloc_chrdev_region动态分配失败范围
它们都主要通过调用__register_chrdev_region实现;注意,这两个函数仅仅是注册设备号。如果要将设备号和cdev(用来表示字符设备)关联起来,还要调用cdev_add
register_chrdev申请指定的设备号,并且将其注册到字符设备驱动模型中。
它所做的事情为:
a -- 注册设备号, 通过调用 __register_chrdev_region() 来实现
b -- 分配一个cdev, 通过调用 cdev_alloc() 来实现
c -- 将cdev添加到驱动模型中, 这一步将设备号和驱动关联了起来. 通过调用 cdev_add() 来实现
d -- 将第一步中创建的 struct char_device_struct 对象的 cdev 指向第二步中分配的cdev. 由于register_chrdev()是老的接口,这一步在新的接口中并不需要。

4、cdev 结构体
在 Linux 字符设备驱动开发 字符设备驱动结构 有解析。

5、文件系统中对字符设备文件的访问

下面看一下上层应用open() 调用系统调用函数的过程。
对于一个字符设备文件,其inode.i_cdev指向字符设备驱动对象cdev,如果i_cdev为Null,说明该设备文件没有被打开
由于多个设备可以共用同一个驱动程序.所以,通过字符设备的inode 中的i_devices 和 cdev中的list组成一个链表.

首先,系统调用open打开一个字符设备的时候, 通过一系列调用,最终会执行到 chrdev_open
  (最终是通过调用到def_chr_fops中的.open, 而def_chr_fops.open = chrdev_open. 这一系列的调用过程,本文暂不讨论)
  int chrdev_open(struct inode * inode, struct file * filp)
chrdev_open()所做的事情可以概括如下:
  1. 根据设备号(inode->i_rdev), 在字符设备驱动模型中查找对应的驱动程序, 这通过kobj_lookup() 来实现, kobj_lookup()会返回对应驱动程序cdev的kobject.
  2. 设置inode->i_cdev , 指向找到的cdev.
  3. 将inode添加到cdev->list 的链表中.
  4. 使用cdev的ops 设置file对象的f_op
  5. 如果ops中定义了open方法,则调用该open方法
  6. 返回执行完 chrdev_open()之后,file对象的f_op指向cdev的ops,因而之后对设备进行的read, write等操作,就会执行cdev的相应操作。

转载于:https://www.cnblogs.com/Ocean-Star/p/9249862.html

3. file、inode结构体及chardevs数组等相关知识解析相关推荐

  1. Linux 字符设备驱动结构(三)—— file、inode结构体及chardevs数组等相关知识解析

    前面我们学习了字符设备结构体cdev Linux 字符设备驱动开发 (一)-- 字符设备驱动结构(上)  下面继续学习字符设备另外几个重要的数据结构. 先看下面这张图,这是Linux 中虚拟文件系统. ...

  2. linux创建文件结构体,Linux file 结构体和 inode 结构体,Go语言入门技术,Go语言基础...

    在设备驱动程序中,一般需要关心两个结构体:file 和 inode. 1. file 结构体 file 结构体代表一个打开的文件,系统中每个打开的文件在内核空间都有一个关联的 struct file. ...

  3. linux驱动 cdev,inode结构体

    前面我们学习了字符设备结构体cdev  Linux 字符设备驱动开发 (一)-- 字符设备驱动结构(上)  下面继续学习字符设备另外几个重要的 数据结构. 先看下面这张图,这是Linux 中虚拟文件系 ...

  4. 内核中inode结构体

    在介绍inode结构体之前先做一个链接文件的实验: 1.创建一个普通的文件test.txt,并写入内容查看,如下 2.创建test.txt的硬链接文件,并测试如下: 3.创建test.txt的软连接文 ...

  5. matlab单元数组与结构体的区别,MATLAB中的结构体和单元数组

    MATLAB中的结构体和单元数组 一.结构: x.real=10 x.imag=5 增加字段 x.scale=1 使用struct函数 s=struct('field1',values1,'field ...

  6. 【VS开发】【编程开发】【C/C++开发】结构体中的数组与指针的内存分配情况说明...

    [VS开发][编程开发][C/C++开发]结构体中的数组与指针的内存分配情况说明 标签:[VS开发] [编程开发] 主要是疑惑在结构体定义的数组的内存空间与指针动态分配的内存空间,在地址上连续性.以及 ...

  7. Golang之空结构体和零长数组的实践

    空结构体和零长数组(两个复合类型)都仅仅是一个占位符,不占用空间,这里编译器进行了优化,如果结构体或数组的unsafe.sizeof=0则直接返回zerobase. // 必须用key来初始化结构体 ...

  8. go语言json字符串解析为结构体数组,结构体指针的数组

    废话不多说直接上效果图 完整代码如下 package main import ("encoding/json""fmt" ) // 结构体定义 type rob ...

  9. C#结构体和字节数组的转换

    在写C#TCP通信程序时,发送数据时,只能发送byte数组,处理起来比较麻烦不说,如果是和VC6.0等写的程序通信的话,很多的都是传送结构体,在VC6.0中可以很方便的把一个char[]数组转换为一个 ...

  10. C#通信,结构体和byte数组互转

    在C#进行UDP或者TCP/IP通信时,经常要用到byte数组来传递信息.一开始,我是使用BitConverter和Encoding来进行转换.后来经过大神指导,其实一切不必那么麻烦.只需将要发送的信 ...

最新文章

  1. QIIME 2用户文档. 12训练特征分类器Training feature classifiers(2018.11)
  2. Python踩坑指南(第一季)
  3. 嵌入式linux设计报告,嵌入式linux课程设计报告
  4. 原理解释|直觉与实现:Batch Normalization
  5. lnmp环境切换php版本,并安装相应redis扩展
  6. docusign文档打不开_怎样查看 docusign pdf 电子签名
  7. CentOS5.6下配置rsync内网同步数据到外网
  8. JS 借助 pako.js 实现网络请求获取 gzip 数据流并解析, 解决汉字乱码
  9. 【MyBatis】Mybatis使用SqlSessionFactory加载xml文件
  10. 爱不释手(Typingfaster)1.82beta ,请试用并反馈,谢谢
  11. Spring MVC+Mybatis 多数据源配置及发现的几个问题
  12. mysql完全卸载教程(图文详细)
  13. 软件测试用例篇总结概述
  14. 关于电脑程序无响应的常见原因以及解决办法
  15. 每天进步一点点————MUMA架构优化和应用优化
  16. SAP中利用标准成本报表计算成品人工成本及组成实例
  17. GPA计算 UESTC - 152
  18. Linux基本命令---2
  19. Daimayuan Online Judge 小蜗的疑问
  20. GPS经纬度换算成XY坐标

热门文章

  1. configure: error: no usable zlib; please install zlib devel package or equivalent m
  2. 大哥吾博文写得清清楚楚,怎么就不试一下?
  3. LIBAVCODEC_VERSION_INT是哪里定义的?
  4. VirtualBox 6.1.4的共享剪贴板确实有问题,6.1.0正常
  5. LINUX下载编译libyuv
  6. gcc中使用rpath指定优先搜索路径
  7. OpenCV编译时提示错误“ Built target libprotobuf”
  8. 换回了WINDOWS7
  9. 在头文件中定义或声明变量
  10. linux查找当前目录下所有子目录特定文件类型