前言
我們接下來要談?wù)摰氖俏覀冋Z言層面的緩沖區(qū)(C,C++之類的),不是我們操作系統(tǒng)內(nèi)核里面自帶的緩沖區(qū),我們每次在打開一個文件的時候,以C語言為例子,C語言會為我們所打開的這個文件分配一塊緩沖區(qū),用來緩存我們讀寫的數(shù)據(jù)`,這個緩沖區(qū)會被放在我們創(chuàng)建的FILE的結(jié)構(gòu)體里面,里面存放著緩沖區(qū)的字段和維護信息
一、緩沖區(qū)刷新方法分類
a.無緩沖–直接刷新
b.行緩沖–不刷新,直到碰到\n才刷新
顯示器寫入一般采用的是行緩沖
c.全緩沖–緩沖區(qū)滿了才刷新
文件寫入一般采用的是全緩沖,緩沖區(qū)滿了或者程序結(jié)束的時候刷新
二、 緩沖區(qū)的常見刷新問題
1.問題
我們將可執(zhí)行文件內(nèi)容重定向到log1里面
最后我們發(fā)現(xiàn)與C有關(guān)的接口被打印了兩次,這是什么原因呢?
之前我們說過,我們朝文件里面寫入是全緩沖,也就是等緩沖區(qū)滿了或者程序結(jié)束的時候去刷新,打印兩次的都是屬于C語言的接口, 其會建立一個語言層面的緩沖區(qū), 我們在fork之前,printf,fprintf,fwrite寫入的數(shù)據(jù)都存放在語言層面的緩沖區(qū),fork之后創(chuàng)建子進程,子進程對父進程的數(shù)據(jù)內(nèi)容進行拷貝,因為此時緩沖區(qū)為刷新,子進程會連同父進程語言層面緩沖區(qū)內(nèi)容一起拷貝
所以之后,父子進程語言層面的緩沖區(qū)中都存放著相同的數(shù)據(jù),在程序結(jié)束的時候會對語言層面的緩沖區(qū)進行刷新,將其刷新到系統(tǒng)里面的緩沖區(qū),
若子進程先刷新,因為對父進程數(shù)據(jù)進行更改了(即清空語言緩沖區(qū)),這個時候會發(fā)生寫實拷貝,之后子進程緩沖區(qū)的數(shù)據(jù)就被刷新到系統(tǒng)緩沖區(qū)了。
父進程同理,也會進行一遍緩沖區(qū)的刷新,父子進程都對數(shù)據(jù)進行了刷新寫入系統(tǒng)緩沖區(qū),所以文件里面就會寫入兩次。
wirite屬于系統(tǒng)接口,調(diào)用以后會直接寫入到內(nèi)核緩沖區(qū)里面,之后寫入硬盤文件中,沒有語言層面緩沖區(qū)概念,所以只寫入文件一次
2.刷新本質(zhì)
用戶刷新的本質(zhì)是通關(guān)重定向到文件描述符為1的文件(stdout)+write寫入內(nèi)核緩沖區(qū),F(xiàn)ILE對象屬于用戶不是操作系統(tǒng),F(xiàn)ILE里面的緩沖區(qū)屬于語言層面的緩沖區(qū)(用戶級緩沖區(qū)),目前我們認為,只要數(shù)據(jù)刷新到了內(nèi)核中,數(shù)據(jù)就可以寫入硬件了文章來源:http://www.zghlxwxcb.cn/news/detail-752439.html
這些C接口最后寫入內(nèi)核緩沖區(qū),本質(zhì)都是調(diào)用write的系統(tǒng)接口文章來源地址http://www.zghlxwxcb.cn/news/detail-752439.html
三、模擬實現(xiàn)
1.Mystdio.h
#include <string.h>
#define SIZE 1024
#define FLUSH_NOW 1//無緩沖
#define FLUSH_LINE 2//行緩沖
#define FLUSH_ALL 4//全緩沖
typedef struct IO_FILE{
int fileno;//文件描述符
int flag; //刷新方式
char outbuffer[SIZE]; // 簡單模擬語言層緩沖區(qū)
int out_pos;//緩沖區(qū)當前大小
}_FILE;
_FILE * _fopen(const char*filename, const char *flag);
int _fwrite(_FILE *fp, const char *s, int len);
void _fclose(_FILE *fp);
2.Mystdio.c
#include "Mystdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#define FILE_MODE 0666//文件默認權(quán)限
_FILE * _fopen(const char*filename, const char *flag)
{
assert(filename);
assert(flag);
int f = 0;//文件的寫入方式
int fd = -1;//文件描述符
if(strcmp(flag, "w") == 0) {
f = (O_CREAT|O_WRONLY|O_TRUNC);
fd = open(filename, f, FILE_MODE);
//獲取文件描述符
}
else if(strcmp(flag, "a") == 0) {
f = (O_CREAT|O_WRONLY|O_APPEND);
fd = open(filename, f, FILE_MODE);
}
else if(strcmp(flag, "r") == 0) {
f = O_RDONLY;
fd = open(filename, f);
}
else
return NULL;
if(fd == -1) return NULL;
_FILE *fp = (_FILE*)malloc(sizeof(_FILE));
//創(chuàng)建文件指針結(jié)構(gòu)體
if(fp == NULL) return NULL;
fp->fileno = fd;
//fp->flag = FLUSH_LINE;
fp->flag = FLUSH_ALL;
fp->out_pos = 0;
return fp;
}
int _fwrite(_FILE *fp, const char *s, int len)
{
// "abcd\n"
memcpy(&fp->outbuffer[fp->out_pos], s, len); // 沒有做異常處理, 也不考慮局部問題
fp->out_pos += len;
if(fp->flag&FLUSH_NOW)//無緩沖
{
write(fp->fileno, fp->outbuffer, fp->out_pos);
fp->out_pos = 0;
}
else if(fp->flag&FLUSH_LINE)//行緩沖
{
if(fp->outbuffer[fp->out_pos-1] == '\n'){ // 不考慮其他情況
write(fp->fileno, fp->outbuffer, fp->out_pos);
fp->out_pos = 0;
}
}
else if(fp->flag & FLUSH_ALL)//全緩沖
{
if(fp->out_pos == SIZE){
write(fp->fileno, fp->outbuffer, fp->out_pos);
fp->out_pos = 0;
}
}
return len;
}
void _fflush(_FILE *fp)//手動刷新緩沖區(qū)
{
if(fp->out_pos > 0){
write(fp->fileno, fp->outbuffer, fp->out_pos);
fp->out_pos = 0;
}
}
void _fclose(_FILE *fp)
{
if(fp == NULL) return;
_fflush(fp);
close(fp->fileno);
free(fp);
}
3.main.c
#include "Mystdio.h"
#include <unistd.h>
#define myfile "test.txt"
int main()
{
_FILE *fp = _fopen(myfile, "a");
if(fp == NULL) return 1;
const char *msg = "hello world\n";
int cnt = 10;
while(cnt){
_fwrite(fp, msg, strlen(msg));
// fflush(fp);
sleep(1);
cnt--;
}
_fclose(fp);
return 0;
}
到了這里,關(guān)于【Linux】語言層面緩沖區(qū)的刷新問題以及簡易模擬實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!