1. 程式人生 > >Linux open系統呼叫流程(1)

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函式:

/*找到一個沒有使用的檔案描述符,並標記為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操作。