Linux呼び出し駆動open関数プロセスの詳細

12577 ワード

まず大まかな流れを述べて、更に流れの詳細を補充して、本ブログは主にVFSファイルシステムを核心として紹介して、実際のファイルシステムext 2あるいはsysfsファイルシステムに対して、本ブログの範囲を超えます
*Linuxファイルを開くには、2つのステップが必要です.
  • (1)ファイルが見つかりました
  • (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); }
  • def_が見つかりましたchr_fops構造、path_lookup_Open関数は、デバイスファイルに対応するファイルシステムのデフォルトopen関数も得る.const struct file_operations def_chr_fops = { .open = chrdev_open, };

  • また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関数を呼び出す.