日期:2014-05-16  浏览次数:20785 次

Linux虚拟文件系统--open()

        open()系统调用用来打开一个文件,本文就VFS层,对open系统调用的过程进行一个简单的分析。

 

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
{
	long ret;

	if (force_o_largefile())
		flags |= O_LARGEFILE;

	ret = do_sys_open(AT_FDCWD, filename, flags, mode);
	/* avoid REGPARM breakage on x86: */
	asmlinkage_protect(3, ret, filename, flags, mode);
	return ret;
}

force_o_largefile()用来判断系统是否为32位的,如果不是32位,也就是说为64位,则将O_LARGEFILE置位,主体工作由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_flags(flags);//为文件分配一个文件描述符
		if (fd >= 0) {
			//实际的OPEN操作处理
			struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);
			if (IS_ERR(f)) {
				put_unused_fd(fd);
				fd = PTR_ERR(f);
			} else {
				fsnotify_open(f->f_path.dentry);
				fd_install(fd, f);
			}
		}
		putname(tmp);
	}
	return fd;
}


open操作是特定于某个进程进行的,因此涉及到了VFS中特定于进程的结构,这里简单的介绍下

struct files_struct {
  /*
   * read mostly part
   */
	atomic_t count;
	struct fdtable *fdt;
	struct fdtable fdtab;
  /*
   * written part on a separate cache line in SMP
   */
	spinlock_t file_lock ____cacheline_aligned_in_smp;
	int next_fd;
	struct embedded_fd_set close_on_exec_init;
	struct embedded_fd_set open_fds_init;
	struct file * fd_array[NR_OPEN_DEFAULT];
};

count表示共享该结构的进程数

fdtable是该进程的文件描述符数组

fdt指向fdtable

next_fd表示最大文件描述符号+1

embedded_fd_set是一个位图结构,用来标记文件描述符,close_on_exec_init用来标记那些执行exec时要关闭的文件的文件描述符,open_fds_init用来标记已经分配出去了的文件描述符

fd_array用来存储进程打开的文件的struct file指针

 

do_sys_open()的一个重要任务就是调用get_unused_fd_flags()为即将打开的文件分配一个文件描述符

#define get_unused_fd_flags(flags) alloc_fd(0, (flags))

 

int alloc_fd(unsigned start, unsigned flags)
{
	struct files_struct *files = current->files;//获取当前进程的files_struct
	unsigned int fd;
	int error;
	struct fdtable *fdt;

	spin_lock(&files->file_lock);
repeat:
	fdt = files_fdtable(files);//获取进程的fdtable
	fd = start;
	if (fd < files->next_fd)
		fd = files->next_fd;

	if (fd < fdt->max_fds)
		fd = find_next_zero_bit(fdt->open_fds->fds_bits,
					   fdt->max_fds, fd);//从位图中获取一个空闲位

	error = expand_files(files, fd);//这里根据需要扩充文件描述符数组
	if (error < 0)
		goto out;

	/*
	 * If we needed to expand the fs array we
	 * might have blocked - try again.
	 */
	if (error)//之前进行了扩充操作,重新进行一次空闲bit的搜索
		goto repeat;

	if (start <= files->next_fd)
		files->next_fd = fd + 1;

	FD_SET(fd, fdt->open_fds);//在open_fds的位图上置位
	if (flags & O_CLOEXEC)//如果设定了O_CLOEXEC,则在close_on_exec位图上将相应位置位
		FD_SET(fd, fdt->close_on_exec);
	else
		FD_CLR(fd, fdt->close_on_exec);
	error = fd;
#if 1
	/* Sanity check */
	if (rcu_dereference(fdt->fd[fd]) != NULL) {
		printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);
		rcu_assign_pointer(fdt-&g