本篇内容介绍了“基于linux的open原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
我们操作一个文件之前都需要先open一下。我们看看open在虚拟文件系统中大致的执行过程。不会分析具体的过程。主要分析一下虚拟文件系统的实现原理。
asmlinkage int sys_open(const char * filename,int flags,int mode){ char * tmp; int error; error = getname(filename, &tmp); if (error) return error; error = do_open(tmp,flags,mode); putname(tmp); return error;}
AI代码助手复制代码
下面是do_open的代码。
// 打开一个文件int do_open(const char * filename,int flags,int mode){ struct inode * inode; struct file * f; int flag,error,fd; // 找到一个可用的文件描述符 for(fd=0; fd<NR_OPEN && fd<current->rlim[RLIMIT_NOFILE].rlim_cur; fd++) // 还没被使用则找到可用的 if (!current->files->fd[fd]) break; // 找不到可用的 if (fd>=NR_OPEN || fd>=current->rlim[RLIMIT_NOFILE].rlim_cur) return -EMFILE; // 清除close_on_exec标记位 FD_CLR(fd,¤t->files->close_on_exec); // 获取一个可用的file结构体 f = get_empty_filp(); if (!f) return -ENFILE; // 建立fd到file结构体的映射 current->files->fd[fd] = f; f->f_flags = flag = flags; f->f_mode = (flag+1) & O_ACCMODE; if (f->f_mode) flag++; if (flag & (O_TRUNC | O_CREAT)) flag |= 2; // 找到文件对应的inode节点,放到inode变量中 error = open_namei(filename,flag,mode,&inode,NULL); if (!error && (f->f_mode & 2)) { error = get_write_access(inode); if (error) iput(inode); } if (error) { current->files->fd[fd]=NULL; f->f_count--; return error; } // 建立file结构体和inode的映射关系 f->f_inode = inode; // 初始化文件偏移 f->f_pos = 0; f->f_reada = 0; f->f_op = NULL; // 赋值操作file结构体的函数集 if (inode->i_op) f->f_op = inode->i_op->default_file_ops; if (f->f_op && f->f_op->open) { // 调用底层文件系统的open函数 error = f->f_op->open(inode,f); if (error) { if (f->f_mode & 2) put_write_access(inode); iput(inode); f->f_count--; current->files->fd[fd]=NULL; return error; } } f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); return (fd);}
AI代码助手复制代码
主要工作是通过open_namei找到文件对应的inode节点,然后执行底层的open。最后建立fd到file到inode的关系。在虚拟文件系统中,超级块、inode、目录、文件都是一个抽象的概念,具体的操作函数由各个文件系统实现。所以我们首先找到一个结构体,然后根据实际的操作值调用具体文件系统的操作函数就行。
所以我们先看open_namei,再看具体的open函数。
下面是open_namei的核心代码。
// 找出最后一级目录的inode error = dir_namei(pathname,&namelen,&basename,base,&dir);// 在最后一级目录下找出某文件对应的inode error = lookup(dir,basename,namelen,&inode);
AI代码助手复制代码
大致思路就是先找到最后一级目录的inode,然后在最后一级目录下找某个文件。我们看看怎么找到最后一级目录对应的inode。
// 找出pathname中最后一级目录对应的inodestatic int dir_namei(const char * pathname, int * namelen, const char ** name, struct inode * base, struct inode ** res_inode){ char c; const char * thisname; int len,error; struct inode * inode; *res_inode = NULL; // 没有传base则从进程的当前工作目录开始找 if (!base) { base = current->fs->pwd; base->i_count++; } // 绝对路径,则从根目录开始找 if ((c = *pathname) == '/') { iput(base); base = current->fs->root; pathname++; base->i_count++; } // 找出pathname最后一级目录对应的inode节点 while (1) { thisname = pathname; // 以/分割路径部分,遇到/则得到某级路径名称,thisname指向当前路径首地址,len代表该部分路径对应的长度 for(len=0;(c = *(pathname++))&&(c != '/');len++) /* nothing */ ; // c为空说明到最后一个字符,即路径结束,不找最后一级的文件或者目录了 if (!c) break; base->i_count++; // 在base目录下查找某个文件 error = lookup(base,thisname,len,&inode); if (error) { iput(base); return error; } // 设置base为inode,继续找 error = follow_link(base,inode,0,0,&base); if (error) return error; } if (!base->i_op || !base->i_op->lookup) { iput(base); return -ENOTDIR; } *name = thisname; *namelen = len; *res_inode = base; return 0;}
AI代码助手复制代码
就是通过以/分割字符串,在开始的目录下,即根目录或者给定的目录。找到下一级目录的inode,不断地迭代就能找到最后一级目录的inode了。我们发现在查找最后一级目录的inode和在某个目录下找某个文件的inode都用到lookup函数。我们看看他的实现。核心代码。
return dir->i_op->lookup(dir,name,len,result);
以ext文件系统为例。看看lookup的实现。
int ext_lookup(struct inode * dir,const char * name, int len, struct inode ** result){ int ino; struct ext_dir_entry * de; struct buffer_head * bh; *result = NULL; if (!dir) return -ENOENT; if (!S_ISDIR(dir->i_mode)) { iput(dir); return -ENOENT; } if (!(bh = ext_find_entry(dir,name,len,&de,NULL,NULL))) { iput(dir); return -ENOENT; } ino = de->inode; brelse(bh); if (!(*result = iget(dir->i_sb,ino))) { iput(dir); return -EACCES; } iput(dir); return 0;}static struct buffer_head * ext_find_entry(struct inode * dir, const char * name, int namelen, struct ext_dir_entry ** res_dir, struct ext_dir_entry ** prev_dir, struct ext_dir_entry ** next_dir){ long offset; struct buffer_head * bh; struct ext_dir_entry * de; *res_dir = NULL; if (!dir) return NULL;#ifdef NO_TRUNCATE if (namelen > EXT_NAME_LEN) return NULL;#else if (namelen > EXT_NAME_LEN) namelen = EXT_NAME_LEN;#endif // 读取目录的文件内容,然后比较 bh = ext_bread(dir,0,0); if (!bh) return NULL; if (prev_dir) *prev_dir = NULL; if (next_dir) *next_dir = NULL; offset = 0; de = (struct ext_dir_entry *) bh->b_data; while (offset < dir->i_size) { if ((char *)de >= BLOCK_SIZE+bh->b_data) { brelse(bh); bh = NULL; bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,0); if (!bh) continue; de = (struct ext_dir_entry *) bh->b_data; if (prev_dir) *prev_dir = NULL; } if (de->rec_len < 8 || de->rec_len % 8 != 0 ||de->rec_len < de->name_len + 8 ||(((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) { printk ("ext_find_entry: bad dir entry\n"); printk ("dev=%d, dir=%ld, offset=%ld, rec_len=%d, name_len=%d\n", dir->i_dev, dir->i_ino, offset, de->rec_len, de->name_len); de = (struct ext_dir_entry *) (bh->b_data+BLOCK_SIZE); offset = ((offset / BLOCK_SIZE) + 1) * BLOCK_SIZE; continue;/* brelse (bh); return NULL; */ } // 是否找到了想要的文件 if (ext_match(namelen,name,de)) { *res_dir = de; if (next_dir) if (offset + de->rec_len < dir->i_size &&((char *)de) + de->rec_len < BLOCK_SIZE+bh->b_data) *next_dir = (struct ext_dir_entry *) ((char *) de + de->rec_len); else *next_dir = NULL; return bh; } offset += de->rec_len; if (prev_dir) *prev_dir = de; de = (struct ext_dir_entry *) ((char *) de + de->rec_len); } brelse(bh); return NULL;}
AI代码助手复制代码
代码比较多,可以不用细究,主要是把目录的文件内容从硬盘读取进来,然后遍历每一个文件名是否和要找的一致就行。
通过上面的分析我们已经找到了一个文件对应的inode节点了。一般文件系统没有实现open函数。所以直接返回inode,建立fd到file到inode的关系即可。后面对文件的操作也是通过inode节点的操作函数集进行。
“基于linux的open原理是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/4217331/blog/4379407