Linux呼び出し駆動open関数プロセスの詳細
12577 ワード
まず大まかな流れを述べて、更に流れの詳細を補充して、本ブログは主にVFSファイルシステムを核心として紹介して、実際のファイルシステムext 2あるいはsysfsファイルシステムに対して、本ブログの範囲を超えます
*Linuxファイルを開くには、2つのステップが必要です.(1)ファイルが見つかりました (2)ファイル を開く
Cライブラリがopen関数のシステムエントリを呼び出すときsys_Open関数
//ファイルを探す過程は以下の通り def_が見つかりましたchr_fops構造、path_lookup_Open関数は、デバイスファイルに対応するファイルシステムのデフォルトopen関数も得る.const struct file_operations def_chr_fops = { .open = chrdev_open, };
またdo_を見てfilp_Open関数呼び出しの別の関数nameidata_to_filp
検索プロセスを詳細に分析します.
C関数open("/home/book/test.txt")Linux 2.6.26ソースコードの例
//ndはdo_filp_Open()関数宣言の構造体インスタンス
/*ルートディレクトリ'/'はext 2ファイルシステムのマウントポイントであるため、ext 2ファイルシステムのlookup関数*/
//ext 2ファイルシステムのinode操作関数は以下の通りです.
//dirは親ディレクトリのinode構造で、dentryは検索するnameに基づいて定義されたdentry構造です.
//この関数の実行が完了すると、現在のnameに対応する操作関数を検索し、dentryをnd構造に関連付けると、nd構造で得られた関数を呼び出すことができます.
//ファイルを開くと、nd構造で対応するopen関数を得てopen関数を呼び出す.
*Linuxファイルを開くには、2つのステップが必要です.
Cライブラリがopen関数のシステムエントリを呼び出すときsys_Open関数
asmlinkage long sys_open(const char __user *filename, int flags, int mode)
{
long ret;
// 32
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}
long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
//
char *tmp = getname(filename);
int fd = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
// ,
fd = get_unused_fd_flags(flags);
if (fd >= 0) {
//
struct file *f = do_filp_open(dfd, tmp, flags, mode);
if (IS_ERR(f)) {
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fsnotify_open(f->f_path.dentry);
fd_install(fd, f);
}
}
putname(tmp);
}
return fd;
}
struct file *do_filp_open(int dfd, const char *pathname,
int open_flag, int mode)
{
……
/*
* The simplest case - just a plain lookup.
*/
if (!(flag & O_CREAT)) {
//
error = path_lookup_open(dfd, pathname, lookup_flags(flag),
&nd, flag);
if (error)
return ERR_PTR(error);
goto ok;
}
……
ok:
//
filp = nameidata_to_filp(&nd, open_flag);
……
}
//ファイルを探す過程は以下の通り
path_lookup_open
->__path_lookup_intent_open
->do_path_lookup
->path_walk
->link_path_walk
->__link_path_walk
->do_lookup
->real_lookup
->result = dir->i_op->lookup(dir, dentry, nd); // lookup
static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
{
……
if (ino) {
inode = ext2_iget(dir->i_sb, ino);
if (IS_ERR(inode))
return ERR_CAST(inode);
}
……
}
struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
{
……
if (S_ISREG(inode->i_mode)) {
……
} else if (S_ISDIR(inode->i_mode)) {
……
} else if (S_ISLNK(inode->i_mode)) {
……
} else {
……
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])));
}
……
}
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)
",
mode);
}
またdo_を見てfilp_Open関数呼び出しの別の関数nameidata_to_filp
nameidata_to_filp
->__dentry_open
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
int flags, struct file *f,
int (*open)(struct inode *, struct file *))
{
……
f->f_op = fops_get(inode->i_fop);
……
if (!open && f->f_op)
open = f->f_op->open;
if (open) {
error = open(inode, f); // open , open
if (error)
goto cleanup_all;
}
……
}
static int chrdev_open(struct inode *inode, struct file *filp)
{
……
// cdev_map kobject
kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
// kobject cdev
// cdev file_operation
new = container_of(kobj, struct cdev, kobj);
……
if (filp->f_op->open) {
lock_kernel();
// open
ret = filp->f_op->open(inode,filp);
unlock_kernel();
}
……
}
検索プロセスを詳細に分析します.
C関数open("/home/book/test.txt")Linux 2.6.26ソースコードの例
//ndはdo_filp_Open()関数宣言の構造体インスタンス
static int __path_lookup_intent_open(int dfd, const char *name,
unsigned int lookup_flags, struct nameidata *nd,
int open_flags, int create_mode)
{
// slab , filp_cachep file , slab ,
struct file *filp = get_empty_filp();
int err;
if (filp == NULL)
return -ENFILE;
nd->intent.open.file = filp;
nd->intent.open.flags = open_flags;
nd->intent.open.create_mode = create_mode;
err = do_path_lookup(dfd, name, lookup_flags|LOOKUP_OPEN, nd);
……
return err;
}
static int do_path_lookup(int dfd, const char *name,
unsigned int flags, struct nameidata *nd)
{
int retval = 0;
int fput_needed;
struct file *file;
struct fs_struct *fs = current->fs;
nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags;
nd->depth = 0;
// /home/book/test if . ,
if (*name=='/') {
read_lock(&fs->lock);
if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) {
nd->path = fs->altroot;
path_get(&fs->altroot);
read_unlock(&fs->lock);
if (__emul_lookup_dentry(name,nd))
goto out; /* found in altroot */
read_lock(&fs->lock);
}
nd->path = fs->root; // , '/',
path_get(&fs->root);
read_unlock(&fs->lock);
} else if (dfd == AT_FDCWD) {
……
} else {
……
}
retval = path_walk(name, nd);
……
return retval;
}
static int __link_path_walk(const char *name, struct nameidata *nd)
{
struct path next;
struct inode *inode;
int err;
unsigned int lookup_flags = nd->flags;
// n '/' , open("//////home/book/test.c", O_RDONLY)
// name "home/book/test.c"
while (*name=='/')
name++;
if (!*name)
goto return_reval;
// '/' inode
inode = nd->path.dentry->d_inode;
if (nd->depth)
lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
/* At this point we know we have a real path component. */
for(;;) {
unsigned long hash;
struct qstr this;
unsigned int c;
nd->flags |= LOOKUP_CONTINUE;
// ,
err = exec_permission_lite(inode, nd);
if (err == -EAGAIN)
err = vfs_permission(nd, MAY_EXEC);
if (err)
break;
// this name , this c++ this .
this.name = name;
c = *(const unsigned char *)name; // name c.
hash = init_name_hash(); //hash 0.
// while '/' , .
do {
name++;
hash = partial_name_hash(c, hash); // , .
c = *(const unsigned char *)name;
} while (c && (c != '/'));
// , "home" 4, "book" , "test.c"
this.len = name - (const char *) this.name;
this.hash = end_name_hash(hash);
/* remove trailing slashes? */
// , , last_component last_with_slashes
if (!c) // , "test.c"
goto last_component;
while (*++name == '/'); // n '/' , open("/home/book/////test.c",O_RDONLY)
if (!*name)// '/'
goto last_with_slashes;
……
/* This does the actual lookups.. */
/* this.name dentry */
err = do_lookup(nd, &this, &next);
if (err)
break;
err = -ENOENT;
inode = next.dentry->d_inode;
if (!inode)
goto out_dput;
err = -ENOTDIR;
if (!inode->i_op)
goto out_dput;
if (inode->i_op->follow_link) {
err = do_follow_link(&next, nd);
if (err)
goto return_err;
err = -ENOENT;
inode = nd->path.dentry->d_inode;
if (!inode)
break;
err = -ENOTDIR;
if (!inode->i_op)
break;
} else
path_to_nameidata(&next, nd); // dentry nd
err = -ENOTDIR;
if (!inode->i_op->lookup)
break;
continue;
/* here ends the main loop */
……
}
path_put(&nd->path);
……
}
static int do_lookup(struct nameidata *nd, struct qstr *name,
struct path *path)
{
// nd->path name
struct vfsmount *mnt = nd->path.mnt;
// dentry name dentry , ,
struct dentry *dentry = __d_lookup(nd->path.dentry, name);
if (!dentry)
goto need_lookup;
if (dentry->d_op && dentry->d_op->d_revalidate)
goto need_revalidate;
need_lookup:
dentry = real_lookup(nd->path.dentry, name, nd);
if (IS_ERR(dentry))
goto fail;
goto done;
}
static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
{
struct dentry * result;
struct inode *dir = parent->d_inode;
……
// ,
result = d_lookup(parent, name);
if (!result) {
// dentry . name , dentry dentry
// lookup dentry dentry_hashtable .
struct dentry * dentry = d_alloc(parent, name);
result = ERR_PTR(-ENOMEM);
if (dentry) {
// inode lookup .
result = dir->i_op->lookup(dir, dentry, nd);
if (result)
dput(dentry);
else
result = dentry;
}
mutex_unlock(&dir->i_mutex);
return result;
}
……
return result;
}
/*ルートディレクトリ'/'はext 2ファイルシステムのマウントポイントであるため、ext 2ファイルシステムのlookup関数*/
//ext 2ファイルシステムのinode操作関数は以下の通りです.
const struct inode_operations ext2_dir_inode_operations = {
.create = ext2_create,
.lookup = ext2_lookup,
.link = ext2_link,
.unlink = ext2_unlink,
.symlink = ext2_symlink,
.mkdir = ext2_mkdir,
.rmdir = ext2_rmdir,
.mknod = ext2_mknod,
.rename = ext2_rename,
#ifdef CONFIG_EXT2_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
.removexattr = generic_removexattr,
#endif
.setattr = ext2_setattr,
.permission = ext2_permission,
};
//dirは親ディレクトリのinode構造で、dentryは検索するnameに基づいて定義されたdentry構造です.
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);
// ext2 ,dentry inode
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);
}
// inode dentry
return d_splice_alias(inode, dentry);
}
//この関数の実行が完了すると、現在のnameに対応する操作関数を検索し、dentryをnd構造に関連付けると、nd構造で得られた関数を呼び出すことができます.
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;
// ext2 ino inode
inode = iget_locked(sb, ino);
//
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,
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])));
}
}
//ファイルを開くと、nd構造で対応するopen関数を得てopen関数を呼び出す.