目录
一、缓冲区概述
二、语言层面缓冲区
三、C语言模拟实现stdio库
一、缓冲区概述
Linux缓冲区是指在内存中开辟的一块区域,用于存储输入输出数据的临时存储区域。
当应用程序向文件或设备进行读写操作时,数据会先被存储到缓冲区中,然后再由缓冲区将数据写入磁盘或设备中。
缓冲区的作用是提高数据读写的效率,减少磁盘或设备的访问次数,从而提高系统的性能。
在Linux系统中,缓冲区的大小可以通过修改内核参数来进行调整。
缓冲区的作用:
-
解放使用缓冲区的进程的时间(将数据放到缓冲区后,进程继续执行自己的代码)
-
缓冲区的存在可以集中处理数据刷新,减少I/O的次数,从而达到提高整机的效率!
二、语言层面缓冲区
缓冲区:节省数据进行IO的时间
缓冲区会结合具体的设备,定制自己的刷新策略
- 立即刷新 ---> 无缓冲
- 行刷新 ---> 行缓冲 ---> 显示器
- 缓冲区满 ---> 全缓冲 ---> 磁盘
缓冲区是用户语言层面给我们提供的,在FILE结构体里面
fflush()、close()可强制刷新缓冲区
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main()
{
//C语言接口
printf("hello printf\n");
fprintf(stdout, "hello fprintf\n");
fputs("hello fputs\n", stdout);
//系统接口
const char* agm = "hello write\n";
write(1, agm, strlen(agm));
fork();
return 0;
}
若未使用 > ,打印了四条字符串
- stdout默认使用的是行刷新,在进程fork之前,三条C语言函数已经将数据打印输出到显示器(外设)上
- 此时在FILE内部/进程内部不存在对应的数据了
若使用了 > ,打印了七条字符串
- 写入文件不再是显示器而是普通文件,采用的刷新策略是全缓冲,之前三条C语言显示函数,虽然带了'\n',但是stdout缓冲区并没有刷新
- 执行fork的时候,stdout属于父进程,创建子进程后进程退出,无论谁退出,退出时都会显示缓冲区刷新
- 写时拷贝,数据最终会显示两份
- write没有FILE,而是使用fd,没有C语言提供的缓冲区
三、C语言模拟实现stdio库
mystdio.h
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <assert.h>
#define CAPACITY 1024
#define SYNC_NOW 1
#define SYNC_LINE 2
#define SYNC_FULL 3
typedef struct _FILE
{
int flags;
int fileno;
int cap; //buffer的总容量
int size; //buffer的使用量
char buffer[CAPACITY]; //文件缓冲区
}_FILE;
_FILE* _fopen(const char* path, const char* mode);
void _fwrite(const void* ptr, int num, _FILE* pf);
void _fflush(_FILE* pf);
void _fclose(_FILE* pf);
mystdio.c
#include "mystdio.h"
_FILE* _fopen(const char* path, const char* mode)
{
//标志位传参
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
exit(1);
umask(0);
int defaultMode = 0666;
int fd = 0;
if (flags & O_RDONLY)
fd = open(path, flags);
else
fd = open(path, flags, defaultMode);
_FILE* pf = (_FILE*)malloc(sizeof(_FILE));
assert(pf);
pf->flags = SYNC_LINE;
pf->fileno = fd;
pf->cap = CAPACITY;
pf->size = 0;
memset(pf->buffer, 0, CAPACITY);
return pf;
}
void _fwrite(const void* ptr, int num, _FILE* pf)
{
memcpy(pf->buffer + pf->size, ptr, num);
pf->size += num;
if (pf->flags & SYNC_NOW)
{
write(pf->fileno, pf->buffer, pf->size);
pf->size = 0;
}
else if (pf->flags & SYNC_FULL)
{
if (pf->size == pf->cap)
{
write(pf->fileno, pf->buffer, pf->size);
pf->size = 0;
}
}
else
{
if (pf->buffer[pf->size - 1] == '\n')
{
write(pf->fileno, pf->buffer, pf->size);
pf->size = 0;
}
}
}
void _fflush(_FILE* pf)
{
if (pf->size > 0)
write(pf->fileno, pf->buffer, pf->size);
fsync(pf->fileno); //强制要求操作系统对外设进行数据刷新
pf->size = 0;
}
void _fclose(_FILE* pf)
{
_fflush(pf);
close(pf->fileno);
}
main.c
#include "mystdio.h"
#include <stdio.h>
int main()
{
//_FILE* pf = _fopen("test.txt", "w");
_FILE* pf = _fopen("test.txt", "r");
assert(pf);
//写/追加
// const char* msg = "hello world\n";
// int cnt = 5;
// while (cnt--)
// {
// printf("%d\n", cnt);
// _fwrite(msg, strlen(msg), pf);
// if (cnt == 5)
// _fflush(pf);
// sleep(1);
// }
//读
char* buffer[1024];
read(pf->fileno, buffer, 1024);
printf("%s\n", buffer);
return 0;
}