块设备驱动(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);
}
其结构示意图如下:
块设备驱动(1)_第1张图片

超级块的操作函数集如下:
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;
}将查找新的节点且新建块设备结构体并初始化。

你可能感兴趣的:(块设备驱动(1))