デバイスファイルのinodeコールバック関数
デバイスファイルのオープンは、通常のファイルと同じように、vfs下sys_openがコールされ、指定されるファイルのdentryをルートから下って、目的とするファイルのdentryを取得します。dentryを辿っていく祭、dentryキャッシュを辿るのですが、キャッシュにない場合、新規にdentryを確保し、同時にinodeを取得します。inode取得はデバイス依存のinodeを実際のデバイスから取得します。
デバイスinodeには、そのinodeがデバイスファイルかそうでないかのフラグを有しています。このフラグによりカーネルはコールバック関数を、デバイス用のコールバック関数に置き換えることで、VFS下でデバイスファイルの読み書き込みを通常のファイルの読み書き込みとして操作できるようにしています。
システムコールからdo_sys_open関数がコールされます。オープンの処理は目的のファイルに対するfile構造対を作成することにあります。(file構造体を作成するにはdentry,inodeオブジェクトが必要です。)デバイスファイルのオープンも同じです。
path_lookup_open関数からパス名を分解したディレクトリ名でlink_path_walk関数へと、パスに従ってdentryを下っていきます。そしてdentryがキャッシュにないとreal_lookup関数でスラブからdentryをアロケートし、上のパスのdentryのinodeオペレーションのlookupコールバック関数をコールする事で、dentry内のinodeに掛かる情報を設定します。
raw_inode->i_modeにはこのinodeがデバイスファイルかどうかのビットが設定されています。それをチェックすることで、通常ファイル、ディレクトリ、リンクおよびデバイスファイルかによってそれぞれのコールバックを設定します。
デバイスinodeには、そのinodeがデバイスファイルかそうでないかのフラグを有しています。このフラグによりカーネルはコールバック関数を、デバイス用のコールバック関数に置き換えることで、VFS下でデバイスファイルの読み書き込みを通常のファイルの読み書き込みとして操作できるようにしています。
システムコールからdo_sys_open関数がコールされます。オープンの処理は目的のファイルに対するfile構造対を作成することにあります。(file構造体を作成するにはdentry,inodeオブジェクトが必要です。)デバイスファイルのオープンも同じです。
path_lookup_open関数からパス名を分解したディレクトリ名でlink_path_walk関数へと、パスに従ってdentryを下っていきます。そしてdentryがキャッシュにないとreal_lookup関数でスラブからdentryをアロケートし、上のパスのdentryのinodeオペレーションのlookupコールバック関数をコールする事で、dentry内のinodeに掛かる情報を設定します。
static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd) { struct dentry * result; struct inode *dir = parent->d_inode; mutex_lock(&dir->i_mutex); result = d_lookup(parent, name); if (!result) { struct dentry *dentry; result = ERR_PTR(-ENOENT); if (IS_DEADDIR(dir)) goto out_unlock; dentry = d_alloc(parent, name); result = ERR_PTR(-ENOMEM); if (dentry) { result = dir->i_op->lookup(dir, dentry, nd); if (result) dput(dentry); else result = dentry; } out_unlock: mutex_unlock(&dir->i_mutex); return result; } mutex_unlock(&dir->i_mutex); if (result->d_op && result->d_op->d_revalidate) { result = do_revalidate(result, nd); if (!result) result = ERR_PTR(-ENOENT); } return result; }ext2ファイルシステムでは、dir->i_op->lookupにext2_lookup関数が設定されています。ここからext2_iget関数をコールします。
static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd) { struct inode * inode; ino_t ino; if (dentry->d_name.len > EXT2_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); ino = ext2_inode_by_name(dir, dentry); inode = NULL; if (ino) { inode = ext2_iget(dir->i_sb, ino); if (IS_ERR(inode)) return ERR_CAST(inode); } return d_splice_alias(inode, dentry); }ext2_iget 関数ではext2_get_inode関数で、実際の物理デバイスからraw_inode = ext2_get_inode(inode->i_sb, ino, &bh)でstruct ext2_inodeとするinodeを取得します。
raw_inode->i_modeにはこのinodeがデバイスファイルかどうかのビットが設定されています。それをチェックすることで、通常ファイル、ディレクトリ、リンクおよびデバイスファイルかによってそれぞれのコールバックを設定します。
struct inode *ext2_iget (struct super_block *sb, unsigned long ino) { struct ext2_inode_info *ei; struct buffer_head * bh; struct ext2_inode *raw_inode; struct inode *inode; long ret = -EIO; int n; inode = iget_locked(sb, ino); if (!inode) return ERR_PTR(-ENOMEM); if (!(inode->i_state & I_NEW)) return inode; ei = EXT2_I(inode); ei->i_block_alloc_info = NULL; raw_inode = ext2_get_inode(inode->i_sb, ino, &bh); inode->i_mode = le16_to_cpu(raw_inode->i_mode); inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low); inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low); if (!(test_opt (inode->i_sb, NO_UID32))) { inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16; inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16; } inode->i_nlink = le16_to_cpu(raw_inode->i_links_count); inode->i_size = le32_to_cpu(raw_inode->i_size); inode->i_atime.tv_sec = (signed)le32_to_cpu(raw_inode->i_atime); : : : if (S_ISREG(inode->i_mode)) { inode->i_op = &ext2_file_inode_operations; if (ext2_use_xip(inode->i_sb)) { inode->i_mapping->a_ops = &ext2_aops_xip; inode->i_fop = &ext2_xip_file_operations; } else if (test_opt(inode->i_sb, NOBH)) { inode->i_mapping->a_ops = &ext2_nobh_aops; inode->i_fop = &ext2_file_operations; } else { inode->i_mapping->a_ops = &ext2_aops; inode->i_fop = &ext2_file_operations; } } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &ext2_dir_inode_operations; inode->i_fop = &ext2_dir_operations; if (test_opt(inode->i_sb, NOBH)) inode->i_mapping->a_ops = &ext2_nobh_aops; else inode->i_mapping->a_ops = &ext2_aops; } else if (S_ISLNK(inode->i_mode)) { if (ext2_inode_is_fast_symlink(inode)) inode->i_op = &ext2_fast_symlink_inode_operations; else { inode->i_op = &ext2_symlink_inode_operations; if (test_opt(inode->i_sb, NOBH)) inode->i_mapping->a_ops = &ext2_nobh_aops; else inode->i_mapping->a_ops = &ext2_aops; } } else { inode->i_op = &ext2_special_inode_operations; if (raw_inode->i_block[0]) init_special_inode(inode, inode->i_mode, old_decode_dev(le32_to_cpu(raw_inode->i_block[0]))); else init_special_inode(inode, inode->i_mode, new_decode_dev(le32_to_cpu(raw_inode->i_block[1]))); } : : }デバイスファイルの場合、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 = generic_file_aio_write_nolock, .mmap = generic_file_mmap, .fsync = block_fsync, .unlocked_ioctl = block_ioctl, .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)\n", mode); }実際にデバイスとの入出力を行うアドレスstruct address_space_operationsは以下のように定義されていて、struct file_operations のopenメンバーに設定されるblkdev_open関数から、 bdev = bd_acquire(inode)とコールすることで、inodeにdef_blk_aopsが設定されるようになっています。
static const struct address_space_operations def_blk_aops = { .readpage = blkdev_readpage, .writepage = blkdev_writepage, .sync_page = block_sync_page, .write_begin = blkdev_write_begin, .write_end = blkdev_write_end, .writepages = generic_writepages, .direct_IO = blkdev_direct_IO, };