ブロックデバイススペシャルファイルの参照
ファイルの読み書きは、FILEのアドレスマップのpageをキャッシュとして行います。ファイルをopenすると、ファイルのdentryを取得し、__dentry_open()がコールされ、struct fileを取得します。この時、f->f_mapping = inode->i_mappingとして、オープンするファイルinodeのアドレスマップをpageキャッシュとなります。パーティション毎のデバイスファイルはそれぞれにinodeを有しているわけで、従ってパーティション毎のf->f_mappingも独立したものになります。
inodeにi_fopコールバックがある場合、f->f_op->openがコールされます。通常はgeneric_file_open()がコールされ、フラグ/サイズのチェックが行われるだけですが、ブロックデスペシャルファイルの場合、blkdev_open()がコールされ、filp->f_mapping = bdev->bd_inode->i_mappingで、ブロックデバイス自身のアドレスマップをが設定されます。
パーティションは同じブロックデバイスで、パーティションのデバイススペシャルファイルのf->f_mappingは、ブロックデバイスのそれとなり、かかるパーティションのpageキャッシュはデバイスのサイズに応じたpageを共有することになります。
ブロックデバイスにバルク的なやり取りで巨大なデータを保存する等、直接デバイススペシャルファイルで読み書きすることで、inodeを介する必要はなく、またかかるinode/データブロック等も必要ないため、高速でデバイス領域全体を実領域として使うことができます。またユーザランドサイドで単純なストリームのバルクデータ管理機能を実装することで、セキュリテをかねた保存デバイスシステムの構築も可能です。
inodeにi_fopコールバックがある場合、f->f_op->openがコールされます。通常はgeneric_file_open()がコールされ、フラグ/サイズのチェックが行われるだけですが、ブロックデスペシャルファイルの場合、blkdev_open()がコールされ、filp->f_mapping = bdev->bd_inode->i_mappingで、ブロックデバイス自身のアドレスマップをが設定されます。
パーティションは同じブロックデバイスで、パーティションのデバイススペシャルファイルのf->f_mappingは、ブロックデバイスのそれとなり、かかるパーティションのpageキャッシュはデバイスのサイズに応じたpageを共有することになります。
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, struct file *f, int (*open)(struct inode *, struct file *), const struct cred *cred) { static const struct file_operations empty_fops = {}; struct inode *inode; int error; f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE; if (unlikely(f->f_flags & O_PATH)) f->f_mode = FMODE_PATH; inode = dentry->d_inode; if (f->f_mode & FMODE_WRITE) { error = __get_file_write_access(inode, mnt); if (error) goto cleanup_file; if (!special_file(inode->i_mode)) file_take_write(f); } f->f_mapping = inode->i_mapping; f->f_path.dentry = dentry; f->f_path.mnt = mnt; f->f_pos = 0; file_sb_list_add(f, inode->i_sb); if (unlikely(f->f_mode & FMODE_PATH)) { f->f_op = &empty_fops; return f; } f->f_op = fops_get(inode->i_fop); error = security_dentry_open(f, cred); if (error) goto cleanup_all; error = break_lease(inode, f->f_flags); if (error) goto cleanup_all; if (!open && f->f_op) open = f->f_op->open; if (open) { error = open(inode, f); if (error) goto cleanup_all; } if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) i_readcount_inc(inode); f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping); if (f->f_flags & O_DIRECT) { if (!f->f_mapping->a_ops || ((!f->f_mapping->a_ops->direct_IO) && (!f->f_mapping->a_ops->get_xip_mem))) { fput(f); f = ERR_PTR(-EINVAL); } } return f; cleanup_all: fops_put(f->f_op); if (f->f_mode & FMODE_WRITE) { put_write_access(inode); if (!special_file(inode->i_mode)) { file_reset_write(f); mnt_drop_write(mnt); } } file_sb_list_del(f); f->f_path.dentry = NULL; f->f_path.mnt = NULL; cleanup_file: put_filp(f); dput(dentry); mntput(mnt); return ERR_PTR(error); } static int blkdev_open(struct inode * inode, struct file * filp) { struct block_device *bdev; filp->f_flags |= O_LARGEFILE; if (filp->f_flags & O_NDELAY) filp->f_mode |= FMODE_NDELAY; if (filp->f_flags & O_EXCL) filp->f_mode |= FMODE_EXCL; if ((filp->f_flags & O_ACCMODE) == 3) filp->f_mode |= FMODE_WRITE_IOCTL; bdev = bd_acquire(inode); if (bdev == NULL) return -ENOMEM; filp->f_mapping = bdev->bd_inode->i_mapping; return blkdev_get(bdev, filp->f_mode, filp); } int generic_file_open(struct inode * inode, struct file * filp) { if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS) return -EOVERFLOW; return 0; }通常ファイルのオープンは、そのファイルのファイルシステムのオペレーションコールバックが、通常のファイル/ディレクトリ/リンクに応じてnode->i_opに設定されますが、そうでないなら init_special_inode()がコールされ、デバイススペシャルファイルの時def_blk_fopsが設定されます。
const struct file_operations def_blk_fops = { .open = blkdev_open, .release = blkdev_close, .llseek = block_llseek, .read = do_sync_read, .write = do_sync_write, .aio_read = generic_file_aio_read, .aio_write = blkdev_aio_write, .mmap = generic_file_mmap, .fsync = blkdev_fsync, .unlocked_ioctl = block_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_blkdev_ioctl, #endif .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, }; void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev) { inode->i_mode = mode; if (S_ISCHR(mode)) { inode->i_fop = &def_chr_fops; inode->i_rdev = rdev; } else if (S_ISBLK(mode)) { inode->i_fop = &def_blk_fops; inode->i_rdev = rdev; } else if (S_ISFIFO(mode)) inode->i_fop = &def_fifo_fops; else if (S_ISSOCK(mode)) inode->i_fop = &bad_sock_fops; else printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for" " inode %s:%lu\n", mode, inode->i_sb->s_id, inode->i_ino); } EXPORT_SYMBOL(init_special_inode);
補足
パーティションのスペシャルファイルのinodeのsizeは、パーティションのサイズで、read()でそのサイズを超えていればエラーとなり、ベースのデバイスファイルでなくパーティションのスペシャルファイルを介して、他のパーティション領域を参照することはできません。ブロックデバイスにバルク的なやり取りで巨大なデータを保存する等、直接デバイススペシャルファイルで読み書きすることで、inodeを介する必要はなく、またかかるinode/データブロック等も必要ないため、高速でデバイス領域全体を実領域として使うことができます。またユーザランドサイドで単純なストリームのバルクデータ管理機能を実装することで、セキュリテをかねた保存デバイスシステムの構築も可能です。