本文参考了以下资料和链接:
低调的 Linux 文件系统家族
鳥哥的 Linux 私房菜 -- 基礎學習篇
https://www.youtube.com/watch?v=jXS0kvHKzTs
趣谈Linux操作系统
https://www.cnblogs.com/yikoulinux/p/13559335.html
一口气搞懂「文件系统」,就靠这 25 张图了_sys 文件系统图片-优快云博客
Linux 系统根目录下面的目录名
-
/bin:
重要的二进制应用程序(例如ls, mv, rm, mkdir rmdir, gzip, tar, telnet和ftp等)放在这里,是放置一般用户使用的执行程序。-
注意系统默认的执行文件的放置目录是/bin, /sbin, /usr/bin, /usr/sbin。
-
-
/boot:
启动包含引导加载程序的相关文件。目录下的vimlinux-zzz就是Linux的内核。 -
/dev:
包含设备文件,终端文件,USB 或者连接到系统的任何设备。例如/dev/fd0代表软驱,/dev/cdrom代表光驱。这个目录下的文件通常分为两种:管理硬盘I/O的块文件与外设的字符文件。 -
/etc:
系统配置文件,启动脚本等,包含用户账号与密码,系统的主要设定,http架站参数,启动/停止单个应用程序的启动和关闭 shell 脚本等。注意 /etc/passwd和/etc/group跟用户强相关。在这个目录下工作的时候一定要记得备份,否则文件被意外修改后很麻烦。-
/etc/init.d
所有服务默认的启动脚本文件?
-
-
/home:
本地主要路径,所有用户都用 home 目录存储个人信息 -
/lib:
系统库文件,包含支持位于 /bin 和 /sbin 下的二进制库文件 -
/lost+found:
在根目录下提供一个遗失+查找系统,必须在 root 用户下才能查看当前目录下的内容 -
/media:
挂载可移动介质 -
/mnt:
挂载文件系统 -
/opt:
提供一个可选的应用程序安装目录 -
/proc:
特殊的动态目录,用于放置系统核心与执行程序所需的信息,例如当前网络状态,运行中进程等信息。这个目录将在启动Linux的时候自动被挂上,而且该目录不会占用硬盘空间,因为里面都是内存中的数据。 -
/root:
root 用户的主要目录文件夹 -
/sbin:
系统管理常用的的二进制系统文件,例如fdisk, mke2fs, fsck, mkswap和mount等。注意/sbin和/bin不一样,这里面主要放置root管理员使用的程序。 -
/tmp:
系统和用户创建的临时文件,系统重启时,这个目录下的文件都会被删除。 -
/usr:
最重要的一个目录,
包含绝大多数用户都能访问的应用程序和文件。有点类似windows下的Program Files目录。-
/usr/include: 一些套件的头文件
-
/usr/local: 系统预留的让用户安装后将来升级的套件的目录
其中,
/usr/local/bin 放可执行文件
/usr/local.lib 放库文件
/usr/local/etc 放配置文件
/usr/local/share 放其它资源文件 -
/usr/src: 放置核心源代码的默认目录
-
/usr/lib: 内含许多程序与子程序所需的函数库
-
/usr/share/doc: 放置一些系统说明文件的地方
-
/usr/share/man: 放置一些系统说明文件的地方,即使用man时会查询的路径
-
-
/var:
经常变化的文件,诸如日志文件或数据库等。所有服务的登录文件或错误信息文件(log)都在/var/log下。数据库如MySql则在/var/lib下,用户未读邮件默认存放地点为/var/spool/mail。 -
/sys:
https://www.cnblogs.com/linfeng-learning/p/9313757.html
深入概述sysfs文件系统,有这一篇就够了! - 知乎sysfs是一个基于ramfs的文件系统,在2.6内核开始引入,用来导出内核对象(kernel object)的数据、属性到用户空间,以文件目录结构的形式为用户空间提供对这些数据、属性的访问支持。
从驱动开发的角度,/sysfs为用户提供了除了设备文件/dev和/proc之外的另外一种通过用户空间访问内核数据的方式。使用sysfs,编译内核的时候需要定义CONFIG_SYSFS,可以通过mount -t sysfs sysfs /sys命令来挂载sysfs到"/sys"目录。
//from 趣谈Linux操作系统 第31讲 输入与输出
/sys路径下面的/sysfs文件系统是把实际连接 到系统上的设备和总线组成了一个分层的文件系统。这个文件系统是当前系统上实际的设备数的真实反映。
在/sys路径下有下列的文件夹:
a) /sys/devices是内核对系统中所有设备的分层次的表示;
b) /sys/dev目录下一个char文件夹,一个block文件夹,分别维护一个按字符设备和块设备的主次号码 (major:minor)链接到真实的设备(/sys/devices下)的符号链接文件;
c) /sys/block是系统中当前所有的块设备;
d) /sys/module有系统中所有模块的信息。
有了sysfs以后,我们还需要一个守护进程udev。当一个设备新插入系统的时候,内核会检测到这个设备, 并会创建一个内核对象kobject 。 这个对象通过sysfs文件系统展现到用户层,同时内核还向用户空间发送一 个热插拔消息。udevd会监听这些消息,在/dev中创建对应的文件。
注意:
1) /根目录需要256MB。
2) /boot大概在50MB左右,因为启动文件不大。
3) /var至少需要几个GB,因为mail和proxy默认的存储区都在这个目录中。
4) /home和/usr通常是最大的,因为用户所安装的数据文件都在/usr下,而用户数据则都放在/home中,所以通常建议把硬盘剩余空间平均分配给着两个目录。
5) 我们安装文件后,通常主执行文件会放在 /usr/bin 或者 /usr/sbin 下面,其他的库文件会放在 /var 下面, 配置文件会放在 /etc 下面。
对单个文件的系统调用
系统调用 | 描述 |
---|---|
fd = creat(name,mode) | 一种创建一个新文件的方式 |
fd = open(file, ...) | 打开文件读、写或者读写 |
s = close(fd) | 关闭一个打开的文件 |
n = read(fd, buffer, nbytes) | 从文件中向缓存中读入数据 |
n = write(fd, buffer, nbytes) | 从缓存中向文件中写入数据 |
position = lseek(fd, offset, whence) | 移动文件指针 |
s = stat(name, &buf) | 获取文件信息 |
s = fstat(fd, &buf) | 获取文件信息 |
s = pipe(&fd[0]) | 创建一个管道 |
s = fcntl(fd,...) | 文件加锁等其他操作 |
对整个目录和文件的系统调用
系统调用 | 描述 |
---|---|
s = mkdir(path,mode) | 创建一个新的目录 |
s = rmdir(path) | 移除一个目录 |
s = link(oldpath,newpath) | 创建指向已有文件的链接 |
s = unlink(path) | 取消文件的链接 |
s = chdir(path) | 改变工作目录 |
dir = opendir(path) | 打开一个目录读取 |
s = closedir(dir) | 关闭一个目录 |
dirent = readdir(dir) | 读取一个目录项 |
rewinddir(dir) | 回转目录使其在此使用 |
硬链接与符号连接
硬链接就是再建立一个inode链接到文件位置的Block块。进行硬链接时,具体的文件内容不会改变,只是在查询时,利用原来的inode和后来添加的inode都可以指定到该文件放置的地点。因此,读取两个inode的结果都是存取同一个文件的内容。
但是注意,因为inode会链接到Block块,而目录本身仅仅消耗inode,所以硬链接不能链接目录。
符号链接就是软链接,就是再建立一个独立文件,而这个文件会让数据读取操作指向它链接的那个文件。当源文件被删除,符号链接的文件就打不开了。而硬链接的话,即使某一个inode被删除,只要还有一个inode,该文件都能被找到。
符号链接类似于Windows下的快捷方式。
Linux 文件系统都是什么鬼 | labuladong 的算法笔记
1. 如果修改系统配置,就去 /etc
找,如果修改用户的应用程序配置,就在用户家目录的隐藏文件里找。
2. 你在命令行里可以直接输入使用的命令,其可执行文件一般就在以下几个位置:
/bin
/sbin
/usr/bin
/usr/sbin
/usr/local/bin
/usr/local/sbin
/home/USER/.local/bin
/home/USER/.local/sbin
如果你写了一个脚本/程序,想在任何时候都能直接调用,可以把这个脚本/程序添加到上述目录中。
3. 如果某个程序崩溃了,可以到 /val/log
中尝试寻找出错信息,到 /tmp
中寻找残留的临时文件。
4. 设备文件在 /dev
目录,但是一般来说系统会自动帮你挂载诸如 U 盘之类的设备,可以到 /media
文件夹访问设备内容。
关于inode
1. 文件第一次open的时候,会创建文件的inode。
struct file结构体来表示每个打开的文件,每打开一个文件,内核会创建一个结构体,并将对该文件上的操作函数传递给 该结构体的成员变量f_op,当文件所有实例被关闭后,内核会释放这个结构体。
struct file {
const struct file_operations *f_op;
/* needed for tty driver, and maybe others */
void *private_data;
};
2. inode和struct file的区别
struct inode描述的是文件的静态信息,即这些信息很少会改变,而struct file描述的是动态信息,即对文件的操作的时候,struct file里面的信息经常会发生变化。典型的是struct file结构体里面的f_ops(记录当前文件的位移量),每次读写一个普通文件时f_ops的值都会发生改变。
每一个打开的文件,都有一个struct file结构。这里面有一个struct file_operations f_op,用于定义对这个文件做的操作。__vfs_read会调用相应文件系统的file_operations里面的read操作, __vfs_write会调用相应文件系统file_operations里的write操作。
3. inode 和 dentry的区别
Linux文件系统会为每个文件都分配两个数据结构,目录项(dentry , Directory Entry)和索引节点(inode , Index Node)。
inode 描述文件(包括目录)的元信息
dentry 描述目录内文件信息与文件对应的 inode 信息。struct dentry,用于标识目录之外,还可以表示文件名,还会建立了文件名及其inode之间的关联。
//from 极客空间 Linux性能优化 23 基础篇:Linux 文件系统是怎么工作的?
inode和dentry 主要用来记录文件的元信息和目录结构。
索引节点,简称为 inode,用来记录文件的元数据,比如 inode 编号、文件大小、访问权限、修改日期、数据的位置等。索引节点和文件一一对应,它跟文件内容一样,都会被持久化存储到磁盘中。所以记住,索引节点同样占用磁盘空间。
目录项,简称为 dentry,用来记录文件的名字、索引节点指针以及与其他目录项的关联关系。多个关联的目录项,就构成了文件系统的目录结构。不过,不同于索引节点,目录项是由内核维护的一个内存数据结构,所以通常也被叫做目录项缓存。换句话说,索引节点是每个文件的唯一标志,而目录项维护的正是文件系统的树状结构。
目录项和索引节点的关系是多对一,你可以简单理解为,一个文件可以有多个别名。举个例子,通过硬链接为文件创建的别名,就会对应不同的目录项,不过这些目录项本质上还是链接同一个文件,所以,它们的索引节点相同。
4. inode和fd的区别
// from 趣谈Linux操作系统第27讲 文件系统
文件描述符fd,就是用来区分一个进程打开的多个文件的。它的作用域就是当前进程,出了当前进程这个文件 描述符就没有意义了。open返回的fd必须记录好,我们对这个文件的所有操作都要靠这个fd,包括最后关 闭文件。
5. 小文件占inode,大文件占磁盘storage。如果有很多小文件,可能磁盘还没用完,但是inode已经用完了。
a) Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或者绰号。
表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:首先,系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。
https://www.youtube.com/watch?v=S8A_NZfoByw
b) 使用ls -i命令,可以看到文件名对应的inode号码。
查看每个硬盘分区的inode总数和已经使用的数量,可以使用df -i命令。
查看每个inode节点的大小,可以用如下命令:sudo dumpe2fs -h /dev/hda | grep "Inode size"
c) 目录文件的"链接数"。创建目录时,默认会生成两个目录项:"."和".."。前者的inode号码就是当前目录的inode号码,等同于当前目录的"硬链接";后者的inode号码就是当前目录的父目录的inode号码,等同于父目录的"硬链接"。所以,任何一个目录的"硬链接"总数,总是等于2加上它的子目录总数(含隐藏目录)。
d) inode的特殊作用
由于inode号码与文件名分离,这种机制导致了一些Unix/Linux系统特有的现象。
1. 有时,文件名包含特殊字符,无法正常删除。这时,直接删除inode节点,就能起到删除文件的作用。
2. 移动文件或重命名文件,只是改变文件名,不影响inode号码。
3. 打开一个文件以后,系统就以inode号码来识别这个文件,不再考虑文件名。因此,通常来说,系统无法从inode号码得知文件名。
第3点使得软件更新变得简单,可以在不关闭软件的情况下进行更新,不需要重启。因为系统通过inode号码,识别运行中的文件,不通过文件名。更新的时候,新版文件以同样的文件名,生成一个新的inode,不会影响到运行中的文件。等到下一次运行这个软件的时候,文件名就自动指向新版文件,旧版文件的inode则被回收。
7. 无论是文件夹 还是文件,都有一个inode。inode里面会指向数据块,对于文件夹的数据块,里面是一个表,是下一层的 文件名和inode的对应关系,文件的数据块里面存放的才是真正的数据。//from 趣谈Linux操作系统 第28讲 - 硬盘文件系统。
关于10 essential syscalls
open/close/(creat)
read/write/lseek/(tell)
fstate/ftruncate
unlink/mkdir/dup...
关于File结构
1. 文件系统就相当于一个map, map<string, File > //string是文件路径,File 是文件的File 结构
2. struct File {
mode(permissions, type), size, user, group,
timestamps(atime, mtime, ctime),
content(bytes),
}
3. atime - 访问时间,mtime - 修改时间, ctime - 创建时间
4. 每一个打开的文件,都有一个struct file结构。struct file描述的是动态信息,即对文件的操作的时候,struct file里面的信息经常会发生变化。典型的是struct file结构体里面的f_ops(记录当前文件的位移量),每次读写一个普通文件时f_ops的值都会发生改变。
struct file_operations f_op 用于定义对这个文件做的操作。__vfs_read会调用相应文件系统的file_operations里面的read操作, __vfs_write会调用相应文件系统file_operations里的write操作。
5. 内核中,缓存以页为单位放在内存里面,那我们如何知道,一个文件的哪些数据已经被放到缓存中了呢? 每一个打开的文件都有一个struct file结构,每个struct file结构都有一个struct address_space用于关联文件 和内存,就是在这个结构里面,有一棵树,用于保存所有与这个文件相关的的缓存页。 我们查找的时候,往往需要根据文件中的偏移量找出相应的页面,而基数树radix tree这种数据结构能够快 速根据一个长整型查找到其相应的对象,因而这里缓存页就放在radix基数树里面。//from 趣谈Linux操作系统 第30讲 - 文件缓存。
关于disk block size, disk sector size和memory page size
1. disk sector size is physical, 512B before 2010s, 4KB since then(对于硬盘厂商可以降低成本). 4KB 里面有两种(512e and 4Kn):
512e是指物理上是4KB,逻辑上是512B,读的时候读4KB, 返回需要的那512B。写的时候要先读4KB到buffer,把要写的512B写进buffer,再把4KB写进buffer。
4Kn是4K native, 读写都是按4K来做的。
disk block size is logical, as seen by file system. disk sector size <= disk block size。
disk block size: 512B in 1970s, 1024B for 1980s~1990s, default to 4096B in ~2000s
memory page size
Linux内核要实现内存映射,要求disk sector size <= disk block size <= memory page size
Now they are all 4KB, a magic number.
Maximum filesystem size = disk block size * 2 ^ {block_number_bits}
For 32-bit block numbers (Ext2), max 4,294,967,296 blocks.
Block groups - better locality for meta data and file content (对HDD可以提升locality和throughput,对SSD其实没什么用).
Superblock 和 group descriptors are backed up in some groups.
关于文件描述符和inode的关系
两个进程,一个写,一个读,打开同一个文件,我们可以看出它们的fd不一样,而且它们有独立的file table entry,并且它们的current file offset是不一样的。这两个file table entry指向同一个v-node和i-node。v-node是指VFS node,和i-node可以互相转化(指针互相指向对方)。
三层结构:
file descriptor table -> file entry table, w/ file offset -> inode table, w/ file size
注意
1. Inode table里面实际上时不放文件名的,下面的图省略了一层dentry。
2. 通常文件状态(i.e., File status flags)放在File table里面,只有FD_CLOEXEC放在File descriptors表里面。
目录Directory的主要功能就是把文件名映射成inode number。
趣谈Linux操作系统的小结
from 第29讲 - 虚拟操作系统
进程要想往文件系统里面读写数据,需要很多层的组件一起合作。具体是怎么合作的呢?我们一起来看一 看。
1. 在应用层,进程在进行文件读写操作时,可通过系统调用如sys_open、sys_read、sys_write等。
2. 在内核,每个进程都需要为打开的文件,维护一定的数据结构。
3. 在内核,整个系统打开的文件,也需要维护一定的数据结构。
4. Linux可以支持多达数十种不同的文件系统。它们的实现各不相同,因此Linux内核向用户空间提供了虚拟 文件系统这个统一的接口,来对文件系统进行操作。它提供了常见的文件系统对象模型,例如inode、 directory entry、mount等,以及操作这些对象的方法,例如inode operations、directory operations、 file operations等。
5. 然后就是对接的是真正的文件系统,例如我们上节讲的ext4文件系统。
6. 为了读写ext4文件系统,要通过块设备I/O层,也即BIO层。这是文件系统层和块设备驱动的接口。
7. 为了加快块设备的读写效率,我们还有一个缓存层。
8. 最下层是块设备驱动程序
总结:
1. 对于每一个进程,打开的文件都有一个文件描述符,在files_struct里面会有文件描述符数组。
2. 每个一个文件 描述符是这个数组的下标,里面的内容指向一个file结构,表示打开的文件。这个结构里面有这个文件对应 的inode,最重要的是这个文件对应的操作file_operation。如果操作这个文件,就看这个file_operation里面 的定义了。
3. 对于每一个打开的文件,都有一个dentry对应,虽然叫作directory entry,但是不仅仅表示文件夹,也表示 文件。它最重要的作用就是指向这个文件对应的inode。 如果说file结构是一个文件打开以后才创建的,dentry是放在一个dentry cache里面的,文件关闭了,他依 然存在,因而他可以更长期的维护内存中的文件的表示和硬盘上文件的表示之间的关系。
4. inode结构就表示硬盘上的inode,包括块设备号等。
5. 几乎每一种结构都有自己对应的operation结构,里面都是一些方法,因而当后面遇到对于某种结构进行处 理的时候,如果不容易找到相应的处理函数,就先找这个operation结构,就清楚了。
嵌入式文件系统详解_嵌入式 文件系统工作原理-优快云博客
7.1.1. 嵌入式文件系统概览 — ywg_dev_doc 0.1 文档
传统的ext2, ext3, ntfs等都是针对机械式硬盘设计的,用作Fash文件系统会很多的弊端。
嵌入式Linux一般用专门针对FLASH的文件系统。比较常用的有jffs2,yaffs2,logfs,ubifs。