【Linux后端服务器开发】缓冲区

目录

一、缓冲区概述

二、语言层面缓冲区

三、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;
}

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AllinTome

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值