Linux open系统调用流程(1)

1.概述

我们知道,Linux把设备看成特殊的文件,称为设备文件。在操作文件之前,首先必须打开文件,打开文件的函数是通过open系统调用来实现的。而简单的文件打开操作,在Linux内核实现却是非常的复杂。open函数打开原理就是将进程files_struct结构体和文件对象file相关联。那么具体是怎么实现的呢?让我们一起走进Linux内核文件打开流程。

2. 首先,通过系统调用sys_open函数:

//打开文件的系统调用
asmlinkage long sys_open(const char __user *filename, int flags, int mode)
{
	long ret;

	if (force_o_largefile())
		flags |= O_LARGEFILE;
	//调用do_sys_open函数
	ret = do_sys_open(AT_FDCWD, filename, flags, mode);
	/* avoid REGPARM breakage on x86: */
	prevent_tail_call(ret);
	return ret;
}

这个函数进行了简单的处理,调用do_sys_open函数:

long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
	/*将从用户空间传入的路径名复制到内核空间*/
	char *tmp = getname(filename);
	int fd = PTR_ERR(tmp);

	if (!IS_ERR(tmp)) {
		/*得到一个没有使用的文件描述符*/
		fd = get_unused_fd();
		if (fd >= 0) {
			/*file对象是文件对象,存在于内存,所以没有回写,f_op被赋值*/
			struct file *f = do_filp_open(dfd, tmp, flags, mode);
			if (IS_ERR(f)) {
				put_unused_fd(fd);
				fd = PTR_ERR(f);
			} else {
				fsnotify_open(f->f_path.dentry);
				/*将current->files_struct和文件对象关联*/
				fd_install(fd, f);
			}
		}
		putname(tmp);
	}
	return fd;
}

这个函数主要完成以下几件事情:

(1)调用get_unused_fd得到一个没有使用的文件描述符,这是为读,写准备的,每个打开的文件都有一个文件描述符。

  (2)  调用do_filp_open构建 struct file文件对象,并填充相关信息,这个函数非常复杂,我们以后再看。

  (3)  调用fd_install将文件对象和进程的files_struct对象关联。

 首先看一下get_unused_fd函数:

扫描二维码关注公众号,回复: 4905604 查看本文章

/*找到一个没有使用的文件描述符,并标记为busy
 * Find an empty file descriptor entry, and mark it busy.
 */
int get_unused_fd(void)
{
	/*得到files_struct结构体*/
	struct files_struct * files = current->files;
	int fd, error;
	/*定义fdtable结构*/
	struct fdtable *fdt;
  	error = -EMFILE;
	spin_lock(&files->file_lock);

repeat:
	/*返回files的fdt指针*/
	fdt = files_fdtable(files);
	/*从fdt->open_ds->fds_bits数组查找一个没有置位的文件描述符,open_ds表示打开的文件描述符集,当位图为1表示已经打开,为0已经关闭*/
	fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds,
				files->next_fd);

	/*
	 * N.B. For clone tasks sharing a files structure, this test
	 * will limit the total number of files that can be opened.
	 */
	if (fd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
		goto out;

	/* Do we need to expand the fd array or fd set?  */
	error = expand_files(files, fd);
	if (error < 0)
		goto out;

	if (error) {
		/*
	 	 * If we needed to expand the fs array we
		 * might have blocked - try again.
		 */
		error = -EMFILE;
		goto repeat;
	}
	/*将文件描述符集合的fd置位*/
	FD_SET(fd, fdt->open_fds);
	FD_CLR(fd, fdt->close_on_exec);
	/*下一个描述符,即搜索的位置加1*/
	files->next_fd = fd + 1;
#if 1
	/* Sanity check */
	if (fdt->fd[fd] != NULL) {
		printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd);
		fdt->fd[fd] = NULL;
	}
#endif
	error = fd;

out:
	spin_unlock(&files->file_lock);
	return error;
}

在第7行,得到当前进程的files指针。 在第16-18行,返回打开文件表,在打开的文件描述符集open_ds的fds_bits数组查找对应位置为0的位图,返回位置,表示这个文件描述符没有被使用。接下来,在41-43行,分别将open_fds的fd位置的位图置位,并将fd+1赋值给下一下文件描述符。如果这个文件描述符被占用,就将fdt->fd[fd]=NULL. 最后返回文件描述符fd.

接下来,调do_filp_open函数,其主要功能是返回一个已经填充好的文件对象指针。这个函数比较复杂,在下一节进行分析。

最后,分析一下fd_install函数,传入参数文件描述符fd和文件对象f,具体如下:

/*
 * Install a file pointer in the fd array.
 *
 * The VFS is full of places where we drop the files lock between
 * setting the open_fds bitmap and installing the file in the file
 * array.  At any such point, we are vulnerable to a dup2() race
 * installing a file in the array before us.  We need to detect this and
 * fput() the struct file we are about to overwrite in this case.
 *
 * It should never happen - if we allow dup2() do it, _really_ bad things
 * will follow.
 */
//将进程的current->files对象与file文件对象进行绑定,从而直接操作定义的方法
void fastcall fd_install(unsigned int fd, struct file * file)
{
	/*进程的files_struct对象*/
	struct files_struct *files = current->files;
	/*进程文件表*/
	struct fdtable *fdt;
	spin_lock(&files->file_lock);
	/*取得fdt对象*/
	fdt = files_fdtable(files);
	BUG_ON(fdt->fd[fd] != NULL);
	/*将fdt->fd[fd]指向file对象*/
	rcu_assign_pointer(fdt->fd[fd], file);
	spin_unlock(&files->file_lock);
}
这个函数首先得到files_struct对象指针,然后调用rcu_assign_pointer,将文件对象file赋给fdt->fd[fd], 这样,文件对象就和进程相关联起来了。

因此,不同的进程打开相同的文件,每次打开都会构建一个struct file文件对象,然后将这个对象和具体的进程相关联。其实open调用可以概括如下:

(1)得到一个未使用的文件描述符

(2)构建文件对象struct file

(3)将文件对象和进程相关联

在下一节中,我们将深入理解非常复杂的do_filp_open操作。




猜你喜欢

转载自blog.csdn.net/chenjin_zhong/article/details/8452453