目录
[一 c语言接口](#一 c语言接口)
[二 认识文件系统调用](#二 认识文件系统调用)
[三 访问文件的本质](#三 访问文件的本质)
原理
1.文件=内容+属性
2.文件分为打开文件 和未打开文件
3.打开的文件由进程 打开--本质是研究进程和文件的关系
文件打开后属性一定加载到内存,他与进程1:n
操作系统内部存在在众多打开文件,管理方式为**先描述,再组织,**内核中,打开文件中必须包含自身的文件的打开对象,包含文件属性
4.没打开的文件在磁盘 上,我们需要关注在众多没打开文件中,++文件如何被分门别类的放置好--为进行增删查改快速找到文件。++
一 c语言接口
源码


需要注意"w"是覆盖性写入,而"a"是追加型写入
fopen 是C语言中用于打开文件的标准库函数,功能是创建一个文件指针并关联指定文件,常见用法如下:
基本格式
FILE *fopen(const char *filename, const char *mode);- filename :要打开的文件名(可含路径)
++路径及CWD,有觉得路径相对路径两种, const char *filename,****我们可以给完整路径(/home/ldg)或者给出文件名(test.c),进程会在他运行得当前路径为我们生成文件。++
- mode :文件打开模式(如 "r" 读、 "w" 写、 "a" 追加等)
常用模式及说明
读操作:
"r" :打开文本文件用于读取,文件需存在
"rb" :以二进制模式读取
写操作:
"w" :创建新文件写入,若文件存在则覆盖
"wb" :二进制模式写入
"a" :追加内容到文件末尾,文件不存在则创建
读写操作:
"r+" :可读可写,文件需存在
"w+" :创建新文件,可读可写(覆盖原文件)
返回值
- 成功时返回指向 FILE 结构体的指针
-失败时返回 NULL (需用 perror 等函数查看错误原因)
注意事项
打开文件后必须用 fclose 关闭,避免资源泄漏
二进制模式需加 b (如 "rb" ),跨平台更安全
处理中文路径时需注意编码问题(Windows下常用GBK)
C常见文件操作接口
cpp
//1. 打开与关闭文件
- fopen() :打开文件并返回文件指针
FILE *fp = fopen("file.txt", "r"); // 以只读模式打开文本文件
- fclose() :关闭文件并释放资源
fclose(fp); // 关闭文件指针fp
//2. 字符级读写
- fgetc() :从文件读取单个字符
int ch = fgetc(fp); // 读取一个字符,返回EOF表示读取失败
- fputc() :向文件写入单个字符
fputc('A', fp); // 向文件写入字符'A'
//3. 行级读写
- fgets() :读取一行字符串(含换行符)
char buf[100];
fgets(buf, 99, fp); // 读取最多99个字符到buf
- fputs() :写入字符串(不包含末尾'\0')
fputs("hello", fp); // 向文件写入"hello"
//4. 格式化读写
- fscanf() :按格式从文件读取数据
int num;
fscanf(fp, "%d", &num); // 从文件读取整数
- fprintf() :按格式向文件写入数据
fprintf(fp, "num = %d", 100); // 写入格式化字符串
//5. 块读写(二进制文件)
- fread() :读取二进制数据块
struct Data data;
fread(&data, sizeof(data), 1, fp); // 读取一个结构体
- fwrite() :写入二进制数据块
fwrite(&data, sizeof(data), 1, fp); // 写入一个结构体
//6. 文件定位
- fseek() :移动文件指针到指定位置
fseek(fp, 0, SEEK_SET); // 移动到文件开头(第二个参数:SEEK_SET=开头,SEEK_CUR=当前,SEEK_END=结尾)
- ftell() :获取当前文件指针位置
long pos = ftell(fp); // 返回当前偏移量(字节数)
- rewind() :重置文件指针到开头
rewind(fp); // 等价于fseek(fp, 0, SEEK_SET)
//7. 状态检查
- feof() :检查是否到达文件末尾
if (feof(fp)) printf("已到文件末尾");
- ferror() :检查文件操作是否出错
if (ferror(fp)) perror("文件操作错误");
- clearerr() :清除错误和EOF标志
clearerr(fp); // 重置错误状态
//8. 其他工具函数
- remove() :删除文件
remove("old.txt"); // 删除文件old.txt
- rename() :重命名文件
rename("old.txt", "new.txt"); // 重命名文件
//二进制文件操作需使用 "rb" / "wb" 模式(如 fopen("img.bin", "rb") )。
//- 操作文件前需检查指针是否为 NULL ,避免空指针错误。
//- 频繁读写时可配合 fflush() 刷新缓冲区(如 fflush(fp) 强制写入)。
二 认识文件系统调用
Linux下一位皆是文件
c语言默认启动会打开三个标准输入输出流(文件)

stdin(键盘文件) stdout stderr(显示器文件)
我们所说的文件 其实存放在磁盘 ,访问文件其实就是访问磁盘,是外设硬件
用户不能直接访问硬件,需要借助操作系统内部帮助(几乎所以得库只要是访问硬件设备,必须要封装系统调用!!)
介绍接口open
在Linux系统中,open 是用于文件操作的系统调用接口,属于POSIX标准,相比C语言标准库的 fopen 更底层,直接操作文件描述符。以下是其核心用法和解析:
- 函数原型与头文件

-
参数说明:
-
pathname :文件路径(绝对路径或相对路径)。
-
flags :打开文件的标志(必选,可通过**|**组合多个标志)。
-
mode :创建文件时的权限(仅当使用 O_CREAT 标志时有效)。
2. 常用flags标志
基础打开模式(必选其一)
-
O_RDONLY :只读模式(返回文件描述符为可读)。
-
O_WRONLY :只写模式。
-
O_RDWR :读写模式。
扩展标志(可组合使用)
-
O_CREAT :若文件不存在则创建(需配合 mode 参数)。
-
O_APPEND :追加写入(每次写入从文件末尾开始)。
-
O_TRUNC :若文件已存在,清空文件内容。
-
O_EXCL :与 O_CREAT 配合使用时,若文件已存在则打开失败。
-
O_NONBLOCK :非阻塞模式(适用于设备文件,如管道、套接字)。
3. mode参数(文件权限)
当使用 O_CREAT 时,需指定文件权限,通常用八进制表示:
-
示例: 0666 (所有者、组、其他用户均可读可写)。
-
实际权限会受当前进程的umask影响(如 umask 002 时,最终权限为 0666 & ~002 = 0664 )。
可以通过umask接口修改umask值

- 返回值与错误处理
-
成功:返回文件描述符(非负整数,默认最小可用值,如 3 ,标准输入/输出/错误占用 0/1/2 )。
-
失败:返回 -1 ,可通过 errno 变量或 perror() 查看错误原因(如 ENOENT 文件不存在、 EACCES 权限拒绝)。
- 核心操作示例
打开文件并写入

读取文件内容

- 与fopen的区别
|----------|-------------------------|-----------------------|
| 特性 | open (系统调用) | fopen (C标准库) |
| 接口层级 | 底层(直接操作文件描述符) | 高层(封装文件流指针) |
| 返回值 | 文件描述符(int类型) | FILE* 指针 |
| 缓冲机制 | 无标准缓冲(需手动管理) | 自带缓冲(提升I/O效率) |
| 跨平台性 | Linux/UNIX专用 跨平台 | (Windows/Linux等) |
| 错误处理 | 通过 errno 或 perror() | 通过返回值和 ferror() 等 |
- 其他相关系统调用
- close() :关闭文件描述符
cppclose(fd); // 成功返回0,失败返回-1
- lseek() :文件指针定位
cppoff_t new_pos = lseek(fd, 0, SEEK_END); // 移动到文件末尾
- read() / write() :读写数据
cppssize_t n = read(fd, buf, size); // 读取size字节到buf
- fcntl() :文件描述符控制(如设置非阻塞、获取/修改标志)
注意事项
文件描述符是进程级资源,不同进程可通过相同描述符操作文件。
二进制文件无需特殊标志(如 O_BINARY ),Linux系统不区分文本/二进制文件。
多线程环境中需注意文件描述符的线程安全(如共享描述符时加锁)。
三 访问文件的本质
一、文件描述符表的本质与作用
-
文件描述符(File Descriptor):是进程访问文件的唯一标识符 ,通常为非负整数(如0、1、2等)。
-
文件描述符表:每个进程都有独立的文件描述符表 ,表中存储了进程当前打开的所有**文件的映射关系,**每个表项对应一个文件描述符,指向内核中的"文件表项" 。
二、访问磁盘文件的核心流程
- 打开文件时的映射建立
-
进程调用 open() 函数打开磁盘文件时,内核会:
-
分配一个空闲的文件描述符(通常从最小未使用的整数开始,如3)。
从三开始的原因:stdin,stdout,stderr的占位(进程初始化阶段完成)
-
创建"文件表项",记录文件的当前偏移量、打开模式(读/写/追加等)、文件状态标志等信息。
-
将文件描述符与文件表项绑定 ,存入进程的文件描述符表。
- 通过文件描述符操作文件
-
进程调用 read() 、 write() 等函数时,只需传入文件描述符:
**- 内核根据文件描述符查找进程的文件描述符表,定位到对应的文件表项。 -
通过文件表项找到文件的"索引节点(Inode)"指针,Inode中存储了文件在磁盘上的物理位置、权限、类型等元数据。**
-
根据Inode信息访问磁盘物理块,完成读写操作。
- 关闭文件时的资源释放
-
进程调用 close() 函数时,内核会:
-
从文件描述符表中删除对应表项,释放文件描述符。
- 减少文件表项的引用计数,若计数为0,则释放文件表项及相关资源(如内存缓冲区)。
三、关键数据结构与内核层交互
- 三层数据结构关联:
进程文件描述符表 → 文件表项 → 索引节点(Inode)→ 磁盘物理块

-
文件表项:由内核维护,多个进程可通过不同文件描述符指向同一文件表项(如父子进程共享文件)。
-
Inode:每个文件唯一对应一个Inode,存储文件的物理地址映射(如块号数组)。
-
缓冲区的作用:
内核为文件操作设置缓冲区(如页缓存),读写操作先在内存缓冲区中完成,再由内核异步刷新到磁盘,提升I/O效率。
四、举个简单例子辅助理解
- 假设进程用 open("/data/file.txt", O_RDONLY) 打开文件:
-
内核分配文件描述符 fd=3 ,创建文件表项(记录偏移量0、读模式)。
-
进程调用 read(fd, buf, 100) 时,内核通过 fd=3 找到文件表项,根据Inode找到文件在磁盘的位置,从偏移量0读取100字节到 buf 。
-
读取后文件表项的偏移量更新为100,下次读取从该位置开始。
五、总结核心逻辑
文件描述符表是进程访问文件的"索引工具",通过它将用户空间的描述符映射到内核空间的文件控制结构,最终实现对磁盘文件的物理访问。这一机制确保了进程对文件的安全管理和高效操作。