本文以字符设备为例,简述了应用程序访问设备驱动程序的基本原理,及其相关的数据结构struct inode、struct file、struct file_operations。。

/************************************************************************************

*本文为个人学习记录,如有错误,欢迎指正。

*本文参考资料: 

*        http://www.169it.com/tech-qa-linux/article-5682294992603241339.html

*        https://www.cnblogs.com/yanghong-hnu/p/5699528.html

*        https://www.cnblogs.com/wanghetao/archive/2012/05/28/2521675.html

*        http://www.cnblogs.com/xiaojiang1025/p/6196198.html

*        https://www.cnblogs.com/chen-farsight/p/6177870.html

*        http://www.cnblogs.com/xiaojiang1025/p/6363626.html

************************************************************************************/

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

在Linux内核中,当创建一个文件时,就会在相应的文件系统创建一个inode与之对应,文件实体和文件的inode是一 一对应的,创建好一个inode会存在存储器中,第一次open就会将inode在内存中有一个备份,同一个文件被多次打开并不会产生多个inode,当所有被打开的文件都被close之后,inode在内存中的实例才会被释放。

当创建一个设备文件(mknod或udev)时,也会在文件系统中创建一个inode,该inode用来存储关于这个文件的静态信息,其中包括该设备文件对应的设备号、文件路径以及对应的驱动对象等。

inode结构体中包含了设备文件的大量信息,着重关心以下结构体成员即可:

(1)dev_t i_rdev:表示设备文件对应的字符设备的设备号。

(2)struct cdev *i_cdev:指向字符设备对应的cdev结构体。

(3)const struct file_operations *i_fop:文件的操作方法集,创建设备文件的时候i_fops填充的是def_chr_fops,blk_blk_fops,def_fifo_fops,bad_sock_fops之一。

  1. //linux/fs.h
  2. struct inode {
  3. struct hlist_node i_hash; /* 哈希表 */
  4. struct list_head i_list; /* 索引节点链表 */
  5. struct list_head i_dentry; /* 目录项链表 */
  6. unsigned long i_ino; /* 节点号 */
  7. atomic_t i_count; /* 引用记数 */
  8. umode_t i_mode; /* 访问权限控制 */
  9. unsigned int i_nlink; /* 硬链接数 */
  10. uid_t i_uid; /* 使用者id */
  11. gid_t i_gid; /* 使用者id组 */
  12. kdev_t i_rdev; /* 实设备标识符 */
  13. loff_t i_size; /* 以字节为单位的文件大小 */
  14. struct timespec i_atime; /* 最后访问时间 */
  15. struct timespec i_mtime; /* 最后修改(modify)时间 */
  16. struct timespec i_ctime; /* 最后改变(change)时间 */
  17. unsigned int i_blkbits; /* 以位为单位的块大小 */
  18. unsigned long i_blksize; /* 以字节为单位的块大小 */
  19. unsigned long i_version; /* 版本号 */
  20. unsigned long i_blocks; /* 文件的块数 */
  21. unsigned short i_bytes; /* 使用的字节数 */
  22. spinlock_t i_lock; /* 自旋锁 */
  23. struct rw_semaphore i_alloc_sem; /* 索引节点信号量 */
  24. struct inode_operations *i_op; /* 索引节点操作表 */
  25. struct file_operations *i_fop; /* 默认的索引节点操作 */
  26. struct super_block *i_sb; /* 相关的超级块 */
  27. struct file_lock *i_flock; /* 文件锁链表 */
  28. struct address_space *i_mapping; /* 相关的地址映射 */
  29. struct address_space i_data; /* 设备地址映射 */
  30. struct dquot *i_dquot[MAXQUOTAS]; /* 节点的磁盘限额 */
  31. struct list_head i_devices; /* 块设备链表 */
  32. struct pipe_inode_info *i_pipe; /* 管道信息 */
  33. struct block_device *i_bdev; /* 块设备 */
  34. struct cdev *i_cdev; /* 字符设备 */
  35. unsigned long i_dnotify_mask; /* 目录通知掩码 */
  36. struct dnotify_struct *i_dnotify; /* 目录通知 */
  37. unsigned long i_state; /* 状态标志 */
  38. unsigned long dirtied_when; /* 首次修改时间 */
  39. unsigned int i_flags; /* 文件系统标志 */
  40. unsigned char i_sock; /* 可能是个套接字吧 */
  41. atomic_t i_writecount; /* 写者记数 */
  42. void *i_security; /* 安全模块 */
  43. __u32 i_generation; /* 索引节点版本号 */
  44. union {
  45. void *generic_ip; /* 文件特殊信息 */
  46. } u;
  47. };

struct inode

 

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

Linux中同一个文件可被多个进程打开,该文件每被打开一次,内核就会在该进程中创建一个struct file来描述该文件。由此可知,一个文件在内核中可能对应多个struct file,但是该文件只有唯一一个struct inode与之对应。

  1. struct file {
  2. union {
  3. struct list_head fu_list; //文件对象链表指针linux/include/linux/list.h
  4. struct rcu_head fu_rcuhead; //RCU(Read-Copy Update)是Linux 2.6内核中新的锁机制
  5. } f_u;
  6. struct path f_path; //包含dentry和mnt两个成员,用于确定文件路径
  7. #define f_dentry f_path.dentry //f_path的成员之一,当前文件的dentry结构
  8. #define f_vfsmnt f_path.mnt //表示当前文件所在文件系统的挂载根目录
  9. const struct file_operations *f_op; //与该文件相关联的操作函数
  10. atomic_t f_count; //文件的引用计数(有多少进程打开该文件)
  11. unsigned int f_flags; //对应于open时指定的flag
  12. mode_t f_mode; //读写模式:open的mod_t mode参数
  13. off_t f_pos; //该文件在当前进程中的文件偏移量
  14. struct fown_struct f_owner; //该结构的作用是通过信号进行I/O时间通知的数据。
  15. unsigned int f_uid, f_gid; //文件所有者id,所有者组id
  16. struct file_ra_state f_ra; //在linux/include/linux/fs.h中定义,文件预读相关
  17. unsigned long f_version;
  18. #ifdef CONFIG_SECURITY
  19. void *f_security;
  20. #endif
  21. /* needed for tty driver, and maybe others */
  22. void *private_data;
  23. #ifdef CONFIG_EPOLL
  24. /* Used by fs/eventpoll.c to link all the hooks to this file */
  25. struct list_head f_ep_links;
  26. spinlock_t f_ep_lock;
  27. #endif /* #ifdef CONFIG_EPOLL */
  28. struct address_space *f_mapping;
  29. };

struct file

  着重关注以下结构体成员:

(1)struct inode *f_inode:指向该文件对应的inode。

(2)const struct file_operations *f_op:驱动提供的file_operations对象,这个对象应该在第一次open()的时候被填充(调用char_open实现),直至该文件被close。

详见Linux字符设备:字符设备管理框架

 

(1)当一个字符设备文件被创建的时候,内核会构造相应的inode,并对其进行初始化操作。

  1. //fs/char_dev.c
  2. const struct file_operations def_chr_fops = {
  3. .open = chrdev_open,
  4. .llseek = noop_llseek,
  5. };
  6. //fs/inode.c
  7. void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
  8. {
  9. inode->i_mode = mode;
  10. if (S_ISCHR(mode)) {
  11. inode->i_fop = &def_chr_fops;
  12. inode->i_rdev = rdev;
  13. } else if (S_ISBLK(mode)) {
  14. inode->i_fop = &def_blk_fops;
  15. inode->i_rdev = rdev;
  16. } else if (S_ISFIFO(mode))
  17. inode->i_fop = &def_fifo_fops;
  18. else if (S_ISSOCK(mode))
  19. inode->i_fop = &bad_sock_fops;
  20. else
  21. printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
  22. " inode %s:%lu\n", mode, inode->i_sb->s_id,
  23. inode->i_ino);
  24. }

(2)chrdev_open()
–359–>尝试将inode->i_cdev(一个cdev结构指针)保存在局部变量p中;
–360–>如果p为空,即inode->i_cdev为空;
–364–>我们就根据inode->i_rdev(设备号)通过kobj_lookup()搜索cdev_map,并返回与之对应kobj
–367–>由于kobject是cdev的父类,我们根据container_of很容易找到相应的cdev结构并将其保存在inode->i_cdev中;
–374–>找到了cdev,我们就可以将inode->devices挂接到inode->i_cdev的管理链表中,这样下次就不用重新搜索;
–378–>直接cdev_get()即可;
–386–>找到了我们的cdev结构,我们就可以将其中的操作方法集inode->i_cdev->ops传递给filp->f_ops(386-390);
–392–>这样,我们就可以回调我们的设备打开字符设备fops中的char_opena函数。如果我们没有实现自己的open接口,就什么都不做,也不是错。

  1. 351 static int chrdev_open(struct inode *inode, struct file *filp)
  2. 352 {
  3. 353 const struct file_operations *fops;
  4. 354 struct cdev *p;
  5. 355 struct cdev *new = NULL;
  6. 356 int ret = 0;
  7. ...
  8. 359 p = inode->i_cdev;
  9. 360 if (!p) {
  10. 361 struct kobject *kobj;
  11. 362 int idx;
  12. ...
  13. 364 kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
  14. ...
  15. 367 new = container_of(kobj, struct cdev, kobj);
  16. 369 /* Check i_cdev again in case somebody beat us to it while
  17. 370 we dropped the lock. */
  18. 371 p = inode->i_cdev;
  19. 372 if (!p) {
  20. 373 inode->i_cdev = p = new;
  21. 374 list_add(&inode->i_devices, &p->list);
  22. 375 new = NULL;
  23. 376 } else if (!cdev_get(p))
  24. 377 ret = -ENXIO;
  25. 378 } else if (!cdev_get(p))
  26. 379 ret = -ENXIO;
  27. ...
  28. 386 fops = fops_get(p->ops);
  29. ...
  30. 390 replace_fops(filp, fops);
  31. 391 if (filp->f_op->open) {
  32. 392 ret = filp->f_op->open(inode, filp);
  33. ...
  34. 395 }
  35. 396
  36. 397 return 0;
  37. 398
  38. 399 out_cdev_put:
  39. 400 cdev_put(p);
  40. 401 return ret;
  41. 402 }

版权声明:本文为linfeng-learning原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/linfeng-learning/p/9309833.html