块设备驱动(1) 收藏 每个块设备用一个块设备结构进行描述,其结构如下:struct block_device { dev_t bd_dev; /* not a kdev_t - it's a search key */ struct inode * bd_inode; //多大用处 int bd_openers;//打开该设备计数值 struct semaphore bd_sem; //打开或关闭互斥 struct semaphore bd_mount_sem; /* mount mutex */ struct list_head bd_inodes;//设备节点链表 void * bd_holder; int bd_holders; struct block_device * bd_contains;//含有非分区块结构体 unsigned bd_block_size;//块大小非扇区大小,在申请队列中设定 struct hd_struct * bd_part;//硬件分区结构 /* number of times partitions within this device have been opened. */ unsigned bd_part_count; int bd_invalidated; struct gendisk * bd_disk;//虚拟块设备下层的通用磁盘结构 struct list_head bd_list; struct backing_dev_info *bd_inode_backing_dev_info;//反向设备信息 unsigned long bd_private;//私有数据};
块设备的管理是以一个块设备伪文件系统组织的,每个块设备是这个文件系统上的一个块设备节点,其节点结构如下:struct bdev_inode { struct block_device bdev;//块设备结构 struct inode vfs_inode;//在vfs中的节点};
static struct vfsmount *bd_mnt;//块设备文件系统在VFS中的挂载点结构体struct super_block *blockdev_superblock;//块设备文件系统超级块结构体,由于操作块设备文件系统
void __init bdev_cache_init(void){ int err; bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode), 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_PANIC, init_once, NULL);//为struct bdev_inode建立slab缓存,用于分配对象给具体的块设备,用于分配节点以及 //其他缓存 err = register_filesystem(&bd_type);//注册块设备文件系统 if (err) panic("Cannot register bdev pseudo-fs"); bd_mnt = kern_mount(&bd_type);//将块设备文件系统挂载到vfs文件系统链表 err = PTR_ERR(bd_mnt); if (IS_ERR(bd_mnt)) panic("Cannot create bdev pseudo-fs"); blockdev_superblock = bd_mnt->mnt_sb;//得到块设备文件系统的超级块} 其为块文件系统的结构类型结构体:static struct file_system_type bd_type = { .name = "bdev",//名称 .get_sb = bd_get_sb,//得到超级块函数 .kill_sb = kill_anon_super,};
static struct super_block *bd_get_sb(struct file_system_type *fs_type,int flags, const char *dev_name, void *data){//分配超级块结构 根节点 根目录的入口结构并初始化 return get_sb_pseudo(fs_type, "bdev:", &bdev_sops, 0x62646576);}其结构示意图如下:
超级块的操作函数集如下:static struct super_operations bdev_sops = { .statfs = simple_statfs, .alloc_inode = bdev_alloc_inode,//分配节点 .destroy_inode = bdev_destroy_inode,//销毁节点 .drop_inode = generic_delete_inode,//通用删除节点 .clear_inode = bdev_clear_inode,//将设备节点从总设备节点上删除};以bdev_alloc_inode为例: static struct inode *bdev_alloc_inode(struct super_block *sb){ struct bdev_inode *ei = kmem_cache_alloc(bdev_cachep, SLAB_KERNEL); //为某个块设备分配一个块设备节点 if (!ei) return NULL; return &ei->vfs_inode;//还回节点结构}
static void bdev_destroy_inode(struct inode *inode){ struct bdev_inode *bdi = BDEV_I(inode);//根据节点获取块节点地址
bdi->bdev.bd_inode_backing_dev_info = NULL; kmem_cache_free(bdev_cachep, bdi);//释放slab对象,即节点缓存}
块设备伪文件的文件操作函数集如下:struct file_operations def_blk_fops = { .open = blkdev_open,//打开设备函数 .release = blkdev_close,//关闭设备函数 .llseek = block_llseek,//移动设备函数 .read = generic_file_read,//块设备读函数 .write = blkdev_file_write,//块设备写函数 .aio_read = generic_file_aio_read,//地址空间IO读函数 .aio_write = blkdev_file_aio_write,//地址空间IO写函数 .mmap = generic_file_mmap, .fsync = block_fsync, .unlocked_ioctl = block_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_blkdev_ioctl, #endif .readv = generic_file_readv, .writev = generic_file_write_nolock, .sendfile = generic_file_sendfile,};当打开设备文件时,文件系统sys_open->filp_open->dentry_open->blkdev_open的任务就根据主设备号找到相应的数据结构。只分析打开函数:static int blkdev_open(struct inode * inode, struct file * filp){ struct block_device *bdev; int res;
filp->f_flags |= O_LARGEFILE;//容许大文件获取
bdev = bd_acquire(inode);//获取块设备
res = do_open(bdev, filp); if (res) return res;
if (!(filp->f_flags & O_EXCL) ) return 0;
if (!(res = bd_claim(bdev, filp))) return 0;
blkdev_put(bdev); return res;}其中bd_acquire用于获取块设备结构体,若节点中块设备存在则提取即可;若块设备不存在则需要根据设备号在块设备文件系统中查找响应的节点并且建立新的块设备。由bdget函数完成。static struct block_device *bd_acquire(struct inode *inode){ struct block_device *bdev; spin_lock(&bdev_lock); bdev = inode->i_bdev;//获取块设备结构 if (bdev && igrab(bdev->bd_inode)) {//如果块设备存在且节点不为空 spin_unlock(&bdev_lock); return bdev;//返回块设备结构体地址}//为空时或节点为空时 spin_unlock(&bdev_lock); bdev = bdget(inode->i_rdev);//根据设备号建立块设备结构和对应的节点 if (bdev) { spin_lock(&bdev_lock); if (inode->i_bdev) __bd_forget(inode);//将设备删除 inode->i_bdev = bdev; //将新建的块设备赋给节点,此时的节点与块设备中的节点不同故而赋值一次 inode->i_mapping = bdev->bd_inode->i_mapping; //实质将新建节点的i_mapping给老节点i_mapping list_add(&inode->i_devices, &bdev->bd_inodes);//添加到块设备节点链表 spin_unlock(&bdev_lock); } return bdev;}bdget如下所示:struct block_device *bdget(dev_t dev)//根据设备号建立块设备结构{ struct block_device *bdev; struct inode *inode;
inode = iget5_locked(bd_mnt->mnt_sb, hash(dev), bdev_test, bdev_set, &dev);//从挂载的块文件体统中获取新的节点
if (!inode) return NULL;
bdev = &BDEV_I(inode)->bdev;//节点中设备与给新结构体
if (inode->i_state & I_NEW) {//若节点是新的则初始化 bdev->bd_contains = NULL;//分区为空 bdev->bd_inode = inode;//将新建的块文件系统中的节点保存到块设备中便于以后使用 bdev->bd_block_size = (1 << inode->i_blkbits); inode->i_data.a_ops = &def_blk_aops; //将块设备操作集赋给设备地址缓存操作指针,即用于操作页面缓存 mapping_set_gfp_mask(&inode->i_data, GFP_USER);//设置设备地址缓存用于用户空间 inode->i_data.backing_dev_info = &default_backing_dev_info; spin_lock(&bdev_lock); list_add(&bdev->bd_list, &all_bdevs);//将块设备连接到全局链表上 spin_unlock(&bdev_lock); unlock_new_inode(inode); } return bdev;}将查找新的节点且新建块设备结构体并初始化。
发表于 @ 2010年02月01日
本文来自博客,转载请标明出处:http://blog.csdn.net/yd4330152763132/archive/2010/02/01/5275787.aspx