【基础IO】————简单设计一下libc库

目录

[1. mystdio.h](#1. mystdio.h)

[2. mystdio.c](#2. mystdio.c)

[2.1 打开文件](#2.1 打开文件)

[2.2 写文件](#2.2 写文件)

[2.3 刷新缓冲区](#2.3 刷新缓冲区)

[2.4 关闭文件](#2.4 关闭文件)


1. mystdio.h

cpp 复制代码
#pragma once

#include <stdio.h>

#define MAX 1024

// 刷新方式
#define NONE_FLUSH (1<<0)
#define LINE_FLUSH (1<<1)
#define FULL_FLUSH (1<<2)

typedef struct IO_FILE
{
	int fileno;// 文件描述符fd
	int flag;// 文件打开方式
	char outbuffer[MAX];// 缓冲区
	int bufferlen;
	int flush_method;// 刷新方式

}MyFile;

MyFile* MyFopen(const char* path,const  char* mode);
void MyFclose(MyFile* file);
void MyFwrite(MyFile* file,void* str,int len);
void MyFFlush(MyFile* file);

2. mystdio.c

cpp 复制代码
#include "mystdio.h"
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

MyFile* BuyFile(int flag,int fd)
{
	MyFile* f = (MyFile*)malloc(sizeof(MyFile));
	if(f == NULL)
	{
		perror("malloc");
		return NULL;
	}
	f->fileno = fd;
	f->flag = flag;
	f->bufferlen = 0;
	f->flush_method = LINE_FLUSH;
	memset(f->outbuffer,0,sizeof(f->outbuffer));

	return f;
}


MyFile* MyFopen(const char* path,const  char* mode)
{
	int fd = -1;
	int flag = 0;
	if(strcmp(mode,"w") == 0)
	{
		// 写入打开
		flag = O_CREAT | O_WRONLY | O_TRUNC;
		fd = open(path,flag,0666);
	}
	else if(strcmp(mode,"a") == 0)
	{
		// 追加打开
		flag = O_CREAT | O_WRONLY | O_APPEND;
		fd = open(path,flag,0666);
	}
	else if(strcmp(mode,"r") == 0)
	{
		// 只读打开
		flag = O_RDONLY;
		fd = open(path,flag);
	}
	else
	{

	}
	if(fd < 0)
	{
		perror("open");
		return NULL;
	}
	return BuyFile(flag,fd);
}

void MyFclose(MyFile* file)
{
	if(file->fileno < 0)
		return;

	MyFFlush(file);
	close(file->fileno);
	free(file);
}

void MyFwrite(MyFile* file,void* str,int len)
{
	// 从文件的指定偏移量位置开始覆盖
	memcpy(file->outbuffer+file->bufferlen,str,len);
	file->bufferlen += len;
	// 判断是否满足刷新条件
	if((file->flush_method & LINE_FLUSH) && file->outbuffer[file->bufferlen-1] == '\n')
	{
		MyFFlush(file);
	}
}

void MyFFlush(MyFile* file)
{
	if(file->bufferlen <= 0) 
		return;
	int n = write(file->fileno,file->outbuffer,file->bufferlen);
	(void)n;

	// 将缓存数据同步到外设
	fsync(file->fileno);
	file->bufferlen = 0;
}

2.1 打开文件

  • 根据传入的文件路径和打开方式,调用Linux系统函数open打开文件,并获取文件描述符fd
  • 先匹配文件的打开模式,只读r,只写w,追加a
  • 为不同模式设置对应的系统打开标志:只读r:O_RDONLY,只写:O_CREATE | O_WRONLY | O_TRUNC,追加: O_CREATE | O_WRONLY | O_APPEND
  • 调用open打开文件,校验文件描述符是否有效
  • 打开成功后,调用BuyFile创建并初始化自定义文件对象
cpp 复制代码
MyFile* BuyFile(int flag,int fd)
{
	MyFile* f = (MyFile*)malloc(sizeof(MyFile));
	if(f == NULL)
	{
		perror("malloc");
		return NULL;
	}
	f->fileno = fd;
	f->flag = flag;
	f->bufferlen = 0;
	f->flush_method = LINE_FLUSH;
	memset(f->outbuffer,0,sizeof(f->outbuffer));

	return f;
}


MyFile* MyFopen(const char* path,const  char* mode)
{
	int fd = -1;
	int flag = 0;
	if(strcmp(mode,"w") == 0)
	{
		// 写入打开
		flag = O_CREAT | O_WRONLY | O_TRUNC;
		fd = open(path,flag,0666);
	}
	else if(strcmp(mode,"a") == 0)
	{
		// 追加打开
		flag = O_CREAT | O_WRONLY | O_APPEND;
		fd = open(path,flag,0666);
	}
	else if(strcmp(mode,"r") == 0)
	{
		// 只读打开
		flag = O_RDONLY;
		fd = open(path,flag);
	}
	else
	{
        // ...
	}
	if(fd < 0)
	{
		perror("open");
		return NULL;
	}
	return BuyFile(flag,fd);
}

2.2 写文件

  • 不直接调用系统函数write写入文件,而是先把数据拷贝到自定义结构体MyFile的outbuffer数据中(用户缓冲区)
  • 拷贝的时候也是从缓冲区当前有效数据的末尾(bufferlen偏移处)开始拷贝,避免覆盖之前拷贝进缓冲区的已有数据,并更新缓冲区有效数据长度bufferlen
  • 判断刷新条件,检查刷新策略是否为行刷新(LINE_FLUSH),检查最后写入的字符是不是换行符\n,两个条件同时满足时,就调用MyFFlush把用户缓冲区数据刷到磁盘文件,并清空缓冲区
  • 不直接将数据写入磁盘的目的是:
  1. 减少系统调用writte的次数(系统调用开销大)
  2. 用户态缓冲区批量写数据,提升文件写入效率
cpp 复制代码
void MyFwrite(MyFile* file,void* str,int len)
{
	// 从文件的指定偏移量位置开始覆盖
	memcpy(file->outbuffer+file->bufferlen,str,len);
	file->bufferlen += len;
	// 判断是否满足刷新条件
	if((file->flush_method & LINE_FLUSH) && file->outbuffer[file->bufferlen-1] == '\n')
	{
		MyFFlush(file);
	}
}

2.3 刷新缓冲区

  • 先判断缓冲区有效长度,没数据直接返回,避免无效操作
  • 调用系统write将用户缓冲区数据写入内核缓冲区,把MyFile->outbuffer里的有效数据,写入文件描述符fileno对应的文件,这一步只是把数据从用户缓冲区拷贝到内核缓冲区,还没真正落盘
  • 调用fsync强制内核把缓冲区数据刷到磁盘硬件,保证数据真正持久化,不会丢失
  • 清空缓冲区,bufferlen = 0,标记缓冲区已空,下次写入可以从头开始覆盖使用。
cpp 复制代码
void MyFFlush(MyFile* file)
{
	if(file->bufferlen <= 0) 
		return;
	int n = write(file->fileno,file->outbuffer,file->bufferlen);
	(void)n;

	// 将缓存数据同步到外设
	fsync(file->fileno);
	file->bufferlen = 0;
}

2.4 关闭文件

  • 先判断文件描述符是否有效,无效直接返回,避免非法操作
  • 调用MyFFlush,把用户缓冲区里还没写入磁盘的剩余数据强制落盘,防止关闭文件时丢失缓冲区中还未刷新的内容
  • 调用系统close函数,释放内核中对应的文件描述符资源,断开与文件的连接
  • 调用free释放MyFile结构体本身占用的堆内存,避免内存泄漏
cpp 复制代码
void MyFclose(MyFile* file)
{
	if(file->fileno < 0)
		return;

	MyFFlush(file);
	close(file->fileno);
	free(file);
}
相关推荐
IT_陈寒15 分钟前
React状态更新总是不及时?你可能漏了这步批处理机制
前端·人工智能·后端
恋猫de小郭24 分钟前
AI Agent 开发究竟是啥?如何用 AI 开发 Agent ?深入浅出给你一套概念
android·前端·ai编程
前端双越老师26 分钟前
我开发 AI Agent 项目踩过的 5个坑
前端·agent·全栈
晓得迷路了1 小时前
栗子前端技术周刊第 134 期 - React Router v8、TypeScript 7 RC、React Native 0.86...
前端·javascript·react.js
Carson带你学Android1 小时前
Android 17 正式发布:AI 终于成了系统能力
android·前端·ai编程
Mike_jia1 小时前
ZbxTable:Zabbix开源报表神器,从运维数据到决策洞察的最后一公里
前端
LinXunFeng10 小时前
Obsidian - 使用 Share Note 分享笔记并自部署
前端·笔记·github
乘风gg14 小时前
为什么AI 时代来临,大部分人吃不到红利
前端·ai编程·claude
恋猫de小郭14 小时前
Android 限制侧载新进展,谷歌联合国内厂商推验证计划
android·前端·flutter
IT_陈寒14 小时前
Redis内存爆了,原来我漏掉了这个致命配置
前端·人工智能·后端