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. 【BZOJ1998】[HNOI2010]物品调度(并查集,模拟)

    [BZOJ1998][HNOI2010]物品调度(并查集,模拟) 题面 BZOJ,为啥这题都是权限题啊? 洛谷 题解 先不管\(0\)位置是个空,把它也看成一个箱子.那么最终的答案显然和置换循环节的个 ...

  2. SharePoint 2013 APP 开发示例 (三)使用远程的web资源

    在这个示例里我们将详细介绍 TokenHelper 类, 我们将看到它是怎么简单地从远程web站点访问SharePoint的.我们还将取到它的一些值.这将帮助我们理解连接是怎么被构造的,同时也方便我们 ...

  3. 强大log

    http://lovelease.iteye.com/blog/1886907 import java.io.BufferedReader; import java.io.File; import j ...

  4. 私有仓库GitLab快速入门篇

    私有仓库GitLab快速入门篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 安装文档请参考官网:https://about.gitlab.com/installation/#ce ...

  5. CM记录-Cloudera Manager常见问题汇总(转载)

    1.无法加载最新的supervisord 解决方案:ps -ef | grep supervisord     kill -9 pid 2.cloudera-scm-agent dead but pi ...

  6. SqlServer中的临时表

    一.什么是临时表 临时表属于会话级的,会话结束的时候,临时表被释放,其创建.使用.删除都和普通表一样,临时表空间一般利用虚拟内存,不必进行磁盘I/O,因此效率较高..临时表有两种:普通临时表 (#Tb ...

  7. js中常用事件

    鼠标事件 /* onclick:点击某个对象时触发 ondblclick:双击某个对象时触发 onmouseover:鼠标移入某个元素时触发 onmouseout:鼠标移出某个元素时触发 onmous ...

  8. elasticsearch核心知识梳理

    https://blog.csdn.net/laoyang360/article/details/52244917

  9. Java 连接 SqlServer工具类

    1.下载 server2008R2驱动jar包 下载jar包 http://www.microsoft.com/zh-cn/download/confirmation.aspx?id=21599 2. ...

  10. Postfix 邮件服务 - roundcube webmail

      roundcubemail作为web端的邮件客户端.是一个基于浏览器,支持多国语言的IMAP客户端,它的操作界面看起像一个桌面应用程序.它提供一个email客户端应该具备的所有功能,包括MIME支 ...