Linux以文件的形式对计算机中的数据和硬件资源进行管理,也就是彻底的一切皆文件,反映在Linux的文件类型上就是:普通文件、目录文件(也就是文件夹)、设备文件、链接文件、管道文件、套接字文件(数据通信的接口)等等。而这些种类繁多的文件被Linux使用目录树进行管理, 所谓的目录树就是以根目录(/)为主,向下呈现分支状的一种文件结构。不同于纯粹的ext2之类的文件系统,我把它称为文件体系,一切皆文件和文件目录树的资源管理方式一起构成了Linux的文件体系,让Linux操作系统可以方便使用系统资源。

一、文件系统概述

1、linux文件系统模型

  由上而下主要分为用户层、VFS层、文件系统层、缓存层、块设备层、磁盘驱动层、磁盘物理层。
这里写图片描述

2、文件I/O与标准I/O

  文件IO又被称为不带缓冲的IO,“不带缓冲“是指每个读写操作都调用系统内核的一个系统调用。文件IO与标准IO的区别在于:

  • 标准IO默认采用缓冲机制,打开文件的同时,在内存中建立一个缓冲区;而文件IO一般不会建立缓冲区,需要手动创建;
  • 标准IO针对的是控制台、打印输出到屏幕等;文件IO主要是对文件操作、读写硬盘等;
  • 标准IO是跨平台的,二文件IO只适用于Unix/Linux平台。

3、文件描述符

  对内核而言,所打开的文件都是通过文件描述符引用,当打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符,该文件描述符对文件进行读写操作。
  通过将文件描述符0与标准输入相关联,将文件描述符1与标准输出相关联,将文件描述符2与标准错误相关联(STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO)。

4、缓冲机制

  基于流的操作最终都会以系统调用的形式进行IO操作,为了通过程序的运行效率,流对象通常会提供缓冲区,以减少系统调用的次数。

1. 全缓冲:直到缓冲区被填满,才调用系统IO函数。

2. 行缓冲:直到遇到换行符’\n’,才调用系统IO函数。

3. 无缓冲:没有缓冲区,数据立即读入或者输出到外存文件或设备上。

二、文件类型

  UNIX系统的大多数文件是普通文件或目录,但是也有另外一些文件类型。文件类型包括如下几种:

1、普通文件(-)

  • 从Linux的角度来说,类似mp4、pdf、html这样应用层面上的文件类型都属于普通文件;
  • Linux用户可以根据访问权限对普通文件进行查看、更改和删除。

2、目录文件(d,directory file)

  • 目录文件对于用惯Windows的用户来说不太容易理解,目录也是文件的一种;
  • 目录文件包含了各自目录下的文件名和指向这些文件的指针,打开目录事实上就是打开目录文件,只要有访问权限,你就可以随意访问这些目录下的文件(普通文件的执行权限就是目录文件的访问权限),但是只有内核的进程能够修改它们;
  • 虽然不能修改,但是我们能够通过vim去查看目录文件的内容。

3、块设备文件(b,block)和字符设备文件(c,char)

  • 这些文件一般隐藏在/dev目录下,在进行设备读取和外设交互时会被使用到;
  • 比如磁盘光驱就是块设备文件,串口设备则属于字符设备文件;
  • 系统中的所有设备要么是块设备文件,要么是字符设备文件,无一例外。

4、符号链接(l,symbolic link)

  • 这种类型的文件类似Windows中的快捷方式,是指向另一个文件的间接指针,也就是我们常说的软链接。

5、FIFO(p,pipe)

  • 管道文件主要用于进程间通讯。比如使用mkfifo命令可以创建一个FIFO文件,启用一个进程A从FIFO文件里读数据,启动进程B往FIFO里写数据,先进先出,随写随读。

6、套接字(s,socket)

  • 用于进程间的网络通信,也可以用于本机之间的非网络通信;
  • 这些文件一般隐藏在/var/run目录下,证明着相关进程的存在。

使用ls -l 命令 也可以查看文件的类型:

# ls -l
总用量 40
drwxr-xr-x 2 root   root    4096 Mar 30 16:42 test
-rw-r--r-- 1 root   root     872 Mar 30 15:04 test.c

三、文件系统结构与工作原理

  ext4文件系统的结构:
这里写图片描述

引导块:为磁盘分区的第一个块,记录文件系统分区的一些信息,,引导加载当前分区的程序和数据被保存在这个块中,一般占用2kB。

超级块:用于存储文件系统全局的配置参数(譬如:块大小,总的块数和inode数)和动态信息(譬如:当前空闲块数和inode数),其处于文件系统开始位置的1k处,所占大小为1k。为了系统的健壮性,最初每个块组都有超级块和组描述符表(以下将用GDT)的一个拷贝,但是当文件系统很大时,这样浪费了很多块(尤其是GDT占用的块多),后来采用了一种稀疏的方式来存储这些拷贝,只有块组号是3, 5 ,7的幂的块组(譬如说1,3,5,7,9,25,49…)才备份这个拷贝。通常情况下,只有主拷贝(第0块块组)的超级块信息被文件系统使用,其它拷贝只有在主拷贝被破坏的情况下才使用。

块组描述符:GDT用于存储块组描述符,其占用一个或者多个数据块,具体取决于文件系统的大小。它主要包含块位图,inode位图和inode表位置,当前空闲块数,inode数以及使用的目录数(用于平衡各个块组目录数),具体定义可以参见ext3_fs.h文件中struct ext3_group_desc。每个块组都对应这样一个描述符,目前该结构占用32个字节,因此对于块大小为4k的文件系统来说,每个块可以存储128个块组描述符。由于GDT对于定位文件系统的元数据非常重要,因此和超级块一样,也对其进行了备份。GDT在每个块组(如果有备份)中内容都是一样的,其所占块数也是相同的。从上面的介绍可以看出块组中的元数据譬如块位图,inode位图,inode表其位置不是固定的,当然默认情况下,文件系统在创建时其位置在每个块组中都是一样的,如图2所示(假设按照稀疏方式存储,且n不是3,5,7的幂)

块组:每个块组包含一个块位图块,一个 inode 位图块,一个或多个块用于描述 inode 表和用于存储文件数据的数据块,除此之外,还有可能包含超级块和所有块组描述符表(取决于块组号和文件系统创建时使用的参数)。下面将对这些元数据作一些简要介绍。

块位图:块位图用于描述该块组所管理的块的分配状态。如果某个块对应的位未置位,那么代表该块未分配,可以用于存储数据;否则,代表该块已经用于存储数据或者该块不能够使用(譬如该块物理上不存在)。由于块位图仅占一个块,因此这也就决定了块组的大小。

Inode位图:Inode位图用于描述该块组所管理的inode的分配状态。我们知道inode是用于描述文件的元数据,每个inode对应文件系统中唯一的一个号,如果inode位图中相应位置位,那么代表该inode已经分配出去;否则可以使用。由于其仅占用一个块,因此这也限制了一个块组中所能够使用的最大inode数量。

Inode表:Inode表用于存储inode信息。它占用一个或多个块(为了有效的利用空间,多个inode存储在一个块中),其大小取决于文件系统创建时的参数,由于inode位图的限制,决定了其最大所占用的空间。

以上这几个构成元素所处的磁盘块成为文件系统的元数据块,剩余的部分则用来存储真正的文件内容,称为数据块,而数据块其实也包含数据和目录。

读取文件的过程:

1. 根据文件所在目录的inode信息,找到目录文件对应数据块;

2. 根据文件名从数据块中找到对应的inode节点信息;

3. 从文件inode节点信息中找到文件内容所在数据块块号;

4. 读取数据块内容。

这里写图片描述

四、目录结构

/ (root 文件系统) : root 文件系统是文件系统的顶级目录。它必须包含在挂载其它文件系统前需要用来启动 Linux 系统的全部文件。它必须包含需要用来启动剩余文件系统的全部可执行文件和库。文件系统启动以后,所有其他文件系统作为 root 文件系统的子目录挂载到标准的、预定义好的挂载点上。

/bin :目录包含用户的可执行文件。

/boot:包含启动 Linux 系统所需要的静态引导程序和内核可执行文件以及配置文件。

/dev:该目录包含每一个连接到系统的硬件设备的设备文件。这些文件不是设备驱动,而是代表计算机上的每一个计算机能够访问的设备。

/etc:包含主机计算机的本地系统配置文件。

/home:主目录存储用户文件,每一个用户都有一个位于 /home 目录中的子目录(作为其主目录)。

/lib:包含启动系统所需要的共享库文件。

/media:一个挂载外部可移动设备的地方,比如主机可能连接了一个 USB 驱动器。

/mnt:一个普通文件系统的临时挂载点(如不可移动的介质),当管理员对一个文件系统进行修复或在其上工作时可以使用。

/opt:可选文件,比如供应商提供的应用程序应该安装在这儿。

/sbin:系统二进制文件。这些是用于系统管理的可执行文件。

/tmp:临时目录。被操作系统和许多程序用来存储临时文件。用户也可能临时在这儿存储文件。注意,存储在这儿的文件可能在任何时候在没有通知的情况下被删除。

/usr:该目录里面包含可共享的、只读的文件,包括可执行二进制文件和库、man 文件以及其他类型的文档。

/var:可变数据文件存储在这儿。这些文件包括日志文件、MySQL 和其他数据库的文件、Web 服务器的数据文件、邮件以及更多。

五、与目录结构相关的函数

struct stat
{
	dev_t         st_dev;      //文件的设备编号
    ino_t         st_ino;      //节点
    mode_t        st_mode;     //文件的类型和存取的权限
    nlink_t       st_nlink;    //连到该文件的硬连接数目,刚建立的文件值为1
    uid_t         st_uid;       //用户ID
    gid_t         st_gid;       //组ID
    dev_t         st_rdev;   //若此文件为设备文件,则为其设备编号
    off_t         st_size;      //文件字节数(文件大小)
    unsigned long st_blksize;   //块大小(文件系统的I/O 缓冲区大小)
    unsigned long st_blocks;    //块数
    time_t        st_atime;     //最后一次访问时间
    time_t        st_mtime;     //最后一次修改时间
    time_t        st_ctime;     //最后一次改变时间(指属性)
};
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
/*
功能:通过文件名file_name获取文件信息,并保存在buf所指的结构体stat中。
返回值:执行成功则返回0,失败返回-1。
*/
int stat(const char *file_name, struct stat *buf);

/*
功能:打开一个目录。
返回值:成功则返回DIR*型态的目录流, 打开失败则返回NULL。
*/
DIR* opendir(constchar * path );

/*
功能:读取目录,需要循环读取dp中的文件和目录,
	每读取一个文件或目录都返回一个dirent结构体指针。
返回值:dirent结构体指针。
*/
struct dirent* readdir(DIR* dp);  

/*
功能:用来设置参数dp目录流目前的读取位置为原来开头的读取位置。
*/
void rewinddir(DIR *dp);

/*
功能:关闭参数dp所指的目录流。
返回值:关闭成功则返回0,,失败返回-1。
*/
int closedir(DIR*dp);

/*
功能:获取当前dp位置。
返回值:返回目录流dp的当前位置,
		此返回值代表距离目录文件开头的偏移量,有错误发生时返回-1。
*/
long int telldir(DIR *dp);

/*
功能:用来设置参数dp目录流当前的读取位置,在调用readdir()时便从此
	新位置开始读取。参数loc代表距离目录文件开头的偏移量。
*/
void seekdir(DIR *dp,long int loc);

【Demo(遍历目录下的所有文件和文件夹)】:

#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{    
	if (2 != argc)    
	{        
		printf("Usage: ListFile SourceFolder ");
		return 1;    
	}    
	DIR* pDir = NULL;    
	struct dirent* ent = NULL;    
	pDir = opendir(argv[1]);    
	if (NULL == pDir)
	{        
		printf("Source folder not exists!");
		return 1;    
	}    
	while (NULL != (ent=readdir(pDir)))    
	{      
	    printf("%s ", ent->d_name);  
	}    
	closedir(pDir);  
	pDir = NULL;    
	ent = NULL;    
	return 1;
}

参考:https://blog.csdn.net/xingyue0422/article/details/78395858
https://www.cnblogs.com/bellkosmos/p/detail_of_linux_file_system.html
https://blog.csdn.net/new0801/article/details/63687127
https://blog.csdn.net/iteye_19319/article/details/81396884
https://blog.csdn.net/yuan_hong_wei/article/details/50324667

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐