Linux:从零实现简易IO缓冲区:模拟C标准库文件操作
·
目录
哈喽,编程搭子们!😜 又到了沉浸式敲代码的快乐时间~把生活调成「代码模式」,带着满满的热爱钻进编程的奇妙世界——今天也要敲出超酷的代码,冲鸭!🚀
✨ 我的博客主页:喜欢吃燃面
📚 我的专栏(持续更新ing):
《C语言》 |
《C语言之数据结构》 |
《C++》 |
《Linux学习笔记》
💖 超感谢你点开这篇博客!真心希望这些内容能帮到正在打怪升级的你~如果有任何想法、疑问,或者想交流学习心得,都欢迎留言/私信,咱们一起在编程路上互相陪伴、共同进步呀!
在C语言中,文件IO操作(如fopen、fputs、fflush)之所以高效,核心是依赖内存缓冲区减少磁盘IO次数。本文将从零实现一个极简版的IO缓冲区,模拟标准库的核心逻辑,帮助理解缓冲区的工作原理。
一、核心原理
程序写入数据时,系统不会直接写磁盘(磁盘IO速度远慢于内存),而是先写入内存缓冲区:
- 缓冲区满/程序退出/主动调用
fflush时,数据才会刷入磁盘; - 行缓冲模式下,遇到
\n会触发即时刷新; - 全缓冲模式下,仅缓冲区满时刷新。
缓冲模式对比
二、完整实现代码
1. 头文件:mystdio.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
// 缓冲区大小定义
#define BUFFER_SIZE 1024
// 刷新模式宏
#define LINE_BUFFER 1 // 行缓冲(遇\n刷新)
#define FULL_BUFFER 2 // 全缓冲(满才刷新)
#define TRY_FLUSH 1 // 尝试刷新
#define MUST_FLUSH 2 // 强制刷新
// 文件权限(open系统调用必需)
#define MODE 0644
// 自定义文件结构体(模拟标准库FILE)
typedef struct _myFILE {
int fd; // 系统文件描述符
char outbuffer[BUFFER_SIZE]; // 输出缓冲区
int pos; // 缓冲区当前写入位置
int flush_mode; // 缓冲模式
} myFILE;
// 函数声明
myFILE* myfopen(const char* pathname, const char* mode);
int myfputs(const char* str, myFILE* fp);
void myfflush(myFILE* fp);
void myfclose(myFILE* fp);
static void myfflushcore(myFILE* fp, int flag);
数据结构内存布局
2. 实现文件:mystdio.c
#include "mystdio.h"
// 自定义fopen:创建文件结构体+打开文件
myFILE* myfopen(const char* pathname, const char* mode) {
int fd = -1;
int flags = 0;
// 解析打开模式
if (strcmp(mode, "r") == 0) {
flags = O_RDONLY;
} else if (strcmp(mode, "w") == 0) {
flags = O_WRONLY | O_CREAT | O_TRUNC;
} else if (strcmp(mode, "a") == 0) {
flags = O_WRONLY | O_CREAT | O_APPEND;
} else {
return NULL;
}
// 打开文件
fd = open(pathname, flags, MODE);
if (fd < 0) {
perror("open failed");
return NULL;
}
// 初始化自定义文件结构体
myFILE* fp = (myFILE*)malloc(sizeof(myFILE));
if (fp == NULL) {
close(fd);
perror("malloc failed");
return NULL;
}
fp->fd = fd;
fp->pos = 0;
fp->flush_mode = LINE_BUFFER; // 默认行缓冲
memset(fp->outbuffer, 0, BUFFER_SIZE);
return fp;
}
// 核心刷新函数:将缓冲区数据写入磁盘
static void myfflushcore(myFILE* fp, int flag) {
if (fp == NULL || fp->pos == 0) return;
// 写入磁盘
ssize_t ret = write(fp->fd, fp->outbuffer, fp->pos);
if (ret < 0) {
perror("write failed");
} else {
// 清空缓冲区
fp->pos = 0;
memset(fp->outbuffer, 0, BUFFER_SIZE);
}
}
// 自定义fputs:写入数据到缓冲区
int myfputs(const char* str, myFILE* fp) {
if (str == NULL || fp == NULL) return -1;
int len = strlen(str);
if (len == 0) return 0;
// 缓冲区不足时先刷新
if (fp->pos + len > BUFFER_SIZE) {
myfflushcore(fp, MUST_FLUSH);
}
// 拷贝数据到缓冲区
memcpy(fp->outbuffer + fp->pos, str, len);
fp->pos += len;
// 行缓冲:遇\n刷新
if (fp->flush_mode == LINE_BUFFER && strchr(str, '\n') != NULL) {
myfflushcore(fp, TRY_FLUSH);
}
return len;
}
// 自定义fflush:主动刷新缓冲区
void myfflush(myFILE* fp) {
if (fp == NULL) return;
myfflushcore(fp, MUST_FLUSH);
}
// 自定义fclose:关闭文件+刷新缓冲区
void myfclose(myFILE* fp) {
if (fp == NULL) return;
myfflush(fp); // 关闭前强制刷新
close(fp->fd); // 关闭系统文件描述符
free(fp); // 释放内存
fp = NULL;
}
函数调用关系
3. 测试文件:test.c
#include "mystdio.h"
#include <unistd.h>
int main() {
// 打开文件(创建log1.txt)
myFILE* fp = myfopen("log1.txt", "w");
if (!fp) {
perror("myfopen failed");
return -1;
}
// 循环写入数据
const char* str = "hello mystdio\n";
for (int i = 0; i < 5; i++) {
myfputs(str, fp);
sleep(1); // 睡眠1秒,观察缓冲区状态
printf("debug: outbuffer=%s, pos=%d\n", fp->outbuffer, fp->pos);
}
// 主动刷新+关闭文件
myfflush(fp);
myfclose(fp);
return 0;
}
程序执行时序
4. 编译脚本:Makefile
# 编译规则
test: test.c mystdio.c
gcc -o $@ $^ -Wall
# 伪目标声明
.PHONY: clean run all
# 构建并运行
all: run
# 运行程序
run: test
./test
# 清理产物
clean:
rm -f test log1.txt
三、编译运行
1. 编译
make
2. 运行
./test
3. 查看结果
# 查看终端输出(pos=0是因为行缓冲遇\n即时刷新)
debug: outbuffer=, pos=0
debug: outbuffer=, pos=0
...
# 查看写入的文件
cat log1.txt
# 输出:5行hello mystdio
四、关键知识点
- 缓冲区作用:减少磁盘IO次数,提升程序效率;
- 刷新触发条件:缓冲区满、遇
\n(行缓冲)、主动调用fflush、关闭文件; - 内存管理:自定义
myFILE结构体需手动malloc/free,避免内存泄漏; - 系统调用:底层依赖
open/write/close等系统调用操作文件。
五、扩展优化
- 增加读缓冲区逻辑,模拟
fgets; - 支持更多缓冲模式(如无缓冲);
- 增加错误处理(如缓冲区溢出、文件权限检查);
- 兼容更多打开模式(如
r+、w+)。
通过这个简易实现,能直观理解C标准库IO函数的底层逻辑——所有便捷的文件操作,本质都是对"缓冲区+系统调用"的封装。
更多推荐




所有评论(0)