基于linux的open原理是什么
本篇内容介绍了“基于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;
}
下面是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; fdrlim[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);
}
主要工作是通过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);
大致思路就是先找到最后一级目录的inode,然后在最后一级目录下找某个文件。我们看看怎么找到最后一级目录对应的inode。
// 找出pathname中最后一级目录对应的inode
static 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;
}
就是通过以/分割字符串,在开始的目录下,即根目录或者给定的目录。找到下一级目录的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;
}
代码比较多,可以不用细究,主要是把目录的文件内容从硬盘读取进来,然后遍历每一个文件名是否和要找的一致就行。
通过上面的分析我们已经找到了一个文件对应的inode节点了。一般文件系统没有实现open函数。所以直接返回inode,建立fd到file到inode的关系即可。后面对文件的操作也是通过inode节点的操作函数集进行。
“基于linux的open原理是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注创新互联网站,小编将为大家输出更多高质量的实用文章!
本文题目:基于linux的open原理是什么
标题链接:http://azwzsj.com/article/jppdpc.html