kern_mount
socket/pipe/shmem等は、vfs下で実装されており、socket()/shmget()/pipe()の返り値は、FILE IDその物です。従ってこの様な実装では、掛かるファイルシステムがマウントされてなければなりません。
fileのベースはdentry、dentryのベースはinode、inodeのベースはファイルシステムのスーパブロックです。このスーパブロックを管理しているのがvfsmntだからです。
通常のmountは、vfsmntを取得後マウント先にバインドしますが、これらのmountはその必要がありません。この処理を行うのがkern_mount()です。
mountコマンドは、sys_mount()からdo_mount()をコールし、マウントフラグにより do_remount()/do_loopback()/do_change_type()/do_move_mount()/ do_new_mount()がコールされます。新規マウントならdo_new_mount()がコールされ、do_kern_mount()でファイルシステムのvfsmounを取得した後、do_add_mount()でマウント先にバインドします。do_kern_mount()はvfs_kern_mount()でvfsmountを取得しているだけです。
kern_mount_data()も、mountコマンドと同じようにvfs_kern_mount()をコールします。ただし、ファイルシステムをユーザ空間にエックスポートする必要がないため、mntフラグをMS_KERNMOUNTとします。これはmountコマンドでファイルシステムをmontできません。
dentry取得もinode取得と同じです。通常は親dentry(ディレクトリ)をdentry->d_parentとし、親兄弟リストに追加しますが、kern_mount()は、パス走査されないためその必要がありません。
inodeファイルオペレーションコールバックは無視して、fileを取得時に直接ファイルオペレーションコールバックを設定してるケースもあるようです。カーネル内の処理ですから可能なわけですが・・・。
fileのベースはdentry、dentryのベースはinode、inodeのベースはファイルシステムのスーパブロックです。このスーパブロックを管理しているのがvfsmntだからです。
通常のmountは、vfsmntを取得後マウント先にバインドしますが、これらのmountはその必要がありません。この処理を行うのがkern_mount()です。
mountコマンドは、sys_mount()からdo_mount()をコールし、マウントフラグにより do_remount()/do_loopback()/do_change_type()/do_move_mount()/ do_new_mount()がコールされます。新規マウントならdo_new_mount()がコールされ、do_kern_mount()でファイルシステムのvfsmounを取得した後、do_add_mount()でマウント先にバインドします。do_kern_mount()はvfs_kern_mount()でvfsmountを取得しているだけです。
static int do_new_mount(struct path *path, char *type, int flags, int mnt_flags, char *name, void *data) { struct vfsmount *mnt; int err; if (!type) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; mnt = do_kern_mount(type, flags, name, data); if (IS_ERR(mnt)) return PTR_ERR(mnt); err = do_add_mount(real_mount(mnt), path, mnt_flags); if (err) mntput(mnt); return err; }kern_mount()はkern_mount_data()をdata=NULLでコールします。dataはファイルシステム毎のmountコールバック関数のパラメータです。
kern_mount_data()も、mountコマンドと同じようにvfs_kern_mount()をコールします。ただし、ファイルシステムをユーザ空間にエックスポートする必要がないため、mntフラグをMS_KERNMOUNTとします。これはmountコマンドでファイルシステムをmontできません。
#define kern_mount(type) kern_mount_data(type, NULL) static struct vfsmount * do_kern_mount(const char *fstype, int flags, const char *name, void *data) { struct file_system_type *type = get_fs_type(fstype); struct vfsmount *mnt; if (!type) return ERR_PTR(-ENODEV); mnt = vfs_kern_mount(type, flags, name, data); if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) && !mnt->mnt_sb->s_subtype) mnt = fs_set_subtype(mnt, fstype); put_filesystem(type); return mnt; } struct vfsmount *kern_mount_data(struct file_system_type *type, void *data) { struct vfsmount *mnt; mnt = vfs_kern_mount(type, MS_KERNMOUNT, type->name, data); if (!IS_ERR(mnt)) { mnt_make_longterm(mnt); } return mnt; }inode取得は、通常はnew_inode()がコールされ、new_inode_pseudo()でinodeを取得した後、スーパブロックにリストしますが、kern_mount()のinode取得は、直接new_inode_pseudo()がコールしinodeを取得するのみです。たぶんinodeキャッシュをスワップ対象でないからでは思います。
struct inode *new_inode(struct super_block *sb) { struct inode *inode; spin_lock_prefetch(&inode_sb_list_lock); inode = new_inode_pseudo(sb); if (inode) inode_sb_list_add(inode); return inode; } struct inode *new_inode_pseudo(struct super_block *sb) { struct inode *inode = alloc_inode(sb); if (inode) { spin_lock(&inode->i_lock); inode->i_state = 0; spin_unlock(&inode->i_lock); INIT_LIST_HEAD(&inode->i_sb_list); } return inode; }alloc_inode()でinodeを取得します。struct inodeはvfs下のinodeで、ファイルシステムに関係なく同じものです。実inodeはファイルシステムに応じて違っています。故にスーパブロックalloc_inode()コールバックで実inodeを取得します。この実inodeのメンバーのvfs_indeに、vfsのinodeを有しています。vfs inodeからcontein_of()マクロで実inodeを容易に取得できます。
static struct inode *alloc_inode(struct super_block *sb) { struct inode *inode; if (sb->s_op->alloc_inode) inode = sb->s_op->alloc_inode(sb); else inode = kmem_cache_alloc(inode_cachep, GFP_KERNEL); if (!inode) return NULL; if (unlikely(inode_init_always(sb, inode))) { if (inode->i_sb->s_op->destroy_inode) inode->i_sb->s_op->destroy_inode(inode); else kmem_cache_free(inode_cachep, inode); return NULL; } return inode; }dentry取得は、通常d_alloc()がコールされ、kern_mount()のdentry取得は、d_alloc_pseudo()をコールします。
dentry取得もinode取得と同じです。通常は親dentry(ディレクトリ)をdentry->d_parentとし、親兄弟リストに追加しますが、kern_mount()は、パス走査されないためその必要がありません。
struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) { struct dentry *dentry = __d_alloc(parent->d_sb, name); if (!dentry) return NULL; spin_lock(&parent->d_lock); __dget_dlock(parent); dentry->d_parent = parent; list_add(&dentry->d_u.d_child, &parent->d_subdirs); spin_unlock(&parent->d_lock); return dentry; } struct dentry *d_alloc_pseudo(struct super_block *sb, const struct qstr *name) { struct dentry *dentry = __d_alloc(sb, name); if (dentry) dentry->d_flags |= DCACHE_DISCONNECTED; return dentry; }
補足
socket()/shmget()/pipe()をコールすると、かかるpseudo(見せ掛け)inode/dentryを取得し、struct fileにこのdentryを設定すると同時に、inodeのファイルオペレーションコールバックをfileに設定した後、カレントプロセスの構造体から空きのファイルIDにこのfileを設定することで、FILE IDでかかる操作が可能となります。FILE IDそのものですから、リモート端末のようなsshなんかは、そのsockeの返り値を標準入力/出力にリダイレクトすればいいわけです。(たぶん)inodeファイルオペレーションコールバックは無視して、fileを取得時に直接ファイルオペレーションコールバックを設定してるケースもあるようです。カーネル内の処理ですから可能なわけですが・・・。