上图展示了进程打开文件使用的内核数据结构,所以要打开文件,就要构造上图中的关系。
int sys_open(const char *filename,int flag,int mode)
{
struct m_inode *inode;
struct file *f;
int i,fd;
mode&=0777&~current->umask;
//在filp数组中寻找“空闲位置”
for(fd=0;fd<NR_OPEN;fd++)
if(!current->filp[fd])
break;
if(fd>=NR_OPEN)
return -EINVAL;
current->close_on_exec&=~(1<<fd);
//在file_table中寻找“空闲位置”
f=0+file_table;
for(i=0;i<NR_FILE,i++,f++)
if(!f->f_count)
break;
if(i>=NR_FILE)
return -EINVAL;
//此时我们让进程对应文件句柄fd的文件结构指针指向搜索到的文件结构,并令文件引用计数加1,然后调用open_namei()函数执行打开操作,如果返回值小于0,则说明出错,于是释放刚刚申请的文件结构,返回出错码i;若文件打开成功,则inode是打开文件的i节点指针。
(current->filp[fd]=f)->count++;
if((i=open_namei(filename,flag,mode,&inode))<0)
{
current->filp[fd]=NULL;
f->f_count=0;
return i;
}
....
f->f_mode=inode->i_mode;
f->f_flags=flag;
f->f_inode=inode;
f->f_pos=0;
return fd;
}
注解:f->f_inode=inode,这步骤完成之后,本文上来的那个图的关系就全连起来了。
sys_open核心就在于open_namei()函数,这个函数会牵扯到整个文件系统,理解这个函数就对0.12内核的文件系统有一个整体的认识,下面来重点分析open_namei()函数,这会涉及到dir_namei()、get_dir()、find_entry()、new_inode()、add_entry()、follow_link()、bmap()等函数,我们需要首先说明这些函数的功能。
1.static struct m_inode* get_dir(const char *pathname,struct m_inode *inode)
功能:根据函数给定的路径名进行搜索,直到达到最顶端目录(/etc/bin/vi--bin/是最顶端目录喔!)
参数:
pathname--路径名
inode--指定其实目录的i节点
返回值:目录或者文件的i节点指针
2.static struct m_inode* dir_namei(const char *pathname,int *namelen,const char **name,struct m_inode *base)
功能:返回指定目录名的i节点指针,以及在最顶层目录的名称
参数:
pathname--目录路径名(in)
namelen--路径名长度(out)
name--返回的最顶层目录名(out)
base--搜索其实目录的i节点(in)
返回:指定目录名最顶层目录的i节点指针和最顶层目录名和长度,出错返回NULL
static struct m_inode* dir_namei(const char *pathname,int *namelen,const char **name,struct m_inode *base)
{
char c;
const char *basename;
struct m_inode *dir;
if(!dir=get_dir(pathname,base))
return NULL;
basename=pathname;
while(c=get_fs_byte(pathname++)
if(c=='/')
basename=pathname;
*namelen=pathname-basename-1;
*name=basename;
return dir;
}
比如给定目录/abc/def/gh
get_dir返回def目录对应的内存i节点
dir_namei,不仅完成get_dir功能,而且返回gh和长度2
3.static struct buffer_head* find_entry(struct m_inode **dir,const char *name,int namelen,struct dir_entry **res_dir)
功能:查找指定目录和文件名的目录项
参数:*dir--指定目录i节点指针;name--文件名;namelen--文件名长度
返回:成功则返回高速缓冲区指针,并在res_dir处返回的目录项结构指针。失败则返回空指针NULL。
4.open_namei函数
int open_namei(const char *pathname,int flag,int mode,struct m_inode **res_inode)
{
const char *basename;
int inr,dev,namelen;
struct m_inode *dir,*inode;
struct buffer_head *bh;
struct dir_entry *de;
...
if(!(dir=dir_namei(pathname,&namelen,&basename,NULL)))
return -ENOENT
if(!namelen)
{
if(!(flag & (O_ACCMODE|O_CREATE|O_TRUNC)))
{
*res_inode=dir;
return 0;
}
iput(dir);
return -EISDIR;
}
bh=find_entry(&dir,basename,namelen,&de);
if(!bh)
{
//没有对应的目录项,很有可能是创建文件...
}
//如果bh!=NULL
inr=de->inode;
dev=dir->i_dev;
brelse(bh);
if(flag & O_EXCL)
{
iput(dir);
return -EEXIST;
}
//读取目录项的i节点内容
if(!(inode=follow_link(dir,iget(dev,inr))))
return -EACCES;
...
inode->i_atime=CURRENT_TIME;
if(flag & o_TRUNC)
truncate(inode);
*res_inode=inode;
return;
}