Linux从0到1——模拟实现一部分C库接口
0. 前言
我们模拟实现的函数肯定和C库中的函数有很大差别,这里只是从理解的角度去带大家模拟实现一下。实际C库中的函数会更加复杂。
1. mystdio.h头文件
#pragma once
#define SIZE 4096 // 缓冲区大小
// 刷新策略
#define FLUSH_NONE 1
#define FLUSH_LINE (1<<1) // 行刷新
#define FLUSE_ALL (1<<2) // 全刷新
typedef struct _myFILE
{
int fileno; // fd
int flag; // 刷新策略
char buffer[SIZE]; // 缓冲区
int end; // 使用start和end管理缓冲区,start默认是0
}myFILE;
extern myFILE *my_fopen(const char *path, const char *mode);
extern int my_fwrite(const char *s, int num, myFILE *stream);
extern int my_fflush(myFILE *stream);
extern int my_fclose(myFILE *stream);
目标是模拟实现fopen/fwrite/fflush/fclose
这四个接口。
2. mystdio.c文件
#include"mystdio.h"
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#define DFL_MODE 0666
myFILE *my_fopen(const char *path, const char *mode)
{
int fd = 0;
int flag = 0;
if (strcmp(mode, "r") == 0)
{
flag |= O_RDONLY;
}
else if (strcmp(mode, "w") == 0)
{
flag |= (O_CREAT | O_WRONLY | O_TRUNC);
}
else if (strcmp(mode, "a") == 0)
{
flag |= (O_CREAT | O_WRONLY | O_APPEND);
}
else
{
// do nothing
}
if (flag & O_CREAT) // 如果选项中有O_CREAT
{
fd = open(path, flag, DFL_MODE);
}
else
{
fd = open(path, flag);
}
if (fd < 0)
{
errno = 2; // 2错误码,对应的错误信息是文件找不到
return NULL;
}
myFILE *fp = (myFILE*)malloc(sizeof(myFILE));
if (!fp)
{
errno = 3;
return NULL;
}
fp->flag = FLUSH_LINE; // 默认是行刷新
fp->end = 0;
fp->fileno = fd;
return fp;
}
int my_fwrite(const char *s, int num, myFILE *stream)
{
// 写入
memcpy(stream->buffer + stream->end, s, num);
stream->end += num;
// 判断是否需要刷新
if ((stream->flag & FLUSH_LINE) && stream->end > 0 && stream->buffer[stream->end - 1] == '\n')
{
// 必须满足 1. 刷新策略是行缓冲 2. 缓冲区不为空 3. 缓冲区最后一个字符是`\n`才进行缓冲区的行刷新。
my_fflush(stream);
}
return num;
}
int my_fflush(myFILE *stream)
{
if (stream->end > 0) // 缓冲区不为空
{
write(stream->fileno, stream->buffer, stream->end); // 将数据刷新到操作系统
// fsync(stream->fileno); // 内核级别的强制刷新,将数据刷新到存储设备
stream->end = 0;
}
// 缓冲区为空,什么都不做
return 0;
}
int my_fclose(myFILE *stream)
{
my_fflush(stream);
return close(stream->fileno);
}
3. 测试文件main.c
#include"mystdio.h"
#include<stdio.h>
#include<string.h>
#include<unistd.h>
int main()
{
myFILE *fp = my_fopen("./log.txt", "w");
if (!fp)
{
perror("my_fopen");
return 1;
}
const char *s = "hello world\n";
int cnt = 5;
while(cnt--)
{
my_fwrite(s, strlen(s), fp);
sleep(1);
}
my_fclose(fp);
return 0;
}