字符设备驱动结构

  1. 字符设备驱动结构
    1. file文件结构体
    2. inode结构体
    3. chardevs 数组
    4. cdev结构体
    5. 文件系统中(上层open()) 对字符设备文件的访问

字符设备驱动结构

一般的设备文件与设备驱动程序值间的函数调用关系

用户空间使用 open() 函数打开一个字符设备 fd = open(“/dev/hello”,O_RDWR ) , 这一函数会调用两个数据结构 struct inode{…}struct file{…}

file文件结构体

而struct file,却是内核当中的数据结构 ,因此,它也不会出现在用户层程序中。

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

inode结构体

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

chardevs 数组

从图中可以看出, 通过数据结构 struct inode{…} 中的 i_cdev 成员可以找到cdev ,而所有的字符设备都在 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; background-color: rgb(255, 255, 255);">  </span>

如果分配了一个设备号,就会创建一个 struct char_device_struct 的对象,并将其添加到 chrdevs 中;这样,通过chrdevs数组,我们就可以知道分配了哪些设备号。

register_chrdev_region( ) 分配指定的设备号范围

alloc_chrdev_region( ) 动态分配设备范围

他们都主要是通过调用函数 __register_chrdev_region() 来实现的; 要注意,这两个函数仅仅是注册设备号! 如果要和cdev关联起来,还要调用cdev_add()。

cdev结构体

​ 在Linux内核中, 使用cdev结构体来描述一个字符设备

字符设备三者关系

<include/linux/cdev.h>

struct cdev { 
    struct kobject kobj;                  //内嵌的内核对象.
    struct module *owner;                 //该字符设备所在的内核模块的对象指针.
    const struct file_operations *ops;    //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体.
    struct list_head list;                //用来将已经向内核注册的所有字符设备形成链表.
    dev_t dev;                            //字符设备的设备号,由主设备号和次设备号构成.
    unsigned int count;                   //隶属于同一主设备号的次设备号的个数.
};

文件系统中(上层open()) 对字符设备文件的访问

对于一个字符设备文件, 其inode->i_cdev 指向字符驱动对象cdev, 如果i_cdev为 NULL ,则说明该设备文件没有被打开.

由于多个设备可以共用同一个驱动程序.所以,通过字符设备的inode 中的i_devices 和 cdev中的list组成一个链表

首先,系统调用open打开一个字符设备的时候, 通过一系列调用,最终会执行到 chrdev_open

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的相应操作。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 351134995@qq.com

×

喜欢就点赞,疼爱就打赏