设备并不是通过其文件名来标识,而是通过文件的主、次设备号标识(文件名和文件的主次设备号在设备文件的父目录的inode的数据区中表现出来的,这是fs层的东东)。 设备文件和普通文件的区别: 查看设备文件的命令为:ls -l /dev/ 1:访问权限前面的字母b/c,分别表示块设备和字符设备。 2:设备文件没有文件长度,而增加了另外两个值,分别为主设备号和次设备号。二者共同形成一个唯一的号码,内核由此可以查找到对应的设备驱动程序。 由于引入了udev机制,/dev不再放置到基于磁盘的文件系统中,而是使用tmpfs,这是RAM磁盘文件系统ramfs的一种轻型变体。这意味着设备结点不是持久性的,系统关机/重启后就会消失。 IOCTL:输入输出控制接口,是用于配置和修改特定设备属性的通用接口。 内核如果能了解到系统中有哪些字符设备和块设备可用,那自然是很有利的,因而需要维护一个数据库,此外必须提供一个接口,以便驱动程序开发者能够将新项添加到数据库中。 数据结构: 1、设备数据库 尽管块设备和字符设备彼此的行为有很大的不同,但用于跟踪所有可用设备的数据库是相同的。因为字符设备和块设备都是通过唯一的设备号标识。但是,数据库会根据块设备/字符设备来跟踪不同的对象。 。每个字符设备都表示为一个struct cdev的实例 。struct genhd用于管理块设备的分区,作用类似于字符设备的cdev. 有两个全局数组(bdev_map用于块设备,cdev_map用于字符设备)用来实现散列表,使用主设备号作为散列键。cdev_map和bdev_map都是同一数据结构struct kobj_map的实例。散列的方法很简单:major%5. struct kobj_map { struct probe { 互斥量lock实现了对散列表访问的串行化.struct probe的成员如下: next:将所有散列表链接在一个单链表中。(没有搞明白这个单链表中的设备的主设备号都是相同的否?) dev: 表示设备号,包括主次设备号 rang:从设备号的连续范围存储在range中。那么与设备关联的各个从设备号的范围是[MINORS(dev), MINORS(dev)+range-1]. owner:指向提供设备驱动程序的模块。 get:指向一个函数,可以返回与设备关联的kobject实例。 data:字符设备和块设备的区别就在于data.对于字符设备,他指向struct cdev的一个实例,而对于块设备,则指向struct genhd的实例。
2、字符设备范围数据库 第二类数据库只是用于字符设备。他是用于管理为驱动程序分配的设备号范围。驱动程序可以请求一个动态的设备号(用register_chrdev_region()函数注册设备号),或者指定一个范围,从中获取(用alloc_chrdev_region()函数分配设备号)。 再次使用散列表来跟踪已经分配的设备号范围,并同样使用主设备号作为散列键。数据结构如下: static struct char_device_struct { next:链接同一散列行中的所有散列元素 major:主设备号 baseminor是包含minorct个从设备号的连续范围的最小的从设备 name为设备提供了一个标识符 与文件系统关联 inode中设备文件成员 只列出与驱动有关的成员 struct inode{ ... dev_t i_rdev; ... umode_t i_mode; ... struct file_operations *i_fop; ... union { struct block_device *i_bdev; struct cdev *i_cdev; }; .... }; i_mode:为唯一的标识与一个设备文件关联,内核在i_mode中存储量文件类型(面向块/面向字符) i_rdev中存储了主次设备号 i_fop是一组函数指针的集合,包括许多文件操作(open,read,write等),这些有虚拟文件系统使用来出来块设备 内核会根据inode表示块设备还是字符设备,来使用i_bdev/i_cdev指向更多具体信息。 标准文件操作 在打开一个设备文件时,各种文件系统的实现都会调用init_special_inode函数,为字符设备或者块设备文件创建一个Inode(在文件系统层). 在此还是有疑问的,在打开设备的时候才创建Inode,难道在设备文件创建的时候fs系统没有创建Inode吗,fs只是把设备文件的ID和文件名存于/dev的inode的dentry中吗? 针对上面的问题需要说明的是:在设备文件打开的时候和创建设备文件的时候都创建了inode,但这两个inode不是同一个,是两 个不同类型的数据。设备文件创建的时候生成的Inode是保存在硬盘中的,他的结构比较简单,随设备文件的删除会消失的。而设备文件打开的时候创建的 inode是struct inode结构体,他是保存在ram中的,随着文件的关闭会消失的。 对下面的问题“fs只是把设备文件的id 和文件名存于/dev的inode的dentry中吗?”这样的猜想是错误的,有这样的说法,/dev目录是一个临时的文件夹,他并不在inode块中存 在inode结点,但他目录下的文件在inode块中是存在inode结点的。那么我们就该想了,那fs是如何找到/dev目录下的文件呢?设备文件是一 类比较特殊的文件,他的查找方式跟普通的文件的查找方式是不同的。其实设备文件根本就不需要查找他在磁盘上对应的inode结点,因为设备文件根本就没有 数据区,我们需要的设备文件的设备操作指针(f_ops).如何找到设备操作指针的呢?看下面的黑体部分就可以了。(上面的说法对字符设备文件是可行的, 但没有看块设备,不知道块设备是否也是这样的。其实也同样存在一个问题,按照上面的解释,我们完全可以不用为设备文件在磁盘上创建一个inode结点,但 系统还是建了,可以肯定是这样的inode结点是有作用的,系统不会干没意义的事的) void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev) 用于字符设备的标准操作 fs/device.c const struct file_operations def_chr_fops = { chrdev_open()函数的主要任务是向该结构(其实不明白"该结构"指的是什么结构,似乎有点像struct file)填入已打开设备的函数指针(file_operation),使得能够在设备文件上执行有意义的操作,并最终能够操作设备本身。 chrdev_open()函数的框图: 假定表示设备文件的inode此前没有打开过,根据设备号,kobject_lookup查询字符设备的数据库(cdev_map),并返回与该驱动程序向关联的kobject实例。该返回值可用于获取cdev实例。 获取了对应于设备的cdev实例,内核通过cdev->ops还可以访问特定于设备的file_operations.接下来设置各种数据结构之间的关联
inode->i_cdev指向所选择的cdev实例。在下一次打开该inode时,就不必再查询字符设备的数据库,因为可以使用缓存的值。 该inode将添加到cdev->list file->f_ops是用于struct file新的file_operations,设置为指向struct cdev给出的file_operations实例。 接下来调用struct file新的file_operations中的open函数(现在是用于特定的设备,我们针对特定的设备在驱动层实现的open函数),在设备上执行所需的初始化任务。
用于块设备的标准操作 fs/block_dev.c const struct file_operations def_blk_fops = { 尽管file_operations与block_device_operations的结构类似,但两者不能混淆。 file_operations由VFS层用来与用户空间通信的,他里面的函数是有用户层来调用的。其中的函数会调用 block_device_operations中的函数,以实现与块设备的通信。block_device_operations必须针对各种具体的块 设备分别实现,对设备的属性加以抽象,而在此基础上建立file_operations,是用户可以使用相同的操作(file_operations提供 的函数)即可处理所有的块设备。 |
|