open.c源代码阅读

来源:互联网 发布:网络摄像机软件下载 编辑:程序博客网 时间:2024/06/08 01:54
就是将open弄明白
open.c的核心就是sys_open()函数 就是找到inode,将inode读出来
当用户空间程序用open系统调用打开一个文件的时候,内核对应的处理是sys_open函数:
fs/open.c
asmlinkage long sys_open(const char __user * filename, int flags, int mode){char * tmp;int fd, error;#if BITS_PER_LONG != 32flags |= O_LARGEFILE;#endiftmp = getname(filename);//从进程地址空间读取该文件的路径名fd = PTR_ERR(tmp);if (!IS_ERR(tmp)) {fd = get_unused_fd();//调用get_unused_fd()在current->files->fd中查找一个空的位置。相应的索引存放在fd局部变量中if (fd >= 0) {//打开文件,将文件名转换成文件结构struct file *f = filp_open(tmp, flags, mode);//调用file_open()函数,传递他的参数为路径名、访问模式标志、以及许可权位掩码error = PTR_ERR(f);if (IS_ERR(f))goto out_error;fd_install(fd, f);}}
对于filp_open()文件打开函数,filp_open()的定义也在open.c文件中
struct file *filp_open(const char * filename, int flags, int mode){ int namei_flags, error; struct nameidata nd; //这个结构存放了查找操作的结果 namei_flags = flags; if ((namei_flags+1) & O_ACCMODE)  namei_flags++; if (namei_flags & O_TRUNC)  namei_flags |= 2; error = open_namei(filename, namei_flags, mode, &nd);//顺着文件名打开操作 if (!error)  return dentry_open(nd.dentry, nd.mnt, flags);//如果成功的话,根据返回的数据填充file结构体: return ERR_PTR(error);}EXPORT_SYMBOL(filp_open);
这个函数执行以下操作:
           1. 把访问模式标志复制到namei_flags标志中
           2. 调用open_namei(),传递他的参数为路径名、修改的访问模式以及局部nameidata数据结构的地址
           3. 调用dentry_open()函数,传递他的参数为访问模式标志、目录项对象的地址以及由查找操作确定的已安装文件系统对象
接下来重点是open_namei()函数,实现了函数名到inode的转换
open_namei()定义在fs/namei.c文件中
int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd){int acc_mode, error = 0;struct dentry *dentry;struct dentry *dir;int count = 0;acc_mode = ACC_MODE(flag);//从flags得到打开模式/* Allow the LSM permission hook to distinguish append    access from general write access. */if (flag & O_APPEND)//总是在文件末尾写acc_mode |= MAY_APPEND;/* 填充nameidata的intent字段,即将打开文件的信息放在nd的intent中*/nd->intent.open.flags = flag;nd->intent.open.create_mode = mode;/* 不需要创建文件*/   //不需要创建文件如果没有设置的话,则在未找到文件的时候不用创建文件,直接通过查找来打开文件if (!(flag & O_CREAT)) {//直接找pathname的dentry和挂载点,结果填充在nd中error = path_lookup(pathname, lookup_flags(flag)|LOOKUP_OPEN, nd);if (error)return error;goto ok;//成功查找到了目标文件的话,就跳转到ok去执行后续操作}error = path_lookup(pathname, LOOKUP_PARENT|LOOKUP_OPEN|LOOKUP_CREATE, nd);//如果创建文件的话,我们需要知道他的父目录,所以加上LOOKUP_PARENT if (error)  return error;error = -EISDIR;if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])//如果最后返回的结果不是一个普通文件的话,就把错误返回。goto exit;dir = nd->dentry;//获取父目录nd->flags &= ~LOOKUP_PARENT;//取消LOOKUP_PARENT的设置down(&dir->d_inode->i_sem); //释放索引节点信号量dentry = __lookup_hash(&nd->last, nd->dentry, nd);//在父目录下查找last名字的dentry是否存在,如果不存在就进行创建一个新的目录项}
再看一下path_lookup()函数——路径名查找函数。
这个函数的作用是将用户传过来的字符串表示的路径转换成一个dentry结构。
它的执行步骤是:
  1. 如下初始化nd参数的某些字段:a. 把 last_type字段置为LAST_ROOT  b. 把flags字段置为参数的flags的值  c. 把depth字段置为0
  2. 未进行读操作而获取当前进程的current->fs->lock读写信号量
  3. 如果路径名的第一个字符是“/”,那么查找操作必须从当前根目录开始
  4. 如果不是‘/’ 则查找操作必须从当前目录开始:获得相应已安装文件对象和目录项对象的地址
  5. 释放当前进程的读写信号量
  6. 当前进程描述符中的total_link_count字段置0
  7. 调用link_path_walk()函数处理正在进行的查找操作
具体代码为
int fastcall path_lookup(const char *name, unsigned int flags, struct nameidata *nd){int retval;//初始化nd参数的某些字段nd->last_type = LAST_ROOT; /* if there are only slashes... */nd->flags = flags;nd->depth = 0;//未进行读操作而获取当前进程的current->fs->lock读写信号量read_lock(&current->fs->lock);if (*name=='/') {if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {nd->mnt = mntget(current->fs->altrootmnt);nd->dentry = dget(current->fs->altroot);read_unlock(¤t->fs->lock);if (__emul_lookup_dentry(name,nd))return 0;read_lock(¤t->fs->lock);}//获取相应已安装文件对象和目录项对象的地址nd->mnt = mntget(current->fs->rootmnt);nd->dentry = dget(current->fs->root);} else {//如果不是‘/’ 则查找操作必须从当前目录开始:获得相应已安装文件对象和目录项对象的地址nd->mnt = mntget(current->fs->pwdmnt);nd->dentry = dget(current->fs->pwd);}//释放当前进程的读写信号量read_unlock(¤t->fs->lock);//当前进程描述符中的total_link_count字段置0current->total_link_count = 0;//调用link_path_walk()函数处理正在进行的查找操作retval = link_path_walk(name, nd);if (unlikely(current->audit_context     && nd && nd->dentry && nd->dentry->d_inode))audit_inode(name,    nd->dentry->d_inode->i_ino,    nd->dentry->d_inode->i_rdev);return retval;}
当path_lookup()返回时,nd指向的nameidata结构用与路径名查找操作有关的数据来填充。


参考资料:深入理解linux内核