静态文件
定义
静态文件是指内容固定不变的文件,在服务器上以固定的格式存储,每次被请求时,服务器直接将其内容发送给客户端,不经过任何动态处理。常见的静态文件类型包括 HTML 文件、CSS 文件、JavaScript 文件、图片文件(如 JPEG、PNG、GIF 等)、音频文件(如 MP3、WAV 等)和视频文件(如 MP4、AVI 等)。
存储方式
静态文件以二进制数据的形式存储在硬盘的物理扇区中。硬盘会将文件的数据分割成多个小块,存储在不同的扇区里。为了管理这些分散的数据块,文件系统会使用索引或链表等数据结构来记录文件数据块的存储位置。
特点
内容固定:无论何时何地被访问,文件内容始终保持一致,除非手动修改文件本身。
加载速度快:由于不需要服务器进行额外的处理,直接从服务器传输到客户端,所以加载速度相对较快,能够快速呈现给用户。
易于缓存:可以在客户端或中间代理服务器上进行缓存,再次访问时无需再次从服务器获取,进一步提高访问效率,减轻服务器负担。
应用场景
适用于展示固定内容的场景,如网站的首页、产品介绍页面、静态资源文件等,这些内容不依赖于用户的操作或实时数据变化。
inode(i 节点)
定义
inode(index node,索引节点)是 Unix/Linux 文件系统中的一种数据结构,用于存储文件的元数据信息,每个文件和目录在文件系统中都有一个对应的 inode。inode 是文件系统管理文件的核心,它记录了文件的各种属性和数据块的存储位置等信息。
存储内容
文件基本信息:包括文件的类型(普通文件、目录、设备文件等)、文件权限(读、写、执行权限)、文件所有者和所属组、文件的创建时间、修改时间和访问时间等。
文件数据块指针:inode 中包含了指向文件数据块在硬盘上存储位置的指针。通过这些指针,文件系统可以快速定位到文件的数据块,从而读取或写入文件内容。对于大文件,可能会使用多级索引来管理数据块。
作用
文件标识:在文件系统中,每个 inode 都有一个唯一的编号,文件系统通过 inode 编号来识别和管理文件。文件名只是文件的一个外部标识,而 inode 才是文件的真正标识。
文件数据管理:inode 记录了文件数据块的存储位置,使得文件系统能够高效地组织和管理文件的数据。当需要读取或写入文件时,文件系统可以根据 inode 中的指针快速定位到相应的数据块。
文件共享:多个文件名可以指向同一个 inode,实现文件的硬链接。这样,多个文件路径可以访问同一个文件内容,节省了磁盘空间。
内存中被打开的文件
概念
当用户或程序通过系统调用(如 open 函数)请求打开一个文件时,操作系统会将文件的相关信息从磁盘加载到内存中,此时文件就处于被打开的状态。内存中被打开的文件不仅仅包含文件的实际数据内容,还包括一系列用于管理和操作该文件的信息。
相关数据结构
文件表项(File Table Entry):操作系统通常会维护一个全局的文件表,每个被打开的文件在文件表中都有一个对应的表项。文件表项包含了文件的当前偏移量(即文件指针的位置,指示下一次读写操作的起始位置)、访问模式(只读、只写、读写等)以及指向 vnode 的指针等信息。
用户文件描述符表:每个进程都有自己的用户文件描述符表,该表记录了该进程打开的所有文件的描述符(非负整数)与文件表项的映射关系。文件描述符是进程访问被打开文件的标识,通过文件描述符,进程可以方便地对文件进行读写等操作。
作用
提高访问效率:将文件加载到内存中,避免了每次访问文件都要进行磁盘 I/O 操作,大大提高了文件的访问速度。
支持并发访问:多个进程或线程可以同时打开同一个文件,通过文件表项和用户文件描述符表的管理,操作系统可以协调不同进程对文件的访问,确保数据的一致性和完整性。
vnode(v 节点)
概念
vnode(virtual node,虚拟节点)是一种抽象的数据结构,用于在操作系统中统一表示不同类型的文件系统对象,包括普通文件、目录、设备文件等。它是文件系统和内核之间的一个接口层,使得内核可以以统一的方式处理不同文件系统中的文件和目录。
存储内容
文件系统类型信息:记录了该文件所属的文件系统类型,如 ext4、NTFS 等。这使得内核可以根据不同的文件系统类型调用相应的处理函数。
inode 信息:vnode 中包含了指向文件对应的 inode(在磁盘文件系统中,inode 存储了文件的元数据信息)的指针,通过这个指针,vnode 可以获取文件的各种属性,如文件权限、所有者、文件大小等。
操作函数指针:vnode 中定义了一组操作函数指针,这些函数指针指向了对该文件进行各种操作的具体实现函数,如读、写、打开、关闭等操作。不同的文件系统会为这些操作函数提供不同的实现。
作用
抽象文件系统差异:不同的文件系统(如 ext4、FAT32 等)有不同的实现方式和数据结构,vnode 提供了一个统一的抽象接口,使得内核可以以相同的方式处理不同文件系统中的文件,提高了内核的可移植性和可扩展性。
支持文件系统挂载:当一个文件系统被挂载到某个挂载点时,vnode 可以将该文件系统中的文件和目录与内核中的文件系统树进行关联,使得用户可以像访问本地文件一样访问挂载文件系统中的文件。
内存中被打开的文件与 vnode 的关系
关联桥梁:内存中被打开的文件的文件表项中包含了指向 vnode 的指针,通过这个指针,文件表项可以与 vnode 建立关联。当进程通过文件描述符访问被打开的文件时,操作系统首先通过文件描述符找到对应的文件表项,然后通过文件表项中的 vnode 指针找到对应的 vnode,进而通过 vnode 中的操作函数指针调用相应的文件操作函数。
数据共享与一致性:多个进程同时打开同一个文件时,它们的文件表项可能会指向同一个 vnode,这使得不同进程可以共享文件的元数据信息和操作函数。同时,vnode 还负责维护文件数据的一致性,确保不同进程对文件的操作不会相互冲突。
文件
概念:
文件是存储在计算机存储设备(如硬盘、U 盘等)上的一组相关数据的集合,它具有唯一的文件名和文件路径,以方便用户和程序对其进行访问和管理。文件可以包含各种类型的数据,如文本、图像、音频、视频等。
组成部分:
文件名:用于标识文件的名称,通常由用户在创建文件时指定。文件名可以包含字母、数字、下划线和一些特定的字符,但不能包含操作系统保留的字符。
文件扩展名:通常由点号(.)后跟几个字符组成,用于表示文件的类型。例如,.txt 表示文本文件,.jpg 表示图像文件,.mp3 表示音频文件等。操作系统和应用程序可以根据文件扩展名来关联相应的程序来打开和处理文件。
文件内容:文件中实际存储的数据,可以是文本内容、二进制数据或其他特定格式的数据。
作用:
文件是计算机系统中数据持久化存储的主要方式,它允许用户和程序在不同的时间和地点访问和处理数据。文件可以用于存储用户的文档、程序代码、配置信息、多媒体数据等各种类型的数据。
流
概念:
流是一种抽象的概念,它表示数据的流动或传输。在程序中,流可以看作是一个连续的字节序列,数据从数据源(如文件、网络连接、键盘输入等)流向数据目的地(如文件、网络连接、显示器输出等)。流提供了一种统一的方式来处理不同类型的数据源和数据目的地,而无需关心底层的具体实现细节。
分类:
输入流:用于从数据源读取数据到程序中。例如,从文件中读取数据、从网络连接中接收数据或从键盘读取用户输入等都属于输入流操作。
输出流:用于将程序中的数据写入到数据目的地。例如,将数据写入到文件中、将数据发送到网络连接或在显示器上输出数据等都属于输出流操作。
特点:
顺序性:流中的数据是按照顺序依次读取或写入的,通常不能随机访问流中的数据。如果需要随机访问数据,可能需要使用其他数据结构或文件访问方式。
缓冲性:为了提高数据传输的效率,流通常会使用缓冲区来暂存数据。当从输入流读取数据时,数据会先被读取到缓冲区中,然后程序再从缓冲区中获取数据。当向输出流写入数据时,数据会先被写入到缓冲区中,当缓冲区满或流被关闭时,数据才会被真正写入到数据目的地。
作用:
流提供了一种方便、灵活的方式来处理数据的输入和输出,它使得程序可以以统一的方式处理不同类型的数据源和数据目的地,提高了程序的可移植性和可扩展性。同时,流的缓冲机制也可以提高数据传输的效率,减少底层设备的访问次数。
文件是数据的存储实体,而流是数据传输的抽象概念,在实际应用中,经常会使用流来对文件进行读写操作,即将文件作为流的数据源或数据目的地,通过流来实现对文件中数据的读取和写入。
文本文件写入操作
#include <iostream>
#include <fstream>
#include <string>
int main() {
// 创建一个 ofstream 对象,用于向文件写入数据
std::ofstream outFile("example.txt");
// 检查文件是否成功打开
if (outFile.is_open()) {
// 写入文本内容
outFile << "Hello, this is a test file." << std::endl;
outFile << "This is the second line." << std::endl;
// 关闭文件
outFile.close();
std::cout << "File written successfully." << std::endl;
} else {
std::cerr << "Unable to open file for writing." << std::endl;
}
return 0;
}
std::ofstream 是用于输出文件流的类,借助构造函数传入文件名来打开文件。
is_open() 函数用于检查文件是否成功打开。
<< 运算符用于向文件写入数据。
close() 函数用于关闭文件。
文本文件读取操作
#include <iostream>
#include <fstream>
#include <string>
int main() {
// 创建一个 ifstream 对象,用于从文件读取数据
std::ifstream inFile("example.txt");
std::string line;
// 检查文件是否成功打开
if (inFile.is_open()) {
// 逐行读取文件内容
while (std::getline(inFile, line)) {
std::cout << line << std::endl;
}
// 关闭文件
inFile.close();
} else {
std::cerr << "Unable to open file for reading." << std::endl;
}
return 0;
}
std::ifstream 是用于输入文件流的类,通过构造函数传入文件名来打开文件。
std::getline() 函数用于逐行读取文件内容。
close() 函数用于关闭文件。
二进制文件写入操作
#include <iostream>
#include <fstream>
struct Person {
char name[50];
int age;
};
int main() {
Person p = {"John Doe", 30};
// 创建一个 ofstream 对象,以二进制模式打开文件
std::ofstream outFile("binary_example.bin", std::ios::binary);
// 检查文件是否成功打开
if (outFile.is_open()) {
// 写入二进制数据
outFile.write(reinterpret_cast<const char*>(&p), sizeof(Person));
// 关闭文件
outFile.close();
std::cout << "Binary file written successfully." << std::endl;
} else {
std::cerr << "Unable to open binary file for writing." << std::endl;
}
return 0;
}
std::ios::binary 标志用于以二进制模式打开文件。
write() 函数用于将二进制数据写入文件,需要把数据指针转换为 const char* 类型。
二进制文件读取操作
#include <iostream>
#include <fstream>
struct Person {
char name[50];
int age;
};
int main() {
Person p;
// 创建一个 ifstream 对象,以二进制模式打开文件
std::ifstream inFile("binary_example.bin", std::ios::binary);
// 检查文件是否成功打开
if (inFile.is_open()) {
// 读取二进制数据
inFile.read(reinterpret_cast<char*>(&p), sizeof(Person));
// 输出读取的数据
std::cout << "Name: " << p.name << std::endl;
std::cout << "Age: " << p.age << std::endl;
// 关闭文件
inFile.close();
} else {
std::cerr << "Unable to open binary file for reading." << std::endl;
}
return 0;
}
std::ios::binary 标志用于以二进制模式打开文件。
read() 函数用于从文件读取二进制数据,需要把数据指针转换为 char* 类型。