linux open系统调用完全剖析 下载本文

内容发布更新时间 : 2024/6/28 14:27:02星期一 下面是文章的全部内容请认真阅读。

......

.lookup = ext3_lookup, ...... };

file_system_type register_filesystem vfs_kern_mount

此函数先从缓存中查找对应的 inode,如果没有则新分配一个 struct dentry 结构,然后调用 parent->d_inode->i_op->lookup 即调用了 ext3_lookup() 函数来查找 inode。

Lookup函数还会调用到一个重要的函数 inode = ext3_iget(dir->i_sb, ino); */ result = dir->i_op->lookup(dir, dentry, nd); }

//Lookup函数还会调用到一个重要的函数 inode = ext3_iget(dir->i_sb, ino);该函数比较复杂,主要是完成inode的赋值,然后根据inode的信息来判断该文件到底是什么类型的文件,然后会调用到底层向对应的操作该文件的方法对其进行操作 struct inode *ext3_iget(struct super_block *sb, unsigned long ino) {

// 注意:这里的 __ext3_get_inode_loc 是产生 // 一个磁盘 I/O 从磁盘读取真正的 struct inode // 来填充 in core 类型的。注意这个函数使用的 // 第三个参数,为 0 的情况下产生 I/O 从磁盘 // 读取,否则从 buffer_head 磁盘缓存中查找。 ret = __ext3_get_inode_loc(inode, &iloc, 0);

// 如果是普通文件的话,则使用 ext3_file_xxx 函数集 // 注意:在使用 ext3_file_operations 函数集时,它的

// open 函数对应的是 generic_file_open() 函数,而这个函数 // 除了判断大文件是否合法外,几乎就是一个空函数,也就是说 // 如果是在一个 ext3 文件系统上,open 操作其实没有任何具体 // 动作,是无意义的。为什么会这样呢?在后面介绍文件系统时 // 会讲到。 if (S_ISREG(inode->i_mode)) {//是普通文件,使用普通文件的操作方法 inode->i_op = &ext3_file_inode_operations; inode->i_fop = &ext3_file_operations; ext3_set_aops(inode); } else if (S_ISDIR(inode->i_mode)) {// 如果是目录的话,则要区别对待,使用 ext3_dir_xxx 函数集 inode->i_op = &ext3_dir_inode_operations; inode->i_fop = &ext3_dir_operations; } else if (S_ISLNK(inode->i_mode)) { if (ext3_inode_is_fast_symlink(inode)) {// 如果是连接的话,也要区别对待,使用 ext3_symlink_xxx 函数集

inode->i_op = &ext3_fast_symlink_inode_operations; nd_terminate_link(ei->i_data, inode->i_size, sizeof(ei->i_data) - 1); } else { inode->i_op = &ext3_symlink_inode_operations; ext3_set_aops(inode); } } else { // 如果以上三种情况都排除了,那么我们则认为他是一个设备驱动 // 注意:这里的仅对 inode->i_op 函数集进行了直接赋值。对于 // inode->i_fop 函数集使用的是 init_special_inode() 函数 // 进行的赋值 inode->i_op = &ext3_special_inode_operations;//对inode的操作方法进行赋值 if (raw_inode->i_block[0]) init_special_inode(inode, inode->i_mode,//使用init_special_inode() 对文件操作方法进行进行赋值 old_decode_dev(le32_to_cpu(raw_inode->i_block[0]))); else

//例如是字符设备,在此处根据inode的信息,找到struct cdev对应的是我们自己实现的字符设备驱动

//在此处会将我们实现的字符设备驱动操作方法,赋值给文件结构体的操作方法。 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所对应的设备类型进行一个进一步的分析,然后使用到相对应不同的设备的操作方法进行操作。 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))//FIFO设备 inode->i_fop = &def_fifo_fops; else if (S_ISSOCK(mode))//网络设备 inode->i_fop = &bad_sock_fops; else printk(KERN_DEBUG \ mode); }

/*

这个函数根据 struct nameidata 结构返回一个 struct file。可以看到 struct file 是在使用了 __dentry_open() 函数后被填充的,且使用的第

一个参数是 nameidata->dentry,这也是为什么我们要获得 struct nameidata 的一个主要原因,其目的就是为了得到 struct dentry 结构。

在此时,已经将inode和dentry的结构体创建好,并且绑定好,对于不同类型的设备 在上一步已经将其对应的默认文件操作方法赋值*/

struct file *nameidata_to_filp(struct nameidata *nd, int flags) { struct file *filp; /* Pick up the filp from the open intent */ filp = nd->intent.open.file; /* Has the filesystem initialised the file for us? */ if (filp->f_path.dentry == NULL)

// 这个函数主要就是填充一个 struct file 结构,通过这段 // 代码也可以看到,一个 struct file 是动态分配的。 filp = __dentry_open(nd->path.dentry, nd->path.mnt, flags, filp, NULL, cred); return filp; }

static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags, struct file *f, int (*open)(struct inode *, struct file *), const struct cred *cred) { f->f_op = fops_get(inode->i_fop); file_move(f, &inode->i_sb->s_files); if (!open && f->f_op) open = f->f_op->open;// 此处调用 def_chr_fops里的open函数,即chrdev_open if (open) { error = open(inode, f); if (error) goto cleanup_all; } }