当前位置: 首页 > 工具软件 > open-c-book > 使用案例 >

open.c源代码阅读

凌通
2023-12-01
就是将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 != 32
	flags |= O_LARGEFILE;
#endif
	tmp = 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字段置0
	current->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内核




 类似资料: