linux驱动 cdev,inode结构体

前面我们学习了字符设备结构体cdev  Linux 字符设备驱动开发 (一)—— 字符设备驱动结构(上)  下面继续学习字符设备另外几个重要的 数据结构。

       先看下面这张图,这是Linux 中虚拟文件系统、一般的设备文件与设备驱动程序值间的函数调用关系;



        上面这张图展现了一个应用程序调用字符设备驱动的过程, 在设备驱动程序的设计中,一般而言,会关心 file 和 inode 这两个结构体

        用户空间使用 open() 函数打开一个字符设备 fd = open("/dev/hello",O_RDWR) , 这一函数会调用两个数据结构 struct inode{...}struct file{...} ,二者均在虚拟文件系统VFS处,下面对两个数据结构进行解析:


一、file 文件结构体

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

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

     在内核源码中,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 其值从未被内核保存作为下次的引用,即你可以改变与文件相关的各种操作,这种方式效率非常高。

    file_operation 结构体解析如下:Linux 字符设备驱动结构(四)—— file_operations 结构体知识解析

e -- void *private_data;

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



二、 inode结构体

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

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

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

(1) dev_t i_rdev;

      表示设备文件的结点,这个域实际上包含了设备号

(2) struct cdev *i_cdev;

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

下面是源代码:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  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.  };  


三、chardevs 数组

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

下面先看一下 chrdevs 的定义:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #define CHRDEV_MAJOR_HASH_SIZE 255  
  2. static DEFINE_MUTEX(chrdevs_lock);  
  3.   
  4. static struct char_device_struct {  
  5.     struct char_device_struct *next; // 结构体指针  
  6.     unsigned int major;              // 主设备号  
  7.     unsigned int baseminor;          // 次设备起始号  
  8.     int minorct;                     // 次备号个数  
  9.     char name[64];  
  10.     struct cdev *cdev; /* will die */  
  11. } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];      // 只能挂255个字符主设备<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">  </span>  

       可以看到全局数组 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()是老的接口,这一步在新的接口中并不需要。


四、cdev 结构体

        在 Linux 字符设备驱动开发 (一)—— 字符设备驱动结构(上) 有解析。


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

        下面看一下上层应用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的相应操作。



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

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

相关文章

如何在VS和CB中配置MySQL环境

这里&#xff0c;由于我的MySQL安装在D盘 MY SQL\MySQL Server 5.6该路径下&#xff0c;所以后面的路径均以D:\MY SQL\MySQL Server 5.6开头 在VS中配置MySQL环境 包含目录&#xff1a; D:\MY SQL\MySQL Server 5.6\include 库目录&#xff1a;D:\MY SQL\MySQL Server 5.…

点乘和叉乘的区别_关于延时和混响的区别与专用延时器与混响器的调控技巧

关于延时和混响的区别与专用延时器与混响器的调控技巧延时器与混响器是模拟室内声场声音信号特性的专用设备。在录音节目制作中&#xff0c;延时器和混响器可以在模拟的艺术声场中传递时间、空间、方位、距离等重要信息&#xff0c;并且可以制作某些特殊效果。延时器与混响器工…

zabbix监控工具

实验环境网关 classroom 172.25.8.254workstation 172.25.8.9server a-jeth0 172.25.8.10-外网eth1 192.168.0.x内网eth2 192.168.1.x备用servera:webservere:server--------------------------------------------需求&#xff1a;监控设计&#xff1a;原理&#xff1a;硬件&…

linux驱动 自旋锁

最近在内核频繁使用了自旋锁&#xff0c;自旋锁如果使用不当&#xff0c;极易引起死锁&#xff0c;在此总结一下。 自旋锁是一个互斥设备&#xff0c;它只有两个值&#xff1a;“锁定”和“解锁”。它通常实现为某个整数值中的某个位。希望获得某个特定锁得代码测试相关的位。…

百度输入法

[用户界面]&#xff1a;界面美观&#xff0c;有不同画风的ui界面&#xff0c;适合不同消费群体的需求。 [记住用户选择]&#xff1a;会默认记住用户以往的输入习惯&#xff0c;只需输入首拼音字母就会出现过去使用的高频词。 [短期刺激]&#xff1a;美化的用户界面让人眼前一新…

rs232读取智能电表_三相电表怎么看度数 怎么计算总电量

现在的三相电表一般都是在屏幕上面直接看&#xff0c;屏幕上面是有文字提示显示的&#xff0c;三相电表的总度数&#xff0c;上面的文字提醒一般是“正向有功总电量”&#xff0c;三相电表一般屏幕旁边都会上翻键和下翻键&#xff0c;可以上下翻开电表里面的数据。如下图所示&a…

android 学习随笔十六(广播 )

1、广播接收者 BroadcastReceiver接收系统发出的广播现实中的广播&#xff1a;电台为了传达一些消息&#xff0c;而发送的广播&#xff0c;通过广播携带要传达的消息&#xff0c;群众只要买一个收音机&#xff0c;就可以收到广播了 Android中的广播&#xff1a;系统在运行过程中…

驱动面试题总结

1、字符型驱动设备你是怎么创建设备文件的&#xff0c;就是/dev/下面的设备文件&#xff0c;供上层应用程序打开使用的文件&#xff1f; 答&#xff1a;mknod命令结合设备的主设备号和次设备号&#xff0c;可创建一个设备文件。 评&#xff1a;这只是其中一种方式&#xff0…

python程序写诗_将Python诗歌与D结合起来

在与docker一起使用poetry时&#xff0c;需要记住以下几点。 安装 安装poetry的正式方法是通过&#xff1a;curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python 这种方式允许poetry及其依赖项与依赖项分离。但是&#xff0c;在我看来…

Azure运维系列 4:安装和使用Azure PowerShell管理云

前面讲到了很多的管理方式&#xff0c;包括Azure中国最近更新的预览版管理门户和Azure云助理&#xff0c;都是非常不错的管理Azure的方式&#xff0c;今天我们再来介绍一种更加高效的管理方式Azure PowerShell。熟悉命令行的朋友都知道&#xff0c;Linux之所以那么好用是因为其…

[转]char * 和字符数组

[转]char * 和字符数组 原文地址&#xff1a;http://www.cnblogs.com/jeakon/archive/2012/05/27/2816809.html 代码中的int * i就是我们关注的焦点。它是一个指向int指针。也就是说&#xff1a;i指向一个内存地址&#xff0c;从这个地址开始存储了一个数据。int * i中的int标明…

设备模型1

作为开头篇&#xff0c;我不想写HELLLOWORLD驱动&#xff0c;甚至字符设备驱动的开发&#xff0c;这样文章充斥在各大网站上的博客上&#xff0c;随便搜搜&#xff0c;就可以找到几百篇。这是最基本的东西&#xff0c;通过这些内容的学习&#xff0c;我们要掌握LINUX驱动的基本…

如何使用Android Studio把自己的Android library分享到jCenter和Maven Central

第一部分&#xff1a;在bintray上创建package首先&#xff0c;你需要在bintray上创建一个package。为此&#xff0c;你需要一个bintray账号&#xff0c;并在网站上创建一个package。第一步&#xff1a;在bintray.com上注册一个账号。&#xff08;注册过程很简单&#xff0c;自己…

python2编码_Python2字符编码

我们通常见到的字符串编码主要是三种GB2312/GBK、Unicode、UTF-8。GB2312/GBK是多字节(multibytes)编码的一种&#xff0c;属于“ASCII的加强版”&#xff0c;与之平行的由Big5、ShiftJIS之类的编码各自为政&#xff0c;所有这些用两个字节表示汉字的多字节编码标准统称为ANSI编…

angularJs关于指令的一些冷门属性

我们使用ng的时候&#xff0c;经常会使用到指令&#xff0c;大家所熟知的属性我在这里就不介绍了&#xff0c;讲讲大家没怎么留意的属性 1.multiElement 这是指定指令作用区间的功能&#xff0c;最常用的就是ng-repeat-start和ng-repeat-end了。 2.priority 指令优先级&#xf…

设备模型2

前言 在上一篇中&#xff0c;我们大致描述了LINUX设备模型&#xff0c;我们先来总结一下三要素的关系。 从图中可以看出,Linux设备模型就是"总线、设备、驱动、类"这四个概念之前的相互关系;这也是Linux2.6内核抽象出来的用于管理系统中所有设备的模型图; 简单地描述…

angular自定义指令详解

指令&#xff08;directive&#xff09;是angular里面最核心也是最难懂的东西&#xff0c;在慕课网看了下大漠穷秋老湿的视频&#xff0c;自己百度半天做了一些小test&#xff0c;总算把一切都搞明白了。 先列出学习来源&#xff1a; 指令中controller和link的区别&#xff1a;…

delphi7aes加密解密与java互转_跨语言(java vs python vs nodejs)的RSA加解密问题探讨

多次被问到这样的问题&#xff1a;java服务端的rsa加密操作已经完成&#xff0c;返回一个16进制的字符串给python平台&#xff0c;但是在python进行私钥解密的时候发现行不通。。。。前端python加密&#xff0c;后端用java解密&#xff0c;解不出来还有诸如nodejs从理论上来说&…

类的定义、成员定义修饰符

类的定义 修饰符含义1无或internal 只能在当前项目中访问类&#xff0c;其它项目引用后也无法访问2public 可以任何地方访问类3abstract 不能实例化&#xff0c;只能供继承之用4sealed 不能供派生之用&#xff0c;只能实例化5internal abstract136public abstract237intern…

设备模型3

在上一篇分析中&#xff0c;多次提到了SYSFS&#xff0c;这是个什么东西&#xff1f;这可是个很大的TOPIC&#xff0c;关于它的讲述可以写本书&#xff0c;但是我们暂时的目标不是要完全啃投它所有的东西&#xff0c;没时间&#xff0c;没精力&#xff0c;我们只要掌握我们需要…