【Linux】linux基础IO(c语言程序接口,常用文件调用详解)
·
本文是小编巩固自身而作,如有错误,欢迎指出!
目录
一、IO共识原理
在正式学习IO之前,我们先回忆一下之前学习的关于文件的知识:
文件 = 内容 + 属性
文件分为打开的文件和没打开的文件
对于打开的文件:谁来打开?其实是由进程打开,本质是研究进程和文件的关系
对于没打开的文件:是放在哪里?其实是放在磁盘上。我们最关注的是什么问题?其实也就是是文件的存储问题,即没有被打开的文件非常多,那么文件如何被分门别类的放置好?文件被分门别类的放置好后,进而我们就可以快速的对文件进行增删查改,也就是快速的找到文件
二、C语言文件接口
fopen/fclose
函数原型
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
- pathname:文件路径,可以是相对路径或绝对路径。
- mode:打开模式,常用选项包括:
"r":只读,文件必须存在"w":只写,如果文件存在则清空,不存在则创建"a":追加写,如果文件不存在则创建"r+":读写,文件必须存在"w+":读写,文件存在则清空,不存在则创建"a+":读写,写入操作追加到文件末尾
#include <stdio.h>
int fclose(FILE *stream);
- stream:指向
fopen返回的FILE* - 返回值:
- 0:关闭成功
- EOF:关闭失败
这些基础用法我们在之前就已经学习过了,现在重提的原因主要在于,我们今天要重点了解文件换缓冲机制
fopen 返回的 FILE* 会有缓冲区(默认全缓冲或行缓冲),所以写入磁盘不一定是即时的。
当使用 fwrite 或 printf 写入文件时,数据先进入用户级缓冲区(由 FILE* 管理),并不会立即写入磁盘。
fclose 会确保缓冲区的数据被写入磁盘,从而保证文件内容完整
我们先在看一看下面的示例代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main() {
const char *filename = "buffer_test.txt";
const char *msg = "Hello, Linux I/O buffer!\n";
// 打开文件用于写入
FILE *fp = fopen(filename, "w");
if (!fp) {
perror("fopen");
return 1;
}
// 写入缓冲区,但不调用 fclose
fwrite(msg, strlen(msg), 1, fp);
printf("Data written to buffer, but not yet flushed to disk.\n");
printf("Check the file content with: cat %s\n", filename);
// 这里注释掉 fclose 查看缓冲区效果
// fclose(fp);
// 如果你取消上面 fclose 注释,缓冲区会被刷新到磁盘
printf("Now closing file to flush buffer...\n");
fclose(fp);
printf("File closed. Buffer flushed. Check the file content again.\n");
return 0;
}
- 打开文件
- 使用
fopen("buffer_test.txt", "w")打开一个文件用于写入 - 得到一个
FILE*指针,内部维护一个 用户级缓冲区
- 使用
- 写入数据到缓冲区
- 使用
fwrite(msg, strlen(msg), 1, fp)将字符串写入缓冲区 - 注意,这时候 数据还未写入磁盘,仍在内存中
- 使用
- 输出提示
- 提示用户数据已写入缓冲区,但尚未刷新到磁盘
- 可以用
cat buffer_test.txt查看文件内容,此时可能为空
- 关闭文件(可选)
- 调用
fclose(fp) fclose会 刷新缓冲区:把缓冲区内的数据写入磁盘- 同时释放文件描述符,关闭文件
- 调用
- 观察效果
- 不调用 fclose:文件内容可能为空(数据仍在缓冲区)
- 调用 fclose:数据写入磁盘,文件显示完整内容
fwrite
#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
- ptr:指向要写入的数据起始地址
- size:每个元素的字节数
- nmemb:要写入元素的个数
- stream:目标文件的
FILE* - 返回值:成功写入的元素个数(不是字节数)
fwrite写入的是 用户缓冲区,不是直接写磁盘fclose会 刷新缓冲区并关闭文件- 可以用
fflush(fp)代替fclose实现中途刷新缓冲区
fprintf
#include <stdio.h>
int fprintf(FILE *stream, const char *format, ...);
- stream:目标文件的
FILE* - format:格式化字符串
- ...:对应格式的变量
- 返回值:成功写入的字符数,如果出错返回负值
#include <stdio.h>
int main() {
FILE *fp = fopen("fprintf_test.txt", "w");
if (!fp) {
perror("fopen 出错");
return 1;
}
int a = 42;
const char *name = "Linux I/O";
// 写入格式化内容
fprintf(fp, "这是一个测试:变量 a = %d, name = %s\n", a, name);
// 刷新缓冲区并关闭文件
fclose(fp);
printf("已写入 fprintf_test.txt,缓冲区已刷新。\n");
return 0;
}
输出重定向 >和追加重定向 >>
1. 输入重定向 >
作用:将程序的标准输出(stdout)写入文件,如果文件已存在,会覆盖原文件内容

2. 追加重定向 >>
作用:将程序的标准输出(stdout)写入文件,如果文件已存在,内容追加到末尾

三、文件系统调用
文件系统调用概念
- 文件系统调用是 操作系统提供的接口,允许程序访问磁盘上的文件和设备
- 与 C 标准库函数(fopen/fwrite/fclose) 不同,系统调用是 直接与内核交互 的
比特位方式传递标志位(Bit Flags)
- 在 Linux 系统调用(如
open)中,通常会有多个选项 - 为了节省参数,同时可以组合多个选项,Linux 使用 比特位(bit) 表示每个选项
- 每个选项对应 整数中的一位(或多位),通过 按位或
|来组合
#define O_RDONLY 0x0000 // 0000 0000
#define O_WRONLY 0x0001 // 0000 0001
#define O_RDWR 0x0002 // 0000 0010
#define O_CREAT 0x0040 // 0100 0000
#define O_APPEND 0x0400 // 0100 0000 0000
int flags = O_WRONLY | O_CREAT | O_APPEND;
内存中的二进制表示:
0000 0000 0000 0001 (O_WRONLY)
0000 0000 0100 0000 (O_CREAT)
0100 0000 0000 0000 (O_APPEND)
-------------------
0100 0000 0100 0001 (组合后的 flags)
- 节省参数:一个整数可以表示多个选项
- 易于扩展:新选项只需分配新的一位
- 组合灵活:用
|组合,用&检查
open
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
- pathname:文件路径
- flags:打开方式,例如:
O_RDONLY只读O_WRONLY只写O_RDWR读写O_CREAT文件不存在则创建O_APPEND追加写O_TRUNC打开时清空文件
- mode:权限位(仅在创建文件时使用,如 0666)
- 返回值:文件描述符 fd(>=0 表示成功,-1 表示失败)
close
#include <unistd.h>
int close(int fd);
- fd:文件描述符,由
open或其他系统调用返回 - 返回值:
0→ 成功-1→ 出错(并设置errno)
注意事项
- 不要重复关闭同一个 fd
- 重复调用
close(fd)会导致未定义行为
- 重复调用
- fd 会被操作系统重用
- 下一个
open可能会分配相同的 fd
- 下一个
- 和库函数 fclose 的区别
close(fd)只关闭 fdfclose(FILE* fp)先刷新缓冲区再调用close
write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
- fd:文件描述符,由
open或其他系统调用返回 - buf:指向要写入的数据内存地址
- count:写入字节数
- 返回值:实际写入的字节数,如果出错返回 -1 并设置 errno
| 特性 | write | fwrite |
|---|---|---|
| 缓冲区 | 内核缓冲区 | 用户缓冲区 + 内核缓冲区 |
| 调用层级 | 系统调用(内核) | C 标准库封装(调用 write) |
| 输出类型 | 文件描述符 fd | FILE*(可以是 stdout、stderr 或文件) |
| 刷新控制 | 立即写入内核缓冲区 | 需要 fflush 或 fclose 才写入内核 |
read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
- fd:文件描述符,由
open或其他系统调用返回 - buf:指向内存缓冲区,用于存储读取的数据
- count:要读取的最大字节数
- 返回值:
- 成功 → 实际读取的字节数
- 到文件末尾 → 返回 0
- 出错 → 返回 -1 并设置 errno
四、访问文件的本质
在 Linux 中,“访问文件”不仅仅是打开文件或读写文件,而是操作 内核中的文件系统和文件描述符
文件是内核的数据结构
- 文件在 Linux 内核中不仅是磁盘上的字节流,也对应内核维护的 文件结构:
struct file:内核维护每个打开文件的状态- 包含:
- 文件指针(读写偏移位置)
- 访问权限(只读/只写/读写)
- 文件状态(打开、追加、锁定等)
- 关联的 inode(文件的元信息,如大小、权限、设备类型)
文件在磁盘上的字节 + 内核维护的元信息 = 文件的本质
文件描述符(fd)
- 用户程序通过 文件描述符 fd 操作文件
- fd 是 一个索引或句柄,指向内核维护的
struct file - Linux 默认:
0→ stdin1→ stdout2→ stderr
- 每次
open返回的 fd,其实就是 指向内核文件表的索引
文件操作 = 通过 fd 间接操作内核文件结构
核心总结
- 文件不仅是磁盘上的数据,更是 内核维护的数据结构 + 元信息
- 用户程序访问文件 = 通过 fd 间接操作内核文件结构
- 所有 I/O(
read/write/fread/fwrite/printf)本质都是 数据在用户缓冲区 ↔ 内核缓冲区 ↔ 磁盘之间流动 - 文件描述符、缓冲区、系统调用三者构成文件访问的完整链路
本次分享就到这里结束了,后续会继续更新,感谢阅读!
更多推荐



所有评论(0)