Linux文件系统与设备驱动

前言

由于字符设备和块设备都良好地体现了“一切都是文件”的设计思想,掌握Linux文件系统、设备文件系统的知识就显得相当重要了。

  • 首先,驱动最终通过与文件操作相关的系统调用或C库函数(本质也基于系统调用)被访问,而设备驱动的结构最终也是为了迎合提供给应用程序员的API。
  • 其次,驱动工程师在设备驱动中不可避免地会与设备文件系统打交道,包括从Linux 2.4内核的devfs文件系统到Linux 2.6以后的udev。

Linux文件系统与设备驱动

图所示为Linux中虚拟文件系统、磁盘/Flash文件系统及一般的设备文件与设备驱动程序之间的关系。

在这里插入图片描述

应用程序和VFS之间的接口是系统调用

VFS与文件系统以及设备文件之间的接口是file_operations结构体成员函数

这个结构体包含对文件进行打开、关闭、读写、控制的一系列成员函数,关系如图5.2所示。

在这里插入图片描述

  • 字符设备
    由于字符设备的上层没有类似于磁盘的ext2等文件系统,所以字符设备的file_operations成员函数就直接由设备驱动提供了,在稍后的第6章,将会看到file_operations正是字符设备驱动的核心。

  • 块设备
    块设备有两种访问方法,

    • 一种方法是不通过文件系统直接访问裸设备,在Linux内核实现了统一的def_blk_fops这一file_operations,它的源代码位于fs/block_dev.c,所以当我们运行类似于“dd if=/dev/sdb1of=sdb1.img”的命令把整个/dev/sdb1裸分区复制到sdb1.img的时候,内核走的是def_blk_fops这个file_operations;
    • 另外一种方法是通过文件系统来访问块设备,file_operations的实现则位于文件系统内,文件系统会把针对文件的读写转换为针对块设备原始扇区的读写。ext2、fat、Btrfs等文件系统中会实现针对VFS的file_operations成员函数,设备驱动层将看不到file_operations的存在。

在设备驱动程序的设计中,一般而言,会关心file和inode这两个结构体。

1.file结构体

file结构体代表一个打开的文件,系统中每个打开的文件在内核空间都有一个关联的struct file。

它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。在内核和驱动源代码中,struct file的指针通常被命名为file或filp(即file pointer)。代码清单5.3给出了文件结构体的定义。

 1 struct file {
 2  union {
 3      struct llist_node    fu_llist;
 4      struct rcu_head      fu_rcuhead;
 5  } f_u;
 6  struct path              f_path;
 7 #define f_dentry          f_path.dentry
 8  struct inode             *f_inode;      /* cached value */
 9  const struct file_operations*f_op;      /* 和文件关联的操作*/
10
11  /*
12   * Protects f_ep_links, f_flags.
13   * Must not be taken from IRQ context.
14   */
15  spinlock_t          f_lock;
16  atomic_long_t       f_count;
17  unsigned int        f_flags;       /*文件标志,如O_RDONLY、O_NONBLOCK、O_SYNC*/
18  fmode_t             f_mode;        /*文件读/写模式,FMODE_READ和FMODE_WRITE*/
19  struct mutex        f_pos_lock;
20  loff_t              f_pos;         /* 当前读写位置 */
21  struct fown_struct  f_owner;
22  const struct cred   *f_cred;
23  struct file_ra_statef_ra;
24
25 u64                f_version;
26 #ifdef CONfiG_SECURITY
27  void         *f_security;
28 #endif
29  /* needed for tty driver, and maybe others */
30  void         *private_data;        /*文件私有数据*/
31
32 #ifdef CONfiG_EPOLL
33  /* Used by fs/eventpoll.c to link all the hooks to this file */
34  struct list_head     f_ep_links;
35  struct list_head     f_tfile_llink;
36 #endif                              /* #ifdef CONfiG_EPOLL */
37  struct address_space*f_mapping;
38 } __attribute__((aligned(4)));      /* lest something weird decides that 2 is OK */
  • 文件读/写模式mode、
  • 标志f_flags都是设备驱动关心的内容,
  • 而私有数据指针private_data在设备驱动中被广泛应用,大多被指向设备驱动自定义以用于描述设备的结构体

下面的代码可用于判断以阻塞还是非阻塞方式打开设备文件:

if (file->f_flags & O_NONBLOCK)     /* 非阻塞 */
      pr_debug("open: non-blocking\n");
else                                /* 阻塞 */
      pr_debug("open: blocking\n");

2.inode结构体

VFS inode包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。

它是Linux管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁,inode结构体的定义如代码清单5.4所示。

 1 struct inode {
 2     ...
 3     umode_t i_mode;            /* inode的权限 */
 4     uid_t i_uid;               /* inode拥有者的id */
 5     gid_t i_gid;               /* inode所属的群组id */
 6     dev_t i_rdev;              /* 若是设备文件,此字段将记录设备的设备号 */
 7     loff_t i_size;             /* inode所代表的文件大小 */
 8
 9     struct timespec i_atime;   /* inode最近一次的存取时间 */
10     struct timespec i_mtime;   /* inode最近一次的修改时间 */
11     struct timespec i_ctime;   /* inode的产生时间 */
12
13     unsigned int        i_blkbits;
14     blkcnt_t        i_blocks;  /* inode所使用的block数,一个block为512 字节 */
15     union {
16       struct pipe_inode_info  *i_pipe;
17       struct block_device *i_bdev;
18                                /* 若是块设备,为其对应的block_device结构体指针 */
19       struct cdev *i_cdev;     /* 若是字符设备,为其对应的cdev结构体指针 */
20     }
21     ...
22 };
  • 对于表示设备文件的inode结构,
  • i_rdev字段包含设备编号。
  • Linux内核设备编号分为主设备编号和次设备编号,前者为dev_t的高12位,后者为dev_t的低20位。

下列操作用于从一个inode中获得主设备号和次设备号:

unsigned int iminor(struct inode *inode);
unsigned int imajor(struct inode *inode);

查看/proc/devices文件可以获知系统中注册的设备,第1列为主设备号,第2列为设备名,如:

Character devices:
   1 mem
   2 pty
   3 ttyp
   4 /dev/vc/0
   4 tty
   5 /dev/tty
   5 /dev/console
   5 /dev/ptmx
   7 vcs
  10 misc
  13 input
  21 sg
  29 fb
 128 ptm
 136 pts
 171 ieee1394
 180 usb
 189 usb_device
Block devices:
   1 ramdisk
   2 fd
   8 sd
   9 md
  22 ide1
 ...

查看/dev目录可以获知系统中包含的设备文件,日期的前两列给出了对应设备的主设备号和次设备号:

crw-rw----    1 root     uucp       4,  64 Jan 30  2003 /dev/ttyS0
brw-rw----    1 root     disk       8,   0 Jan 30  2003 /dev/sda
  • 主设备号是与驱动对应的概念,同一类设备一般使用相同的主设备号,不同类的设备一般使用不同的主设备号(但是也不排除在同一主设备号下包含有一定差异的设备)。
  • 因为同一驱动可支持多个同类设备,因此用次设备号来描述使用该驱动的设备的序号,序号一般从0开始。

内核Documents目录下的devices.txt文件描述了Linux设备号的分配情况,它由LANANA(the Linux Assigned Names and Numbers authority,网址为http://www.lanana.org/)组织维护,Torben Mathiasen([email protected])是其中的主要维护者。

内容来自:Linux设备驱动开发详解

猜你喜欢

转载自blog.csdn.net/weixin_45264425/article/details/130713215