系統調用的內核實現,一文講透open函數內核真實實現。

上一篇:https://blog.csdn.net/weixin_42523774/article/details/103341058

· 本文是Linux文件系列的第三篇,上文《系統調用如何進入內核層次,深入glibc尋找open函數真實實現。》,講到了從應用程序到如何通過glibc中進入內核的過程,本文接着上文,講系統調用在內核中如何實現,還是以open函數爲例,後續會介紹read和write函數的內核實現。
· 本文采用linux3.2.1內核代碼[下載處],並推薦使用Source Insight,這會讓這個過程更容易。
· 我想先提醒一下,這些系統調用的操作者是某一個進程,open操作其實是在read和write操作之前的一個預備動作,如果是真實文件,這個操作就是將文件從硬盤(或flash)中讀取到內存中,當然這個過程中要防止多個進程的同時操作,因此會有很多鎖作爲保護,這也是代碼理解的難點。
· 首先將搜尋結構圖展示出來,讓大家先知其全貌,後續在一步一步細講:(前面直接套用的函數就沒寫註釋)

compat_sys_openat
|-->do_sys_open
	|-->do_filp_open
		|-->do_filp_open
			|-->path_openat
				|-->path_init  #  nd初始化。
				|-->link_path_walk  #  真實尋找。
				|	|-->for(;;) {
				|	|	|-->may_lookup  #  查詢文件權限是否允許訪。
				|	|	|-->hash = init_name_hash();  #  算出該文件名的哈希值,和文件名長度。
				|	|	|-->##  判斷文件名是否使用了"."或者"..",來標明文件類型type  ##
				|	|	|-->d_hash # 查詢是否有哈希表存在。
				|	|	|-->walk_component  #  依據剛剛識別的類型,做單次搜索。
				|	|	|	|-->handle_dots  #  "."".." 文件名處理。
				|	|	|	|-->do_lookup   # 其他文件的搜索。
				|	|	|	|	|-->__d_lookup_rcu   # 不帶rcu搜尋。
				|	|	|	|	|-->__d_lookup      # 帶rcu,可能引起阻塞搜尋。
				|	|	|	|	|-->d_alloc_and_lookup  # 上兩步搜不到,就要通過硬盤文件系統搜尋。
				|	|	|	|-->should_follow_link #  查看是否可以繼續鏈接文件,前面提到過,對鏈接次數有限制。
				|	|	|-->nested_symlink  # 限制遞歸調用不能超過8次,符號鏈接不能超過40次。
				|	|	|-->can_lookup  # 判斷是否可以繼續查找,可以則繼續。
				|	|	|-->terminate_walk(nd);  #  查找完成操作,包括解RCU鎖。
				|	|-->}
				|-->do_last #  查找完成,做打開文件操作

1.續接前文

· 書接上文,我們找到了內核代碼位置include\asm-generic\unistd.h的如下語句:

#define __NR_openat 56
__SC_COMP(__NR_openat, sys_openat, compat_sys_openat)

· 根據這個分別找到了如下的宏定義:

#define __SC_COMP(_nr, _sys, _comp) __SYSCALL(_nr, _comp)
#define __SYSCALL(nr, call) [nr] = (call),

· 這裏我們就明白了,我們這個宏定義,將 compat_sys_openat函數的地址賦給了一個變量,然後讓swi指令去調用這個函數,我們在fs/compat.c找到了這個函數:

asmlinkage long
compat_sys_openat(unsigned int dfd, const char __user *filename, int flags, int mode)
{
	return do_sys_open(dfd, filename, flags, mode);
}

· asmlinkage 表示這個是通過彙編指令鏈接過來的,do_sys_open這就是open函數的真實實現,入參分別 AT_FDCWD, file, oflag, mode,下面我們看看它做了什麼。

2.do_sys_open

· 本文將整個代碼結構都展示出來,大部分內容通過代碼中加註釋的方式來解釋,關鍵部分在代碼後用文字說明。

long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
	struct open_flags op;  //  創建一個標誌集合
	/*  從flags和mode中分離出lookup event bitmap,返回值lookup表示正在做的事件(尋找目錄和結構),op中存有找到之後期望做的事件(執行或創建)*/
	int lookup = build_open_flags(flags, mode, &op);
	char *tmp = getname(filename); //  將文件名從用戶空間複製到內核空間
	int fd = PTR_ERR(tmp);

	if (!IS_ERR(tmp)) {
		fd = get_unused_fd_flags(flags); //  根據flags獲取一個對應類型的未使用的文件號
		if (fd >= 0) {
			struct file *f = do_filp_open(dfd, tmp, &op, lookup);  //  執行文件打開過程
			if (IS_ERR(f)) {  // 判斷是否出錯
				put_unused_fd(fd);  // 釋放剛用get_unused_fd_flags申請的文件號
				fd = PTR_ERR(f);    // 將指針轉化爲錯誤碼返回
			} else {
				fsnotify_open(f);   // 通知其他相關項,該文件已經打開
				fd_install(fd, f);  // 安裝文件指針到fd數組
			}
		}
		putname(tmp); //  釋放內核態緩存
	}
	return fd;
}

· do_sys_open函數首先做了做了設置open_flags標誌和轉換用戶空間的文件名到內核空間,核心功能是調用do_filp_open,我們進一步尋找:

3.do_filp_open

struct file *do_filp_open(int dfd, const char *pathname,
		const struct open_flags *op, int flags)
{
	struct nameidata nd;
	struct file *filp;
	/* 核心就是path_openat,就是沿着打開文件名的整個路徑,一層層解析,最後得到文件對象 */
	filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
	if (unlikely(filp == ERR_PTR(-ECHILD)))
		filp = path_openat(dfd, pathname, &nd, op, flags);
	if (unlikely(filp == ERR_PTR(-ESTALE)))
		filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL);
	return filp;
}

· 這裏的核心就是path_openat,就是沿着打開文件名的整個路徑,一層層解析,最後得到文件對象。但是爲什麼最多會調用3次?
· 由操作系統發展趨勢一文中講過,我們講過當前的操作系統的內存管理系統中,CPU只能間接通過將硬盤數據緩存到內存中才能使用。而用來緩存文件的區域叫dentry cache ,這是一個哈希表。
· 第一次搜索採用rcu-walk方式搜索dentry cache,這種方式不會阻塞等待(RCU纔會阻塞),更高效,但是可能會失敗(因爲文件inode本身還有順序鎖和自旋鎖的保護);
· 第二次搜索則是採用ref-walk方式搜索dentry cache,考慮rcu鎖的情形,但是可能會阻塞。
· 如果這樣仍然失敗,說明內存中沒有該文件,這樣就需要第三次,直接通過硬盤文件系統,進入硬盤慢速搜索,要帶LOOKUP_REVAL標誌。

4.path_openat

static struct file *path_openat(int dfd, const char *pathname,
		struct nameidata *nd, const struct open_flags *op, int flags)
{
	struct file *base = NULL;
	struct file *filp;
	struct path path;
	int error;

	filp = get_empty_filp(); // 找到一個未使用的文件結構返回
	if (!filp)
		return ERR_PTR(-ENFILE);

    /*  初始化nd中的 intent結構的標誌 */
	filp->f_flags = op->open_flag;
	nd->intent.open.file = filp;
	nd->intent.open.flags = open_to_namei_flags(op->open_flag);
	nd->intent.open.create_mode = op->mode;

	/*  初始化nd中的其他參數,通過開頭是不是"/"和dfd是否是AT_FDCWD,識別是絕對路徑還是相對路徑,設置RCU */
	error = path_init(dfd, pathname, flags | LOOKUP_PARENT, nd, &base);
	if (unlikely(error))
		goto out_filp;
	current->total_link_count = 0;  //  重置鏈接的次數,後續做判斷
	/*  命名解析函數,針對路徑循環搜索,直到找到最後一個不是目錄的文件,次步驟需要細講---> */
	error = link_path_walk(pathname, nd);
	if (unlikely(error))
		goto out_filp;
	/*  處理打開文件的最後操作,如果這個文件是個鏈接文件,則需要找到真實文件之後再執行do_last */
	filp = do_last(nd, &path, op, pathname);
	while (unlikely(!filp)) { /* trailing symlink */
		struct path link = path;
		void *cookie;
		if (!(nd->flags & LOOKUP_FOLLOW)) {
			path_put_conditional(&path, nd);
			path_put(&nd->path);
			filp = ERR_PTR(-ELOOP);
			break;
		}
		nd->flags |= LOOKUP_PARENT;
		nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
		error = follow_link(&link, nd, &cookie);
		if (unlikely(error))
			filp = ERR_PTR(error);
		else
			filp = do_last(nd, &path, op, pathname);
		put_link(nd, &link, cookie);
	}
out:
	if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT))
		path_put(&nd->root);
	if (base)
		fput(base);
	release_open_intent(nd);
	return filp;

out_filp:
	filp = ERR_PTR(error);
	goto out;
}

· nameidata是搜索用到的主要結構,首先要調用path_init將其初始化,然後調用link_path_walk搜尋文件,找到文件之後調用do_last來做文件打開操作,如果是個鏈接文件還需要找到真實文件,再執行do_last。
· 下面深入介紹 path_init 和 link_path_walk。

5.path_init

static int path_init(int dfd, const char *name, unsigned int flags,
		     struct nameidata *nd, struct file **fp)
{
	int retval = 0;
	int fput_needed;
	struct file *file;

	nd->last_type = LAST_ROOT; /* if there are only slashes... */
	nd->flags = flags | LOOKUP_JUMPED;
	nd->depth = 0;
	/* flags中表明是從根目錄搜尋,然後查看文件權限上是否允許,如果允許,則對nd、RCU和順序鎖進行初始化後就返回 */
	if (flags & LOOKUP_ROOT) {
		struct inode *inode = nd->root.dentry->d_inode;
		if (*name) {
			if (!inode->i_op->lookup)
				return -ENOTDIR;
			retval = inode_permission(inode, MAY_EXEC);
			if (retval)
				return retval;
		}
		nd->path = nd->root;
		nd->inode = inode;
		if (flags & LOOKUP_RCU) {
			br_read_lock(vfsmount_lock);
			rcu_read_lock();
			nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
		} else {
			path_get(&nd->path);
		}
		return 0;
	}

	nd->root.mnt = NULL;
	/*通過字符的開頭是"/",也可以表示絕對路徑,則也要做相關初始化*/
	if (*name=='/') {
		if (flags & LOOKUP_RCU) {
			br_read_lock(vfsmount_lock);
			rcu_read_lock();
			set_root_rcu(nd);
		} else {
			set_root(nd);
			path_get(&nd->root);
		}
		nd->path = nd->root;
		/*開頭不是"/",且dfd == AT_FDCWD表示使用相對路徑*/
	} else if (dfd == AT_FDCWD) {
		if (flags & LOOKUP_RCU) {
			struct fs_struct *fs = current->fs;
			unsigned seq;

			br_read_lock(vfsmount_lock);
			rcu_read_lock();

			do {
				seq = read_seqcount_begin(&fs->seq);
				nd->path = fs->pwd;
				nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
			} while (read_seqcount_retry(&fs->seq, seq));
		} else {
			get_fs_pwd(current->fs, &nd->path);
		}
		/*最後一種特殊情況是依據文件號打開方式,本文不考慮此情況*/
	} else {
		struct dentry *dentry;

		file = fget_raw_light(dfd, &fput_needed);
		retval = -EBADF;
		if (!file)
			goto out_fail;

		dentry = file->f_path.dentry;

		if (*name) {
			retval = -ENOTDIR;
			if (!S_ISDIR(dentry->d_inode->i_mode))
				goto fput_fail;

			retval = inode_permission(dentry->d_inode, MAY_EXEC);
			if (retval)
				goto fput_fail;
		}

		nd->path = file->f_path;
		if (flags & LOOKUP_RCU) {
			if (fput_needed)
				*fp = file;
			nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
			br_read_lock(vfsmount_lock);
			rcu_read_lock();
		} else {
			path_get(&file->f_path);
			fput_light(file, fput_needed);
		}
	}

	nd->inode = nd->path.dentry->d_inode;
	return 0;

fput_fail:
	fput_light(file, fput_needed);
out_fail:
	return retval;
}

· 這裏就是依據文件是絕對路徑(從根目錄開始搜尋)還是相對路徑(從當前目錄開始搜索)做區分,對nd、rcu和順序鎖做不同的初始化。AT_FDCWD在這裏終於用到了,就是表示是相對路徑的。
· 下面回到上一步,在看看初始化之後調用的link_path_walk做了什麼:

6.link_path_walk

static int link_path_walk(const char *name, struct nameidata *nd)
{
	struct path next;
	int err;
	
	while (*name=='/') //  如果是/開頭,這需要將其+到不是"/"爲止
		name++;
	if (!*name) //  如果字符是無效值,則異常退出
		return 0;

	/* 到此,我們有了一個依據初始化的nameidata,開始搜尋循環 . */
	for(;;) {
		unsigned long hash;
		struct qstr this;
		unsigned int c;
		int type;

		err = may_lookup(nd);  //  查詢文件權限是否允許訪問
 		if (err)
			break;

		this.name = name;
		c = *(const unsigned char *)name;

		/*  算出該文件名的哈希值,和文件名長度.  */
		hash = init_name_hash();
		do {
			name++;
			hash = partial_name_hash(c, hash);
			c = *(const unsigned char *)name;
		} while (c && (c != '/'));
		this.len = name - (const char *) this.name;
		this.hash = end_name_hash(hash);

		/*  判斷文件名是否使用了"."或者"..",是則標明type  */
		type = LAST_NORM;
		if (this.name[0] == '.') switch (this.len) {
			case 2:
				if (this.name[1] == '.') {
					type = LAST_DOTDOT;
					nd->flags |= LOOKUP_JUMPED;
				}
				break;
			case 1:
				type = LAST_DOT;
		}
		/*  文件名沒有"."或者"..",則是普通文件或目錄,則查看是否有hash緩存表,有表示內存中存在緩存,沒有直接退出  */
		if (likely(type == LAST_NORM)) {
			struct dentry *parent = nd->path.dentry;
			nd->flags &= ~LOOKUP_JUMPED;
			if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
				err = parent->d_op->d_hash(parent, nd->inode,
							   &this);
				if (err < 0)
					break;
			}
		}

		/* 如果尾部是"/",則open要打開的是目錄,則直接退出 */
		if (!c)
			goto last_component;
		while (*++name == '/');  //  當有多個"/"時,則搜索時去掉
		if (!*name)
			goto last_component;

		/* 依據剛剛識別的類型,做不同的操作,此步驟再細說---> */
		err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW);
		if (err < 0)
			return err;

		/* 限制遞歸調用不能超過8次,符號鏈接不能超過40次 */
		if (err) {
			err = nested_symlink(&next, nd);
			if (err)
				return err;
		}
		
		/* 判斷是否可以繼續查找,可以則繼續 */
		if (can_lookup(nd->inode))
			continue;
		err = -ENOTDIR; 
		break;
		/* here ends the main loop */

last_component:
		nd->last = this;
		nd->last_type = type;
		return 0;
	}
	terminate_walk(nd);  //  查找完成操作,包括解RCU鎖
	return err;
}

· 到這裏我們看到循環了,這就是逐次搜索文件名的循環。
· 循環中,首先判斷文件名有沒有".“或者”…",來設置文件類型;然後查看是否有hash表,有表示內存中存在緩存,沒有直接退出。
· 然後繼續調用walk_component做某一次的搜尋操作。

7.walk_component

static inline int walk_component(struct nameidata *nd, struct path *path,
		struct qstr *name, int type, int follow)
{
	struct inode *inode;
	int err;
	/* "." 和 ".." 符號是特殊的,".." 尤其特殊,這是因爲必須知道當前文件所在目錄的父目錄  */
	if (unlikely(type != LAST_NORM))
		return handle_dots(nd, type);  //處理"."和".."文件名,成功則設置nd->path.dentry和nd->inode,如果父目錄是掛載點找到掛載點目錄再處理。
		
	/*  普通文件的搜索  */
	err = do_lookup(nd, name, path, &inode);
	if (unlikely(err)) {
		terminate_walk(nd);
		return err;
	}
	/*  如果沒有找到,返回對應的錯誤碼  */
	if (!inode) {
		path_to_nameidata(path, nd);
		terminate_walk(nd);
		return -ENOENT;
	}
	
	/*  查看是否鏈接次數超過最大值,前面提到過,對鏈接次數有限制  */
	if (should_follow_link(inode, follow)) {
		if (nd->flags & LOOKUP_RCU) {
			if (unlikely(unlazy_walk(nd, path->dentry))) {
				terminate_walk(nd);
				return -ECHILD;
			}
		}
		BUG_ON(inode != path->dentry->d_inode);
		return 1;
	}
	path_to_nameidata(path, nd); //  將path中的dentry放到nd->path中
	nd->inode = inode;
	return 0;
}

· 這裏先判斷文件名是不是".“和”. .",如果是的話就處理退出;
· 如果是普通文件,就執行do_lookup,後面繼續深入分析;
· 然後就是針對搜尋情況做一些異常判斷,請看代碼註釋。

8.do_lookup


static int do_lookup(struct nameidata *nd, struct qstr *name,
			struct path *path, struct inode **inode)
{
	struct vfsmount *mnt = nd->path.mnt;
	struct dentry *dentry, *parent = nd->path.dentry;
	int need_reval = 1;
	int status = 1;
	int err;

	/*  文件可能存在RCU鎖,這表示可能有別的進程使用,則有可能被加載到 dcache 中	 */
	if (nd->flags & LOOKUP_RCU) {
		unsigned seq;
		*inode = nd->inode;
		dentry = __d_lookup_rcu(parent, name, &seq, inode); // 搜索,只使用內存屏障作爲防護手段
		if (!dentry)
			goto unlazy;

		/* 在子目錄的read_seqcount_begin的內存屏障就足夠,這裏判斷父目錄和子目錄的seq是否一致*/
		if (__read_seqcount_retry(&parent->d_seq, nd->seq))
			return -ECHILD;
		nd->seq = seq;
		/* 找到的目錄是否需要重新生效,需要就執行denter的對應函數*/
		if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) {
			status = d_revalidate(dentry, nd);
			if (unlikely(status <= 0)) {
				if (status != -ECHILD)
					need_reval = 0;
				goto unlazy;
			}
		}
		/*  找到的目錄是否需要搜索  */
		if (unlikely(d_need_lookup(dentry)))
			goto unlazy;
		path->mnt = mnt; //將搜索到的mnt和dentry值保存,下次繼續搜索
		path->dentry = dentry;
		/*  判斷是否此文件是掛載點,是則需要在mount_hashtable中找掛載點,再找子目錄 */
		if (unlikely(!__follow_mount_rcu(nd, path, inode)))
			goto unlazy;
		if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
			goto unlazy;
		return 0;
unlazy:
		if (unlazy_walk(nd, dentry))
			return -ECHILD;
	} else {
		dentry = __d_lookup(parent, name); // 搜索,需要使用RCU鎖,在hash表中搜索。
	}
    /*  如果搜到的dentry標記了要再搜一次,這種情況就是該文件是另一個文件系統的掛載點  */
	if (dentry && unlikely(d_need_lookup(dentry))) {
		dput(dentry);
		dentry = NULL;
	}
retry:  // 沒有找到dentry,則需要再搜索一次
	if (unlikely(!dentry)) {
		struct inode *dir = parent->d_inode;
		BUG_ON(nd->inode != dir);

		mutex_lock(&dir->i_mutex);
		dentry = d_lookup(parent, name);  //  再搜索一次
		if (likely(!dentry)) {
			dentry = d_alloc_and_lookup(parent, name, nd);
			if (IS_ERR(dentry)) {
				mutex_unlock(&dir->i_mutex);
				return PTR_ERR(dentry);
			}
			/* known good */
			need_reval = 0;
			status = 1;
		} else if (unlikely(d_need_lookup(dentry))) {
			dentry = d_inode_lookup(parent, dentry, nd);
			if (IS_ERR(dentry)) {
				mutex_unlock(&dir->i_mutex);
				return PTR_ERR(dentry);
			}
			/* known good */
			need_reval = 0;
			status = 1;
		}
		mutex_unlock(&dir->i_mutex);
	}
	if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE) && need_reval)
		status = d_revalidate(dentry, nd);
	if (unlikely(status <= 0)) {
		if (status < 0) {
			dput(dentry);
			return status;
		}
		if (!d_invalidate(dentry)) {
			dput(dentry);
			dentry = NULL;
			need_reval = 1;
			goto retry;
		}
	}

	path->mnt = mnt;
	path->dentry = dentry;
	err = follow_managed(path, nd->flags);
	if (unlikely(err < 0)) {
		path_put_conditional(path, nd);
		return err;
	}
	if (err)
		nd->flags |= LOOKUP_JUMPED;
	*inode = path->dentry->d_inode;
	return 0;
}

· 這裏首先會調用__d_lookup_rcu 或者 __d_lookup進入dcache中搜索看看是否存在相同文件,如果沒找到,則會調用d_lookup再搜索一遍確認一次(可能在阻塞的這段時間被添加進了dcache)。如果真的沒有,就要調用d_alloc_and_lookup 和 d_inode_lookup 使用硬盤文件系統的接口做慢速搜索。

9. dentry cache搜索函數__d_lookup_rcu

struct dentry *__d_lookup_rcu(struct dentry *parent, struct qstr *name,
				unsigned *seq, struct inode **inode)
{
	unsigned int len = name->len;
	unsigned int hash = name->hash;
	const unsigned char *str = name->name;
	struct hlist_bl_head *b = d_hash(parent, hash);
	struct hlist_bl_node *node;
	struct dentry *dentry;

	/* hash表的循環查找,實質上是for循環,要經過多次的比較,如果某項不同就跳過,繼續搜索*/
	hlist_bl_for_each_entry_rcu(dentry, node, b, d_hash) {
		struct inode *i;
		const char *tname;
		int tlen;

		if (dentry->d_name.hash != hash)
			continue;

seqretry:
		*seq = read_seqcount_begin(&dentry->d_seq);
		if (dentry->d_parent != parent)
			continue;
		if (d_unhashed(dentry))
			continue;
		tlen = dentry->d_name.len;
		tname = dentry->d_name.name;
		i = dentry->d_inode;
		prefetch(tname);

		if (read_seqcount_retry(&dentry->d_seq, *seq))
			goto seqretry;
		if (unlikely(parent->d_flags & DCACHE_OP_COMPARE)) {
			if (parent->d_op->d_compare(parent, *inode,
						dentry, i,
						tlen, tname, name))
				continue;
		} else {
			if (dentry_cmp(tname, tlen, str, len))
				continue;
		}

		*inode = i;
		return dentry;
	}
	return NULL;
}

· 這裏就是dcache中的搜索函數,dcache hash表採用for循環,每一項要經過多次的比較,如果某項不同就跳過,繼續搜索,直到找到全部符合的文件,否者就是沒找到。搜不到這不代表這個文件就不在dcache中,因爲條件中有順序鎖seq的判斷,在SMP的多核架構中可能會出現這種情況,這就需要回到第3步的do_filp_open中繼續第二次的path_openat,這是就會調用__d_lookup函數,各位可以自行看看,內容也就增加了rcu和自旋鎖的限制,這會引起阻塞。

10.硬盤搜索函數d_alloc_and_lookup

static struct dentry *d_alloc_and_lookup(struct dentry *parent,
				struct qstr *name, struct nameidata *nd)
{
	struct inode *inode = parent->d_inode;
	struct dentry *dentry;
	struct dentry *old;

	/* Don't create child dentry for a dead directory. */
	if (unlikely(IS_DEADDIR(inode)))
		return ERR_PTR(-ENOENT);
    /*  這裏首先創建一個dentry對象 */
	dentry = d_alloc(parent, name);
	if (unlikely(!dentry))
		return ERR_PTR(-ENOMEM);
	/*  然後調用文件系統的lookup函數搜索 */
	old = inode->i_op->lookup(inode, dentry, nd);
	if (unlikely(old)) {
		dput(dentry);
		dentry = old;
	}
	return dentry;
}

· 到這裏就比較簡單了,先分配一個dentry對象,然後調用硬盤文件系統的lookup接口去搜索。

11.總結

· 回過頭來,我們最好再看看這個調用過程,核心就在最後。
· 經過這次的分析,我們實際上深入了虛擬文件系統的核心部分,知道了內核是如何搜尋一個文件的。爽得很!!!!!!!!!!(這麼多感嘆號,是爲了發泄這兩週對我的折磨)
· 但是爽歸爽,其實中間有些地方一帶而過了,其實我們中間忽略了VFS的一些基礎性的架構介紹,需要把這條走過的路鞏固一下,請見下篇《文件系統原理 和 VFS架構》(一股 百家講壇 的感覺 ~~~)。

------------------如果有所收穫的話,請幫忙點個贊吧!

下一篇:https://blog.csdn.net/weixin_42523774/article/details/103739139

發佈了15 篇原創文章 · 獲贊 19 · 訪問量 4673
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章