一、知识点归纳
1. 1、文件操作级别
文件操作分为五个级别,按照从低到高的顺序排列如下:
(1)硬件级别: 硬件级别的文件操作包括:
- fdisk: 用于将硬盘、U盘或SD卡等存储设备分区。
- mkfs: 用于格式化磁盘分区,为文件系统做好准备。
- fsck: 用于检查和维修文件系统的一致性。
- 碎片整理: 用于压缩文件系统中的文件,以优化存储空间利用率。这些工具主要针对系统管理员和维护人员,普通用户通常不需要直接使用它们。
(2)操作系统内核中的文件系统函数: 操作系统内核提供了一组文件系统相关的函数,这些函数在内核层面支持基本文件操作,例如挂载、创建目录、读写文件等。
(3)系统调用: 用户模式程序使用系统调用来访问内核函数。例如,open()、read()、write()、lseek()等系统调用允许用户进程与文件系统交互。
(4)I/O库函数: C语言提供了一系列标准的I/O库函数,如fopen()、fread()、fwrite()、fprintf()等,这些函数封装了系统调用,提供更高级别的文件操作接口,使用户更方便地读写文件。
(5)用户命令: 用户可以使用操作系统的命令行工具来执行文件操作,例如mkdir、ls、cp、mv等。这些命令通常调用了底层的库函数,最终发出系统调用来执行文件操作。
(6)sh脚本: 用户可以编写脚本来批量执行文件操作,这些脚本可以包含各种文件系统操作和控制结构,如if、for、while等。脚本语言如sh、Perl、Python等广泛用于文件操作。
1.2、文件I/O操作
文件I/O操作涉及用户进程与文件系统之间的数据传输和交互。以下是文件I/O操作的主要步骤:
- 用户程序通过文件流(例如fopen())打开文件,创建一个FILE结构体,包含文件描述符fd、缓冲区fbuf等信息。
- 使用fread()和fwrite()等库函数,用户程序可以在用户空间中读取和写入数据,这些函数会封装底层的系统调用,提供更高级别的文件操作接口。
- 库函数会将数据从用户缓冲区fbuf复制到内核缓冲区,如果需要的话,会调用read()和write()系统调用来读写文件。
- 内核中的文件系统函数根据文件描述符fd和文件系统结构来处理文件读写请求,管理文件的逻辑块和物理块的映射关系。
- 内核使用I/O缓冲区高速缓存来减少磁盘I/O的次数,提高效率。
- 设备I/O由设备驱动程序的上半部分(start_io())和下半部分(中断处理程序)来处理,实现数据的读取和写入。
1.3、低级别文件操作
低级别文件操作包括分区和格式化。以下是一些相关的步骤和命令:
- 分区: 存储设备可以被分为多个逻辑单元,称为分区。分区通常由硬盘分区工具(如fdisk)创建。
- 分区表: 分区表位于存储设备的主引导记录(MBR)中,通常包含4个分区条目。每个分区条目定义了分区的位置、大小和文件系统类型。
- 扩展分区: 扩展分区是一种特殊类型的分区,可以包含更多的逻辑分区。扩展分区本身不能直接用于存储文件,但可以包含逻辑分区。
- 格式化分区: 格式化是为分区准备文件系统的过程。在Linux中,可以使用mkfs命令(例如mke2fs)来格式化分区。
- 挂载分区: 格式化后的分区可以挂载到现有文件系统中的目录,以使其可用于存储文件。使用mount命令来挂载分区。
- 卸载分区: 在使用分区后,可以使用umount命令将其卸载,以使其不再可用。
这些低级别文件操作通常由系统管理员执行,用于准备存储设备和文件系统以供使用。
当涉及到EXT2文件系统时,以下是相关知识点的总结和扩充:
1.4、EXT2文件系统简介:
- 历史:EXT2文件系统是Linux早期的文件系统,于1993年首次发布。它一直被广泛使用,直到后续版本引入了EXT3和EXT4。
- EXT3和EXT4:EXT3是EXT2的扩展,引入了日志文件以提高文件系统的稳定性。EXT4是EXT3的进一步扩展,引入了更大的磁盘块和性能改进。
EXT2文件系统数据结构:
-
引导块:EXT2的引导块(Block#0)用于引导操作系统,不用于文件系统操作。
-
超级块:超级块(Block#1)包含关于整个文件系统的关键信息。其中一些重要字段包括:
s_inodes_count
:索引节点数量。s_blocks_count
:磁盘块数量。s_free_blocks_count
:可用的空闲块数量。s_free_inodes_count
:可用的空闲索引节点数量。s_first_data_block
:第一个数据块的编号。s_log_block_size
:块大小的对数表示。s_blocks_per_group
:每个块组的块数量。s_inodes_per_group
:每个块组的索引节点数量。s_magic
:文件系统类型的幻数(0xEF53表示EXT2/3/4)。
-
块组描述符:块组描述符块(Block#2)用于描述块组的属性,包括块位图、索引节点位图和索引节点表的位置。
-
位图:
- 块位图(Bmap)(Block#8):用于表示磁盘块的分配情况,0表示空闲,1表示已分配。
- 索引节点位图(Imap)(Block#9):用于表示索引节点的分配情况,0表示空闲,1表示已分配。
-
索引节点:索引节点是文件的数据结构,每个文件都有一个相关联的索引节点,其中包含有关文件的元数据和数据块的信息。主要字段包括:
i_mode
:文件类型和访问权限。i_uid
和i_gid
:文件的所有者和所属组。i_size
:文件的大小(以字节为单位)。- 时间字段:包括
i_atime
(访问时间)、i_ctime
(创建时间)和i_mtime
(修改时间)。 i_links_count
:硬链接数量。i_blocks
:文件占用的512字节扇区数。i_block
:指向文件数据块的指针,包括直接块、间接块、双重间接块和三重间接块。
-
目录条目:EXT2中的目录由目录条目组成,每个目录条目包括文件名、索引节点号和其他元数据。目录条目的结构包括:
inode
:关联文件的索引节点号。rec_len
:该目录条目的长度。file_type
:文件类型,例如文件或目录。name
:文件或目录的名称。
1.5、系统调用概述:
系统调用(Syscall)是操作系统提供的一种机制,允许用户程序以有限的权限执行需要特权的操作。这些操作必须在内核模式(Kmode)下执行,而用户程序通常在用户模式(Umode)下运行。
Syscall 是用户程序与操作系统之间的接口,允许用户程序请求执行各种操作,如文件操作、进程管理、内存管理等。
1.6、系统调用手册页:
系统调用的用法和参数通常在系统调用手册页中进行了详细描述,这些手册页通常存储在操作系统的特定目录中。可以使用man命令查看系统调用手册页,如man 2 stat用于查看 stat 系统调用的手册页。
手册页通常提供了系统调用的语法、参数说明、返回值和错误代码等重要信息。
1.7、使用系统调用进行文件操作:
系统调用是通过程序来发起的,其用法类似于普通函数调用。每个系统调用都有一个唯一的编号,通常是第一个参数,然后根据编号将请求路由到内核中的相应函数。
一些常见的文件操作系统调用包括 open(打开文件)、read(读取文件)、write(写入文件)、close(关闭文件)、stat(获取文件状态信息)等。
系统调用的返回值通常用于指示操作的成功或失败,成功时返回0,失败时返回-1。错误信息通常通过 errno 变量传递,可以查找错误原因。
1.8、链接文件:
在Unix/Linux系统中,可以使用链接文件来创建多个路径名指向同一个文件。有两种类型的链接:硬链接和软链接(符号链接)。
硬链接是多个文件路径名指向相同的物理文件数据块,它们共享相同的索引节点。硬链接仅适用于普通文件,不适用于目录。
软链接是一个新的文件,它包含指向目标文件的路径名。软链接可以指向文件、目录等,并且即使目标文件不存在也不会损坏系统。
链接文件的使用可以提高文件系统的灵活性和管理效率。
1.9、stat系统调用:
stat
系统调用用于获取文件的状态信息,包括文件的权限、大小、链接数、所有者、组等信息。它需要文件的路径名作为参数,并将文件信息填充到一个struct stat
结构中。lstat
与stat
类似,但对于符号链接文件,它统计的是链接文件本身而不是链接所引用的文件。fstat
用于获取已经打开文件的状态信息,需要文件描述符作为参数。struct stat
结构包含了文件的各种属性,如文件大小、权限、所有者信息以及访问、修改、更改时间等。
1.10、opendir-readdir 函数:
- 目录也被视为文件,但其内容具有特定的格式。为了读取目录中的内容,可以使用
opendir
和readdir
函数。 opendir
打开一个目录,返回一个指向目录的指针,然后可以使用readdir
函数来读取目录中的条目。readdir
返回一个struct dirent
结构体,其中包含了目录中一个文件或子目录的信息,包括文件名、inode 号等。
readlink 函数:
- 在Linux中,
readlink
系统调用用于读取符号链接文件的内容,即链接所指向的文件或目录的路径。 readlink
需要传递符号链接文件的路径名作为参数,以及一个缓冲区用于存储链接的目标路径名,以及缓冲区的大小。
open-close-lseek 系统调用:
open
系统调用用于打开文件,它接受文件路径、打开标志(例如,读、写、追加等)和文件模式作为参数,并返回一个文件描述符。close
系统调用用于关闭打开的文件描述符,释放系统资源。read
系统调用用于从文件描述符读取数据,将数据存储到用户提供的缓冲区中,并返回实际读取的字节数。write
系统调用用于向文件描述符写入数据,将数据从用户提供的缓冲区中写入文件,并返回实际写入的字节数。lseek
系统调用用于重新定位文件描述符的读/写偏移量,可以用于在文件中进行随机访问。
二、问题及解决
创建循环设备步骤中出现了“资源忙碌的”报错提示,通过询问ChatGpt解决。
三、ChatGpt提问
四、实践及代码托管
创建循环设备
卸载分区断开设备