1.前言

本文所述关于文件管理的系列文章主要是对陈莉君老师所讲述的文件系统管理知识讲座的整理。

Linux可以支持不同的文件系统,它源于unix文件系统,也是unix文件系统的一大特色。

本文将以不同文件系统之间的拷贝为实例进行讲述

2. 实例:文件拷贝

图 不同文件系统之间的拷贝

图 文件拷贝对应的C语言片段

3.打开文件

3.1 open函数

文件读写之前都要先打开文件,打开函数的原型如下:

  • open通过路径名、标志和mask信息,打开或创建文件,最后返回此文件对应的fd
  • 用户态下调用open,进入系统调用处理程序后,会调用内核相应的系统调用服务例程

3.2 打开文件的内核实现

 从整体流程来看,open的内核实现如下:

  • 进程从用户态获取路径名到内核缓冲区;

  • 然后查找到父目录;如果设置了O_CREAT标志,则继续查找路径最后一个分量

  • 最后获取对应文件的打开文件结构

  • 将这个结构与当前进程的打开文件表联系起来,返回相应的fd。

4. do_sys_open

 long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
struct open_flags op;
int fd = build_open_flags(flags, mode, &op);
struct filename *tmp; if (fd)
return fd; tmp = getname(filename);
if (IS_ERR(tmp))
return PTR_ERR(tmp); fd = get_unused_fd_flags(flags);
if (fd >= ) {
struct file *f = do_filp_open(dfd, tmp, &op);
if (IS_ERR(f)) {
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fsnotify_open(f);
fd_install(fd, f);
}
}
putname(tmp);
return fd;
}

open系统调用服务例程的核心为do_sys_open

4.1 do_filp_open

 struct file *do_filp_open(int dfd, struct filename *pathname,
const struct open_flags *op)
{
struct nameidata nd;
int flags = op->lookup_flags;
struct file *filp; set_nameidata(&nd, dfd, pathname);
filp = path_openat(&nd, op, flags | LOOKUP_RCU);
if (unlikely(filp == ERR_PTR(-ECHILD)))
filp = path_openat(&nd, op, flags);
if (unlikely(filp == ERR_PTR(-ESTALE)))
filp = path_openat(&nd, op, flags | LOOKUP_REVAL);
restore_nameidata();
return filp;
}
  • 当内核要访问一个文件时,第一步需要找到这个文件,这由do_filp_open完成

  • 在do_filp_open的实现中,查找文件过程由path_openat调用path_init和link_path_walk完成

  • 这两个函数将用户传进来的用字符串表示的文件路径,转换成一个dentry结构,建立好相应的inode,并返回file对象

4.2 fd_install

 void fd_install(unsigned int fd, struct file *file)
{
__fd_install(current->files, fd, file);
}
 void __fd_install(struct files_struct *files, unsigned int fd,
struct file *file)
{
struct fdtable *fdt; might_sleep();
rcu_read_lock_sched(); while (unlikely(files->resize_in_progress)) {
rcu_read_unlock_sched();
wait_event(files->resize_wait, !files->resize_in_progress);
rcu_read_lock_sched();
}
/* coupled with smp_wmb() in expand_fdtable() */
smp_rmb();
fdt = rcu_dereference_sched(files->fdt);
BUG_ON(fdt->fd[fd] != NULL);
rcu_assign_pointer(fdt->fd[fd], file);
rcu_read_unlock_sched();
}
  • do_sys_open完成以上处理后,将获取到的file结构体通过fd_install到当前进程的打开文件表中。其索引为fd

4.3 do_sys_open剩余操作

do_sys_open的剩余将进程关联的file的描述符返回用户

用户随后通过文件描述符,来访问这些数据结构

如上打开文件的核心是查找文件

5.查找文件

1. 打开文件的核心为查找,通常内核将查找过程分为两部分:

  • 查找起始位置信息

主要是判断是系统根目录还是当前工作目录,以获取后面循环查找的起始位置(如/home/clj/file1.c中的“/”)

  • 循环查找路径名后续分量

以起始位置开始,循环查找后续每个路径分量

2. 循环查找路径分量的过程,涉及多级cache.

循环查找后续路径分量,首先从dentry cache开始查找,在dentry cache中查找对应的dentry,若找到则直接返回;

若没有找到,则必须去底层文件系统查找对应的dentry

5.1 dentry cache的引入

由于块设备速度比较慢,可能需要很长时间才能找到与一个文件名关联的inode信息,所以引入dentry cache

5.2 dentry cache的描述

  • 缓存的组织

散列表:包含了所有活动的dentry对象。散列表由dentry_hashtable组织,dentry通过d_hash连入散列表中;

LRU链表:dentry结构体中由d_lru链表组织。LRU链表中的元素同时也在dentry cache中;

  • 缓存的查找

缓存由d_hash计算散列值,通过值对应的索引从dentry_hashtable中查找相应的队列;

再从队列头循环查找对应的dentry;

并将其从LRU中移除

图 dentry cache组织图

5.3 快速查找关键结构体qstr

 struct qstr {
union {
struct {
HASH_LEN_DECLARE;
};
u64 hash_len;
};
const unsigned char *name;
};

5.4 dentry cache查找关键代码

 struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name)
{
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 *found = NULL;
struct dentry *dentry; /*
* Note: There is significant duplication with __d_lookup_rcu which is
* required to prevent single threaded performance regressions
* especially on architectures where smp_rmb (in seqcounts) are costly.
* Keep the two functions in sync.
*/ /*
* The hash list is protected using RCU.
*
* Take d_lock when comparing a candidate dentry, to avoid races
* with d_move().
*
* It is possible that concurrent renames can mess up our list
* walk here and result in missing our dentry, resulting in the
* false-negative result. d_lookup() protects against concurrent
* renames using rename_lock seqlock.
*
* See Documentation/filesystems/path-lookup.txt for more details.
*/
rcu_read_lock(); hlist_bl_for_each_entry_rcu(dentry, node, b, d_hash) { if (dentry->d_name.hash != hash)
continue; spin_lock(&dentry->d_lock);
if (dentry->d_parent != parent)
goto next;
if (d_unhashed(dentry))
goto next; /*
* It is safe to compare names since d_move() cannot
* change the qstr (protected by d_lock).
*/
if (parent->d_flags & DCACHE_OP_COMPARE) {
int tlen = dentry->d_name.len;
const char *tname = dentry->d_name.name;
if (parent->d_op->d_compare(parent, dentry, tlen, tname, name))
goto next;
} else {
if (dentry->d_name.len != len)
goto next;
if (dentry_cmp(dentry, str, len))
goto next;
} dentry->d_lockref.count++;
found = dentry;
spin_unlock(&dentry->d_lock);
break;
next:
spin_unlock(&dentry->d_lock);
}
rcu_read_unlock(); return found;
}

Linux文件系统3--打开文件的更多相关文章

  1. Linux操作系统中打开文件数量的查看方法

    Linux操作系统中打开文件数量的查看方法ulimit -n 4096也就是限制用户的最大文件打开数为4096个 在网上查了关于怎么查看文件打开数的文章大致有两种说法/proc/sys/fs/file ...

  2. 【Linux学习】Linux文件系统5—查看文件内容命令

    Linux文件系统5-查看文件内容命令 cat: 由第一行开始显示文件内容 more: 一页一页地显示文件内容,空格键可以继续翻页显示下一页内容 less:与more类似,但是可以往前翻页 head: ...

  3. Linux下Firefox打开文件jnlp文件

    ubuntu(linux)打开jnlp文件 咘咘 2019-05-20 15:12:48 1331 收藏展开 前提条件是安装有java环境.whereis java 查看自己java安装目录.本人是在 ...

  4. Linux文件系统与日志文件

    目录 一.inode和block 1.1.inode和block概述 1.2.inode的内容 inode包含文件的元信息: 查看inode号两种方式 目录文件的结构 1.3.inode的号码 用户通 ...

  5. 深入理解Linux文件系统与日志文件

    目录: 一.inode与block 二.inode内容 三.inode的号码 四.inode的大小 五.链接文件 六.inode节点耗尽故障处理 七.恢复EXT类型的文件编译安装extundelete ...

  6. Linux文件系统之删除文件、文件夹(rm,rmdir)

    rm命令,rmdir命令 rm命令Remove,功能:1)删除目录,2)删除文件.  (可以递归的删除指定目录的所有文件及子目录) 注意:rm是一个危险的命令,使用的时候要特别当心,尤其对于初学者来说 ...

  7. 【Linux】系统打开文件最大数量限制(进程打开的最大文件句柄数设置)

    利用ulimit命令可以对资源的可用性进行控制. -H选项和-S选项分别表示对给定资源的硬限制(hard limit)和软限制(soft limit)进行设置. 硬限制(hard limit)一旦被设 ...

  8. Linux记录-lsof打开文件工具常用操作

    lsof `which httpd` //那个进程在使用apache的可执行文件 lsof /etc/passwd //那个进程在占用/etc/passwd lsof /dev/hda6 //那个进程 ...

  9. Linux文件系统之复制文件cp(文件复制)

    cp 命令(文件复制)   cp命令用来将一个或多个源文件或者目录复制到指定的目的文件或目录.它可以将单个源文件复制成一个指定文件名的具体的文件或一个已经存在的目录下.cp命令还支持同时复制多个文件, ...

随机推荐

  1. 树剖模板(洛谷P3384 【模板】树链剖分)(树链剖分,树状数组,树的dfn序)

    洛谷题目传送门 仍然是一个板子. 不过蒟蒻去学了一下BIT维护区间修改区间求和,常数果真十分优秀 设数列为\(a_i\),差分数组\(d_ i=a_ i-a_ {i-1}\),前缀和\(s_i=\su ...

  2. 【BZOJ2142】礼物(拓展卢卡斯定理)

    [BZOJ2142]礼物(拓展卢卡斯定理) 题面 BZOJ 洛谷 题解 显然如果\(\sum w_i>n\)无解. 否则答案就是:\(\displaystyle \prod_{i=1}^m{n- ...

  3. 【转】__int64 与long long 的区别

    //为了和DSP兼容,TSint64和TUint64设置成TSint40和TUint40一样的数 //结果VC中还是认为是32位的,显然不合适 //typedef signed long int    ...

  4. 牛客练习赛 小A与最大子段和 解题报告

    小A与最大子段和 题意 在一个序列 \(\{a\}\) 里找到一个非空子段 \(\{b\}\), 满足 \(\sum\limits_{i=1}^{|b|}b_i\times i\) 最大 \(n\le ...

  5. python 线程间事件通知

    这是线程间最简单的通信机制:一个线程发送事件,其他线程等待事件事件机制使用一个内部的标志,使用set方法进行使能为True,使用clear清除为falsewait方法将会阻塞当前线程知道标记为True ...

  6. 【洛谷P3600】 随机数生成器

    https://www.luogu.org/problem/show?pid=3600#sub (题目链接) 题意 一个$n$个数的序列,里面每个数值域为$[1,X]$.给$q$个区间,每个区间的权值 ...

  7. Holiday、Vacation、Days off、Leave、Break

    http://write.scu.edu.tw/view.php?bd=mistake&no=25 這些字在英文的用法中都有放假.休息的意思,但用法卻不太一樣,接下來就來看看它們的不同吧. 『 ...

  8. 使用php与mysql构建我们的网站

    技术这个玩意就是要不断的去使用,才能够熟能生巧.今天我记录的使用php与mysql构建我们的网站,其实是我两年前的时候写的项目. 现在看看自己以前写的个人项目,也会感叹时间究竟带走了什么?好记性不如烂 ...

  9. $使用dom4j可解析 返回&#x等字样的 html转义字符【转】

    如果以GET或POST请求某个系统返回,带有 $#x 那很有可能是axis服务器返回的. <?xml version="1.0" encoding="UTF-8&q ...

  10. Forth 语言概要 - Forth: An underview

    Forth 语言概要 - Forth: An underview 原作者 Peter Knaggs 原文标题< Forth: An underview > 译者: 未知 译文链接: htt ...