对于块设备来说,驱动程序的注册不仅在其初始化的时候进行而且在编译的时候也要进行注册。在初始化时通过 register_blkdev( ) 函数将相应的块设备添加到数组 blkdevs 中,该数组在fs/block_dev.c中定义如下:
static
struct {
const char *name;
struct block_device_operations *bdops;
} blkdevs[MAX_BLKDEV];
从Linux2.4开始,块设备表的定义与下一节要介绍的字符设备表的定义有所不同。因为每种具体的块设备都有一套具体的操作,因而各自有一个类似于file_operations那样的数据结构,称为block_device_operations结构,其定义为:
struct
block_device_operations {
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
int (*ioctl) (struct inode *, struct file *, unsigned,
unsigned long);
int (*check_media_change) (kdev_t);
int (*revalidate) (kdev_t);
struct module *owner;
};
如果说file_operation结构是连接虚拟的VFS文件的操作与具体文件系统的文件操作之间的枢纽,那么block_device_operations就是连接抽象的块设备操作与具体块设备操作之间的枢纽。
具体的块设备是由主设备号唯一确定的,因此,主设备号唯一地确定了一个具体的block_device_operations数据结构。
下面我们来看 register_blkdev( )函数的具体实现,其代码在fs/block_dev.c中:
int
register_blkdev(unsigned int major, const char * name, struct
block_device_operations *bdops)
{
if (major == 0) {
for (major = MAX_BLKDEV-1; major > 0;
major--) {
if (blkdevs[major].bdops == NULL) {
blkdevs[major].name = name;
blkdevs[major].bdops = bdops;
return major;
}
}
return -EBUSY;
}
if (major >= MAX_BLKDEV)
return -EINVAL;
if (blkdevs[major].bdops && blkdevs[major].bdops !=
bdops)
return -EBUSY;
blkdevs[major].name = name;
blkdevs[major].bdops = bdops;
return 0;
}
这个函数的第一个参数是主设备号,第二个参数是设备名称的字符串,第三项参数是指向具体设备操作的指针。如果一切顺利则返回0,否则返回负值。如果指定的主设备号为0,此函数将会搜索空闲的主设备号分配给该设备驱动程序并将其作为返回值。
那么,块设备注册到系统以后,怎样与文件系统联系起来呢,也就是说,文件系统怎么调用已注册的块设备,这还得从file_operations结构说起。
我们先来看一下块设备的file_operations结构的定义,其位于fs/block_dev.c中:
struct
file_operations def_blk_fops = {
open:
blkdev_open,
release:
blkdev_close,
llseek:
block_llseek,
read:
generic_file_read,
write:
generic_file_write,
mmap:
generic_file_mmap,
fsync:
block_fsync,
ioctl:
blkdev_ioctl,
};
下面我们以open()系统调用为例,说明用户进程中的一个系统调用如何最终与物理块设备的操作联系起来。在此,我们仅仅给出几个open()函数的调用关系,如图11.6所示。
open() 位于用户程序中
sys_open()
filp_open()
dentry_open()
blkdev_open() 位于fs/block_dev.c中
图11.6 几个open()函数的调用关系
如图所示,当调用open()系统调用时,其最终会调用到def_blk_fops 的blkdev_open()函数。blkdev_open()函数的任务就是根据主设备号找到对应的block_device_operations结构,然后再调用block_device_operations结构中的函数指针open所指向的函数,如果open所指向的函数非空,就调用该函数打开最终的物理块设备。这就简单地说明了块设备注册以后,从最上层的系统调用到具体的打开一个设备的过程。
另外要说明的是,如果选择了通过设备文件系统Devfs进行注册,则调用devfs_register_blkdev()函数,该函数的说明及代码在fs/devfs/base.c中定义如下:
/**
*
devfs_register_blkdev - Optionally register a conventional block driver.
*
@major: The major number for the driver.
*
@name: The name of the driver (as seen in /proc/devices).
* @bdops: The &block_device_operations structure pointer.
*
* This function
will register a block driver provided the "devfs=only"
* option was not provided at boot time.
* Returns 0 on
success, else a negative error code on failure.
*/
int
devfs_register_blkdev (unsigned int major, const char *name,
struct block_device_operations *bdops)
{
if (boot_options & OPTION_ONLY) return 0;
return register_blkdev (major, name, bdops);
} /* End Function
devfs_register_blkdev */